mirror of
https://github.com/SuperClaude-Org/SuperClaude_Framework.git
synced 2025-12-29 16:16:08 +00:00
688 lines
22 KiB
Markdown
688 lines
22 KiB
Markdown
|
|
# logger.py - Structured Logging Utilities for SuperClaude Hooks
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The `logger.py` module provides structured logging of hook events for later analysis, focusing on capturing hook lifecycle, decisions, and errors in a structured JSON format. It implements simple, efficient logging without complex features, prioritizing performance and structured data collection for operational analysis and debugging.
|
||
|
|
|
||
|
|
## Purpose and Responsibilities
|
||
|
|
|
||
|
|
### Primary Functions
|
||
|
|
- **Hook Lifecycle Logging**: Structured capture of hook start/end events with timing
|
||
|
|
- **Decision Logging**: Record decision-making processes and rationale within hooks
|
||
|
|
- **Error Logging**: Comprehensive error capture with context and recovery information
|
||
|
|
- **Performance Monitoring**: Timing and performance metrics collection for optimization
|
||
|
|
- **Session Tracking**: Correlation of events across hook executions with session IDs
|
||
|
|
|
||
|
|
### Design Philosophy
|
||
|
|
- **Structured Data**: JSON-formatted logs for machine readability and analysis
|
||
|
|
- **Performance First**: Minimal overhead with efficient logging operations
|
||
|
|
- **Operational Focus**: Data collection for debugging and operational insights
|
||
|
|
- **Simple Interface**: Easy integration with hooks without complex configuration
|
||
|
|
|
||
|
|
## Core Architecture
|
||
|
|
|
||
|
|
### HookLogger Class
|
||
|
|
```python
|
||
|
|
class HookLogger:
|
||
|
|
"""Simple logger for SuperClaude-Lite hooks."""
|
||
|
|
|
||
|
|
def __init__(self, log_dir: str = None, retention_days: int = None):
|
||
|
|
"""
|
||
|
|
Initialize the logger.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
log_dir: Directory to store log files. Defaults to cache/logs/
|
||
|
|
retention_days: Number of days to keep log files. Defaults to 30.
|
||
|
|
"""
|
||
|
|
```
|
||
|
|
|
||
|
|
### Initialization and Configuration
|
||
|
|
```python
|
||
|
|
def __init__(self, log_dir: str = None, retention_days: int = None):
|
||
|
|
# Load configuration
|
||
|
|
self.config = self._load_config()
|
||
|
|
|
||
|
|
# Check if logging is enabled
|
||
|
|
if not self.config.get('logging', {}).get('enabled', True):
|
||
|
|
self.enabled = False
|
||
|
|
return
|
||
|
|
|
||
|
|
self.enabled = True
|
||
|
|
|
||
|
|
# Set up log directory
|
||
|
|
if log_dir is None:
|
||
|
|
root_dir = Path(__file__).parent.parent.parent
|
||
|
|
log_dir_config = self.config.get('logging', {}).get('file_settings', {}).get('log_directory', 'cache/logs')
|
||
|
|
log_dir = root_dir / log_dir_config
|
||
|
|
|
||
|
|
self.log_dir = Path(log_dir)
|
||
|
|
self.log_dir.mkdir(parents=True, exist_ok=True)
|
||
|
|
|
||
|
|
# Session ID for correlating events
|
||
|
|
self.session_id = str(uuid.uuid4())[:8]
|
||
|
|
```
|
||
|
|
|
||
|
|
**Initialization Features**:
|
||
|
|
- **Configurable Enablement**: Logging can be disabled via configuration
|
||
|
|
- **Flexible Directory**: Log directory configurable via parameter or configuration
|
||
|
|
- **Session Correlation**: Unique session ID for event correlation
|
||
|
|
- **Automatic Cleanup**: Old log file cleanup on initialization
|
||
|
|
|
||
|
|
## Configuration Management
|
||
|
|
|
||
|
|
### Configuration Loading
|
||
|
|
```python
|
||
|
|
def _load_config(self) -> Dict[str, Any]:
|
||
|
|
"""Load logging configuration from YAML file."""
|
||
|
|
if UnifiedConfigLoader is None:
|
||
|
|
# Return default configuration if loader not available
|
||
|
|
return {
|
||
|
|
'logging': {
|
||
|
|
'enabled': True,
|
||
|
|
'level': 'INFO',
|
||
|
|
'file_settings': {
|
||
|
|
'log_directory': 'cache/logs',
|
||
|
|
'retention_days': 30
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Get project root
|
||
|
|
root_dir = Path(__file__).parent.parent.parent
|
||
|
|
loader = UnifiedConfigLoader(root_dir)
|
||
|
|
|
||
|
|
# Load logging configuration
|
||
|
|
config = loader.load_yaml('logging')
|
||
|
|
return config or {}
|
||
|
|
except Exception:
|
||
|
|
# Return default configuration on error
|
||
|
|
return {
|
||
|
|
'logging': {
|
||
|
|
'enabled': True,
|
||
|
|
'level': 'INFO',
|
||
|
|
'file_settings': {
|
||
|
|
'log_directory': 'cache/logs',
|
||
|
|
'retention_days': 30
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Configuration Fallback Strategy**:
|
||
|
|
1. **Primary**: Load from logging.yaml via UnifiedConfigLoader
|
||
|
|
2. **Fallback**: Use hardcoded default configuration if loader unavailable
|
||
|
|
3. **Error Recovery**: Default configuration on any loading error
|
||
|
|
4. **Graceful Degradation**: Continue operation even with configuration issues
|
||
|
|
|
||
|
|
### Configuration Structure
|
||
|
|
```python
|
||
|
|
default_config = {
|
||
|
|
'logging': {
|
||
|
|
'enabled': True, # Enable/disable logging
|
||
|
|
'level': 'INFO', # Log level (DEBUG, INFO, WARNING, ERROR)
|
||
|
|
'file_settings': {
|
||
|
|
'log_directory': 'cache/logs', # Log file directory
|
||
|
|
'retention_days': 30 # Days to keep log files
|
||
|
|
}
|
||
|
|
},
|
||
|
|
'hook_configuration': {
|
||
|
|
'hook_name': {
|
||
|
|
'enabled': True # Per-hook logging control
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Python Logger Integration
|
||
|
|
|
||
|
|
### Logger Setup
|
||
|
|
```python
|
||
|
|
def _setup_logger(self):
|
||
|
|
"""Set up the Python logger with JSON formatting."""
|
||
|
|
self.logger = logging.getLogger("superclaude_lite_hooks")
|
||
|
|
|
||
|
|
# Set log level from configuration
|
||
|
|
log_level_str = self.config.get('logging', {}).get('level', 'INFO').upper()
|
||
|
|
log_level = getattr(logging, log_level_str, logging.INFO)
|
||
|
|
self.logger.setLevel(log_level)
|
||
|
|
|
||
|
|
# Remove existing handlers to avoid duplicates
|
||
|
|
self.logger.handlers.clear()
|
||
|
|
|
||
|
|
# Create daily log file
|
||
|
|
today = datetime.now().strftime("%Y-%m-%d")
|
||
|
|
log_file = self.log_dir / f"superclaude-lite-{today}.log"
|
||
|
|
|
||
|
|
# File handler
|
||
|
|
handler = logging.FileHandler(log_file, mode='a', encoding='utf-8')
|
||
|
|
handler.setLevel(logging.INFO)
|
||
|
|
|
||
|
|
# Simple formatter - just output the message (which is already JSON)
|
||
|
|
formatter = logging.Formatter('%(message)s')
|
||
|
|
handler.setFormatter(formatter)
|
||
|
|
|
||
|
|
self.logger.addHandler(handler)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Logger Features**:
|
||
|
|
- **Daily Log Files**: Separate log file per day for easy management
|
||
|
|
- **JSON Message Format**: Messages are pre-formatted JSON for structure
|
||
|
|
- **UTF-8 Encoding**: Support for international characters
|
||
|
|
- **Configurable Log Level**: Log level set from configuration
|
||
|
|
- **Handler Management**: Automatic cleanup of duplicate handlers
|
||
|
|
|
||
|
|
## Structured Event Logging
|
||
|
|
|
||
|
|
### Event Structure
|
||
|
|
```python
|
||
|
|
def _create_event(self, event_type: str, hook_name: str, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
||
|
|
"""Create a structured event."""
|
||
|
|
event = {
|
||
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||
|
|
"session": self.session_id,
|
||
|
|
"hook": hook_name,
|
||
|
|
"event": event_type
|
||
|
|
}
|
||
|
|
|
||
|
|
if data:
|
||
|
|
event["data"] = data
|
||
|
|
|
||
|
|
return event
|
||
|
|
```
|
||
|
|
|
||
|
|
**Event Structure Components**:
|
||
|
|
- **timestamp**: ISO 8601 UTC timestamp for precise timing
|
||
|
|
- **session**: 8-character session ID for event correlation
|
||
|
|
- **hook**: Hook name for operation identification
|
||
|
|
- **event**: Event type (start, end, decision, error)
|
||
|
|
- **data**: Optional additional data specific to event type
|
||
|
|
|
||
|
|
### Event Filtering
|
||
|
|
```python
|
||
|
|
def _should_log_event(self, hook_name: str, event_type: str) -> bool:
|
||
|
|
"""Check if this event should be logged based on configuration."""
|
||
|
|
if not self.enabled:
|
||
|
|
return False
|
||
|
|
|
||
|
|
# Check hook-specific configuration
|
||
|
|
hook_config = self.config.get('hook_configuration', {}).get(hook_name, {})
|
||
|
|
if not hook_config.get('enabled', True):
|
||
|
|
return False
|
||
|
|
|
||
|
|
# Check event type configuration
|
||
|
|
hook_logging = self.config.get('logging', {}).get('hook_logging', {})
|
||
|
|
event_mapping = {
|
||
|
|
'start': 'log_lifecycle',
|
||
|
|
'end': 'log_lifecycle',
|
||
|
|
'decision': 'log_decisions',
|
||
|
|
'error': 'log_errors'
|
||
|
|
}
|
||
|
|
|
||
|
|
config_key = event_mapping.get(event_type, 'log_lifecycle')
|
||
|
|
return hook_logging.get(config_key, True)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Filtering Logic**:
|
||
|
|
1. **Global Enable Check**: Respect global logging enabled/disabled setting
|
||
|
|
2. **Hook-Specific Check**: Allow per-hook logging control
|
||
|
|
3. **Event Type Check**: Filter by event type (lifecycle, decisions, errors)
|
||
|
|
4. **Default Behavior**: Log all events if configuration not specified
|
||
|
|
|
||
|
|
## Core Logging Methods
|
||
|
|
|
||
|
|
### Hook Lifecycle Logging
|
||
|
|
```python
|
||
|
|
def log_hook_start(self, hook_name: str, context: Optional[Dict[str, Any]] = None):
|
||
|
|
"""Log the start of a hook execution."""
|
||
|
|
if not self._should_log_event(hook_name, 'start'):
|
||
|
|
return
|
||
|
|
|
||
|
|
event = self._create_event("start", hook_name, context)
|
||
|
|
self.logger.info(json.dumps(event))
|
||
|
|
|
||
|
|
def log_hook_end(self, hook_name: str, duration_ms: int, success: bool, result: Optional[Dict[str, Any]] = None):
|
||
|
|
"""Log the end of a hook execution."""
|
||
|
|
if not self._should_log_event(hook_name, 'end'):
|
||
|
|
return
|
||
|
|
|
||
|
|
data = {
|
||
|
|
"duration_ms": duration_ms,
|
||
|
|
"success": success
|
||
|
|
}
|
||
|
|
if result:
|
||
|
|
data["result"] = result
|
||
|
|
|
||
|
|
event = self._create_event("end", hook_name, data)
|
||
|
|
self.logger.info(json.dumps(event))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Lifecycle Event Data**:
|
||
|
|
- **Start Events**: Hook name, optional context data, timestamp
|
||
|
|
- **End Events**: Duration in milliseconds, success/failure status, optional results
|
||
|
|
- **Session Correlation**: All events include session ID for correlation
|
||
|
|
|
||
|
|
### Decision Logging
|
||
|
|
```python
|
||
|
|
def log_decision(self, hook_name: str, decision_type: str, choice: str, reason: str):
|
||
|
|
"""Log a decision made by a hook."""
|
||
|
|
if not self._should_log_event(hook_name, 'decision'):
|
||
|
|
return
|
||
|
|
|
||
|
|
data = {
|
||
|
|
"type": decision_type,
|
||
|
|
"choice": choice,
|
||
|
|
"reason": reason
|
||
|
|
}
|
||
|
|
event = self._create_event("decision", hook_name, data)
|
||
|
|
self.logger.info(json.dumps(event))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Decision Event Components**:
|
||
|
|
- **type**: Category of decision (e.g., "mcp_server_selection", "mode_activation")
|
||
|
|
- **choice**: The decision made (e.g., "sequential", "brainstorming_mode")
|
||
|
|
- **reason**: Explanation for the decision (e.g., "complexity_score > 0.6")
|
||
|
|
|
||
|
|
### Error Logging
|
||
|
|
```python
|
||
|
|
def log_error(self, hook_name: str, error: str, context: Optional[Dict[str, Any]] = None):
|
||
|
|
"""Log an error that occurred in a hook."""
|
||
|
|
if not self._should_log_event(hook_name, 'error'):
|
||
|
|
return
|
||
|
|
|
||
|
|
data = {
|
||
|
|
"error": error
|
||
|
|
}
|
||
|
|
if context:
|
||
|
|
data["context"] = context
|
||
|
|
|
||
|
|
event = self._create_event("error", hook_name, data)
|
||
|
|
self.logger.info(json.dumps(event))
|
||
|
|
```
|
||
|
|
|
||
|
|
**Error Event Components**:
|
||
|
|
- **error**: Error message or description
|
||
|
|
- **context**: Optional additional context about error conditions
|
||
|
|
- **Timestamp**: Precise timing for error correlation
|
||
|
|
|
||
|
|
## Log File Management
|
||
|
|
|
||
|
|
### Automatic Cleanup
|
||
|
|
```python
|
||
|
|
def _cleanup_old_logs(self):
|
||
|
|
"""Remove log files older than retention_days."""
|
||
|
|
if self.retention_days <= 0:
|
||
|
|
return
|
||
|
|
|
||
|
|
cutoff_date = datetime.now() - timedelta(days=self.retention_days)
|
||
|
|
|
||
|
|
# Find all log files
|
||
|
|
log_pattern = self.log_dir / "superclaude-lite-*.log"
|
||
|
|
for log_file in glob.glob(str(log_pattern)):
|
||
|
|
try:
|
||
|
|
# Extract date from filename
|
||
|
|
filename = os.path.basename(log_file)
|
||
|
|
date_str = filename.replace("superclaude-lite-", "").replace(".log", "")
|
||
|
|
file_date = datetime.strptime(date_str, "%Y-%m-%d")
|
||
|
|
|
||
|
|
# Remove if older than cutoff
|
||
|
|
if file_date < cutoff_date:
|
||
|
|
os.remove(log_file)
|
||
|
|
|
||
|
|
except (ValueError, OSError):
|
||
|
|
# Skip files that don't match expected format or can't be removed
|
||
|
|
continue
|
||
|
|
```
|
||
|
|
|
||
|
|
**Cleanup Features**:
|
||
|
|
- **Configurable Retention**: Retention period set via configuration
|
||
|
|
- **Date-Based**: Log files named with YYYY-MM-DD format for easy parsing
|
||
|
|
- **Error Resilience**: Skip problematic files rather than failing entire cleanup
|
||
|
|
- **Initialization Cleanup**: Cleanup performed during logger initialization
|
||
|
|
|
||
|
|
### Log File Naming Convention
|
||
|
|
```
|
||
|
|
superclaude-lite-2024-12-15.log
|
||
|
|
superclaude-lite-2024-12-16.log
|
||
|
|
superclaude-lite-2024-12-17.log
|
||
|
|
```
|
||
|
|
|
||
|
|
**Naming Benefits**:
|
||
|
|
- **Chronological Sorting**: Files sort naturally by name
|
||
|
|
- **Easy Filtering**: Date-based filtering for log analysis
|
||
|
|
- **Rotation-Friendly**: Daily rotation without complex log rotation tools
|
||
|
|
|
||
|
|
## Global Interface and Convenience Functions
|
||
|
|
|
||
|
|
### Global Logger Instance
|
||
|
|
```python
|
||
|
|
# Global logger instance
|
||
|
|
_logger = None
|
||
|
|
|
||
|
|
def get_logger() -> HookLogger:
|
||
|
|
"""Get the global logger instance."""
|
||
|
|
global _logger
|
||
|
|
if _logger is None:
|
||
|
|
_logger = HookLogger()
|
||
|
|
return _logger
|
||
|
|
```
|
||
|
|
|
||
|
|
### Convenience Functions
|
||
|
|
```python
|
||
|
|
def log_hook_start(hook_name: str, context: Optional[Dict[str, Any]] = None):
|
||
|
|
"""Log the start of a hook execution."""
|
||
|
|
get_logger().log_hook_start(hook_name, context)
|
||
|
|
|
||
|
|
def log_hook_end(hook_name: str, duration_ms: int, success: bool, result: Optional[Dict[str, Any]] = None):
|
||
|
|
"""Log the end of a hook execution."""
|
||
|
|
get_logger().log_hook_end(hook_name, duration_ms, success, result)
|
||
|
|
|
||
|
|
def log_decision(hook_name: str, decision_type: str, choice: str, reason: str):
|
||
|
|
"""Log a decision made by a hook."""
|
||
|
|
get_logger().log_decision(hook_name, decision_type, choice, reason)
|
||
|
|
|
||
|
|
def log_error(hook_name: str, error: str, context: Optional[Dict[str, Any]] = None):
|
||
|
|
"""Log an error that occurred in a hook."""
|
||
|
|
get_logger().log_error(hook_name, error, context)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Global Interface Benefits**:
|
||
|
|
- **Simplified Import**: Single import for all logging functions
|
||
|
|
- **Consistent Configuration**: Shared configuration across all hooks
|
||
|
|
- **Lazy Initialization**: Logger created only when first used
|
||
|
|
- **Memory Efficiency**: Single logger instance for entire application
|
||
|
|
|
||
|
|
## Hook Integration Patterns
|
||
|
|
|
||
|
|
### Basic Hook Integration
|
||
|
|
```python
|
||
|
|
from shared.logger import log_hook_start, log_hook_end, log_decision, log_error
|
||
|
|
import time
|
||
|
|
|
||
|
|
def pre_tool_use_hook(context):
|
||
|
|
start_time = time.time()
|
||
|
|
|
||
|
|
# Log hook start
|
||
|
|
log_hook_start("pre_tool_use", {"operation_type": context.get("operation_type")})
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Hook logic
|
||
|
|
if context.get("complexity_score", 0) > 0.6:
|
||
|
|
# Log decision
|
||
|
|
log_decision("pre_tool_use", "delegation_activation", "enabled", "complexity_score > 0.6")
|
||
|
|
result = {"delegation_enabled": True}
|
||
|
|
else:
|
||
|
|
result = {"delegation_enabled": False}
|
||
|
|
|
||
|
|
# Log successful completion
|
||
|
|
duration_ms = int((time.time() - start_time) * 1000)
|
||
|
|
log_hook_end("pre_tool_use", duration_ms, True, result)
|
||
|
|
|
||
|
|
return result
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
# Log error
|
||
|
|
log_error("pre_tool_use", str(e), {"context": context})
|
||
|
|
|
||
|
|
# Log failed completion
|
||
|
|
duration_ms = int((time.time() - start_time) * 1000)
|
||
|
|
log_hook_end("pre_tool_use", duration_ms, False)
|
||
|
|
|
||
|
|
raise
|
||
|
|
```
|
||
|
|
|
||
|
|
### Advanced Integration with Context
|
||
|
|
```python
|
||
|
|
def session_start_hook(context):
|
||
|
|
# Start with rich context
|
||
|
|
log_hook_start("session_start", {
|
||
|
|
"project_path": context.get("project_path"),
|
||
|
|
"user_expertise": context.get("user_expertise", "intermediate"),
|
||
|
|
"session_type": context.get("session_type", "interactive")
|
||
|
|
})
|
||
|
|
|
||
|
|
# Log multiple decisions
|
||
|
|
log_decision("session_start", "configuration_load", "superclaude-config.json", "project configuration detected")
|
||
|
|
log_decision("session_start", "learning_engine", "enabled", "user preference learning available")
|
||
|
|
|
||
|
|
# Complex result logging
|
||
|
|
result = {
|
||
|
|
"configuration_loaded": True,
|
||
|
|
"hooks_initialized": 7,
|
||
|
|
"performance_targets": {
|
||
|
|
"session_start_ms": 50,
|
||
|
|
"pre_tool_use_ms": 200
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
log_hook_end("session_start", 45, True, result)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Log Analysis and Monitoring
|
||
|
|
|
||
|
|
### Log Entry Format
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"timestamp": "2024-12-15T14:30:22.123456Z",
|
||
|
|
"session": "abc12345",
|
||
|
|
"hook": "pre_tool_use",
|
||
|
|
"event": "start",
|
||
|
|
"data": {
|
||
|
|
"operation_type": "build",
|
||
|
|
"complexity_score": 0.7
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Log Sequence
|
||
|
|
```json
|
||
|
|
{"timestamp": "2024-12-15T14:30:22.123Z", "session": "abc12345", "hook": "pre_tool_use", "event": "start", "data": {"operation_type": "build"}}
|
||
|
|
{"timestamp": "2024-12-15T14:30:22.125Z", "session": "abc12345", "hook": "pre_tool_use", "event": "decision", "data": {"type": "mcp_server_selection", "choice": "sequential", "reason": "complex analysis required"}}
|
||
|
|
{"timestamp": "2024-12-15T14:30:22.148Z", "session": "abc12345", "hook": "pre_tool_use", "event": "end", "data": {"duration_ms": 25, "success": true, "result": {"mcp_servers": ["sequential"]}}}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Analysis Queries
|
||
|
|
```bash
|
||
|
|
# Find all errors in the last day
|
||
|
|
jq 'select(.event == "error")' superclaude-lite-2024-12-15.log
|
||
|
|
|
||
|
|
# Calculate average hook execution times
|
||
|
|
jq 'select(.event == "end") | .data.duration_ms' superclaude-lite-2024-12-15.log | awk '{sum+=$1; count++} END {print sum/count}'
|
||
|
|
|
||
|
|
# Find all decisions made by specific hook
|
||
|
|
jq 'select(.hook == "pre_tool_use" and .event == "decision")' superclaude-lite-2024-12-15.log
|
||
|
|
|
||
|
|
# Track session completion rates
|
||
|
|
jq 'select(.hook == "session_start" and .event == "end") | .data.success' superclaude-lite-2024-12-15.log
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance Characteristics
|
||
|
|
|
||
|
|
### Logging Performance
|
||
|
|
- **Event Creation**: <1ms for structured event creation
|
||
|
|
- **File Writing**: <5ms for typical log entry with JSON serialization
|
||
|
|
- **Configuration Loading**: <10ms during initialization
|
||
|
|
- **Cleanup Operations**: <50ms for cleanup of old log files (depends on file count)
|
||
|
|
|
||
|
|
### Memory Efficiency
|
||
|
|
- **Logger Instance**: ~1-2KB for logger instance with configuration
|
||
|
|
- **Session Tracking**: ~100B for session ID and correlation data
|
||
|
|
- **Event Buffer**: Direct write-through, no event buffering for reliability
|
||
|
|
- **Configuration Cache**: ~500B for logging configuration
|
||
|
|
|
||
|
|
### File System Impact
|
||
|
|
- **Daily Log Files**: Automatic daily rotation with configurable retention
|
||
|
|
- **Log File Size**: Typical ~10-50KB per day depending on hook activity
|
||
|
|
- **Directory Structure**: Simple flat file structure in configurable directory
|
||
|
|
- **Cleanup Efficiency**: O(n) cleanup where n is number of log files
|
||
|
|
|
||
|
|
## Error Handling and Reliability
|
||
|
|
|
||
|
|
### Logging Error Handling
|
||
|
|
```python
|
||
|
|
def log_hook_start(self, hook_name: str, context: Optional[Dict[str, Any]] = None):
|
||
|
|
"""Log the start of a hook execution."""
|
||
|
|
try:
|
||
|
|
if not self._should_log_event(hook_name, 'start'):
|
||
|
|
return
|
||
|
|
|
||
|
|
event = self._create_event("start", hook_name, context)
|
||
|
|
self.logger.info(json.dumps(event))
|
||
|
|
except Exception:
|
||
|
|
# Silent failure - logging should never break hook execution
|
||
|
|
pass
|
||
|
|
```
|
||
|
|
|
||
|
|
### Reliability Features
|
||
|
|
- **Silent Failure**: Logging errors never interrupt hook execution
|
||
|
|
- **Graceful Degradation**: Continue operation even if logging fails
|
||
|
|
- **Configuration Fallback**: Default configuration if loading fails
|
||
|
|
- **File System Resilience**: Handle permission errors and disk space issues
|
||
|
|
|
||
|
|
### Recovery Mechanisms
|
||
|
|
- **Logger Recreation**: Recreate logger if file handle issues occur
|
||
|
|
- **Directory Creation**: Automatically create log directory if missing
|
||
|
|
- **Permission Handling**: Graceful fallback if log directory not writable
|
||
|
|
- **Disk Space**: Continue operation even if disk space limited
|
||
|
|
|
||
|
|
## Configuration Examples
|
||
|
|
|
||
|
|
### Basic Configuration (logging.yaml)
|
||
|
|
```yaml
|
||
|
|
logging:
|
||
|
|
enabled: true
|
||
|
|
level: INFO
|
||
|
|
|
||
|
|
file_settings:
|
||
|
|
log_directory: cache/logs
|
||
|
|
retention_days: 30
|
||
|
|
|
||
|
|
hook_logging:
|
||
|
|
log_lifecycle: true
|
||
|
|
log_decisions: true
|
||
|
|
log_errors: true
|
||
|
|
```
|
||
|
|
|
||
|
|
### Advanced Configuration
|
||
|
|
```yaml
|
||
|
|
logging:
|
||
|
|
enabled: true
|
||
|
|
level: DEBUG
|
||
|
|
|
||
|
|
file_settings:
|
||
|
|
log_directory: ${LOG_DIR:./logs}
|
||
|
|
retention_days: ${LOG_RETENTION:7}
|
||
|
|
max_file_size_mb: 10
|
||
|
|
|
||
|
|
hook_logging:
|
||
|
|
log_lifecycle: true
|
||
|
|
log_decisions: true
|
||
|
|
log_errors: true
|
||
|
|
log_performance: true
|
||
|
|
|
||
|
|
hook_configuration:
|
||
|
|
session_start:
|
||
|
|
enabled: true
|
||
|
|
pre_tool_use:
|
||
|
|
enabled: true
|
||
|
|
post_tool_use:
|
||
|
|
enabled: false # Disable logging for this hook
|
||
|
|
pre_compact:
|
||
|
|
enabled: true
|
||
|
|
```
|
||
|
|
|
||
|
|
### Production Configuration
|
||
|
|
```yaml
|
||
|
|
logging:
|
||
|
|
enabled: true
|
||
|
|
level: WARNING # Reduce verbosity in production
|
||
|
|
|
||
|
|
file_settings:
|
||
|
|
log_directory: /var/log/superclaude
|
||
|
|
retention_days: 90
|
||
|
|
|
||
|
|
hook_logging:
|
||
|
|
log_lifecycle: false # Disable lifecycle logging
|
||
|
|
log_decisions: true # Keep decision logging
|
||
|
|
log_errors: true # Always log errors
|
||
|
|
```
|
||
|
|
|
||
|
|
## Usage Examples
|
||
|
|
|
||
|
|
### Basic Logging
|
||
|
|
```python
|
||
|
|
from shared.logger import log_hook_start, log_hook_end, log_decision, log_error
|
||
|
|
|
||
|
|
# Simple hook with logging
|
||
|
|
def my_hook(context):
|
||
|
|
log_hook_start("my_hook")
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Do work
|
||
|
|
result = perform_operation()
|
||
|
|
log_hook_end("my_hook", 150, True, {"result": result})
|
||
|
|
return result
|
||
|
|
except Exception as e:
|
||
|
|
log_error("my_hook", str(e))
|
||
|
|
log_hook_end("my_hook", 150, False)
|
||
|
|
raise
|
||
|
|
```
|
||
|
|
|
||
|
|
### Decision Logging
|
||
|
|
```python
|
||
|
|
def intelligent_hook(context):
|
||
|
|
log_hook_start("intelligent_hook", {"complexity": context.get("complexity_score")})
|
||
|
|
|
||
|
|
# Log decision-making process
|
||
|
|
if context.get("complexity_score", 0) > 0.6:
|
||
|
|
log_decision("intelligent_hook", "server_selection", "sequential", "high complexity detected")
|
||
|
|
server = "sequential"
|
||
|
|
else:
|
||
|
|
log_decision("intelligent_hook", "server_selection", "morphllm", "low complexity operation")
|
||
|
|
server = "morphllm"
|
||
|
|
|
||
|
|
log_hook_end("intelligent_hook", 85, True, {"selected_server": server})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Error Context Logging
|
||
|
|
```python
|
||
|
|
def error_prone_hook(context):
|
||
|
|
log_hook_start("error_prone_hook")
|
||
|
|
|
||
|
|
try:
|
||
|
|
risky_operation()
|
||
|
|
except SpecificError as e:
|
||
|
|
log_error("error_prone_hook", f"Specific error: {e}", {
|
||
|
|
"context": context,
|
||
|
|
"error_type": "SpecificError",
|
||
|
|
"recovery_attempted": True
|
||
|
|
})
|
||
|
|
# Attempt recovery
|
||
|
|
recovery_operation()
|
||
|
|
except Exception as e:
|
||
|
|
log_error("error_prone_hook", f"Unexpected error: {e}", {
|
||
|
|
"context": context,
|
||
|
|
"error_type": type(e).__name__
|
||
|
|
})
|
||
|
|
raise
|
||
|
|
```
|
||
|
|
|
||
|
|
## Dependencies and Relationships
|
||
|
|
|
||
|
|
### Internal Dependencies
|
||
|
|
- **yaml_loader**: Configuration loading (optional, fallback available)
|
||
|
|
- **Standard Libraries**: json, logging, os, time, datetime, pathlib, glob, uuid
|
||
|
|
|
||
|
|
### Framework Integration
|
||
|
|
- **Hook Lifecycle**: Integrated into all 7 SuperClaude hooks for consistent logging
|
||
|
|
- **Global Interface**: Shared logger instance across all hooks and modules
|
||
|
|
- **Configuration Management**: Unified configuration via yaml_loader integration
|
||
|
|
|
||
|
|
### External Analysis
|
||
|
|
- **JSON Format**: Structured logs for analysis with jq, logstash, elasticsearch
|
||
|
|
- **Daily Rotation**: Compatible with log analysis tools expecting daily files
|
||
|
|
- **Session Correlation**: Event correlation for debugging and monitoring
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
*This module provides the essential logging infrastructure for the SuperClaude framework, enabling comprehensive operational monitoring, debugging, and analysis through structured, high-performance event logging with reliable error handling and flexible configuration.*
|