e43ca83984
- product-builder.md: implementation-orchestrator -> implementation-pool-supervisor - async-agent-manager.md: updated entire valid_agents list (15+ stale names) - async-agent-monitor.md: implementation-orchestrator -> implementation-pool-supervisor - shared/logging.md: pr-self-reviewer -> pr-reviewer - agent-evolution-pool-supervisor.md: continuous-pr-reviewer -> pr-review-pool-supervisor - shared/tracking_discovery_guide.md: implementation-orchestrator -> implementation-pool-supervisor
365 lines
11 KiB
Markdown
365 lines
11 KiB
Markdown
# Standardized Logging Format
|
|
|
|
This document defines the mandatory logging format and utilities that all agents MUST use to ensure consistent, searchable, and debuggable logs across the system.
|
|
|
|
## Log Format Specification
|
|
|
|
All agents MUST use this exact format for all log entries:
|
|
|
|
```
|
|
[AGENT:{name}][SESSION:{id}][ACTION:{action}][LEVEL:{level}] {message}
|
|
```
|
|
|
|
### Components
|
|
|
|
- **AGENT**: The agent's name (e.g., `implementation-worker`, `pr-reviewer`)
|
|
- **SESSION**: Unique session identifier (use first 8 chars if long)
|
|
- **ACTION**: The current operation (e.g., `clone`, `test`, `review`, `merge`)
|
|
- **LEVEL**: Log level - `INFO`, `WARNING`, `ERROR`, `DEBUG`
|
|
- **Message**: Human-readable description of what happened
|
|
|
|
### Examples
|
|
|
|
```
|
|
[AGENT:implementation-worker][SESSION:abc123de][ACTION:clone][LEVEL:INFO] Cloning repository to /tmp/impl-123
|
|
[AGENT:pr-reviewer][SESSION:def456gh][ACTION:review][LEVEL:WARNING] Found 3 CONTRIBUTING.md violations
|
|
[AGENT:behave-tester][SESSION:ghi789jk][ACTION:test][LEVEL:ERROR] Test execution failed: 5 scenarios failed
|
|
```
|
|
|
|
## Python Implementation
|
|
|
|
```python
|
|
import json
|
|
import time
|
|
from datetime import datetime
|
|
from typing import Optional, Dict, Any
|
|
|
|
class AgentLogger:
|
|
"""Standardized logger for all CleverAgents agents."""
|
|
|
|
def __init__(self, agent_name: str, session_id: str,
|
|
session_state_issue: Optional[int] = None,
|
|
owner: Optional[str] = None,
|
|
repo: Optional[str] = None):
|
|
"""
|
|
Initialize the logger.
|
|
|
|
Args:
|
|
agent_name: Name of the agent
|
|
session_id: Unique session identifier
|
|
session_state_issue: Forgejo issue number for persistence
|
|
owner: Repository owner (for Forgejo persistence)
|
|
repo: Repository name (for Forgejo persistence)
|
|
"""
|
|
self.agent_name = agent_name
|
|
self.session_id = session_id[:8] if len(session_id) > 8 else session_id
|
|
self.session_state_issue = session_state_issue
|
|
self.owner = owner
|
|
self.repo = repo
|
|
self.current_action = "init"
|
|
|
|
def set_action(self, action: str):
|
|
"""Set the current action context."""
|
|
self.current_action = action
|
|
|
|
def _format_log(self, level: str, message: str, action: Optional[str] = None) -> str:
|
|
"""Format a log entry according to standards."""
|
|
action_str = action or self.current_action
|
|
return f"[AGENT:{self.agent_name}][SESSION:{self.session_id}][ACTION:{action_str}][LEVEL:{level}] {message}"
|
|
|
|
def _persist_critical(self, log_line: str):
|
|
"""Persist critical logs to Forgejo."""
|
|
if self.session_state_issue and self.owner and self.repo:
|
|
try:
|
|
comment = f"[LOG] Critical Event\n```\n{log_line}\n```\n\n---\n**Automated by CleverAgents Bot**"
|
|
forgejo_create_issue_comment(self.owner, self.repo,
|
|
self.session_state_issue, comment)
|
|
except:
|
|
pass # Best effort
|
|
|
|
def info(self, message: str, action: Optional[str] = None):
|
|
"""Log an informational message."""
|
|
log_line = self._format_log("INFO", message, action)
|
|
print(log_line)
|
|
return log_line
|
|
|
|
def warning(self, message: str, action: Optional[str] = None):
|
|
"""Log a warning message."""
|
|
log_line = self._format_log("WARNING", message, action)
|
|
print(log_line)
|
|
return log_line
|
|
|
|
def error(self, message: str, action: Optional[str] = None, persist: bool = True):
|
|
"""Log an error message."""
|
|
log_line = self._format_log("ERROR", message, action)
|
|
print(log_line)
|
|
|
|
if persist:
|
|
self._persist_critical(log_line)
|
|
|
|
return log_line
|
|
|
|
def debug(self, message: str, action: Optional[str] = None):
|
|
"""Log a debug message."""
|
|
log_line = self._format_log("DEBUG", message, action)
|
|
print(log_line)
|
|
return log_line
|
|
|
|
def structured(self, action: str, data: Dict[str, Any], level: str = "INFO"):
|
|
"""Log structured data for automated analysis."""
|
|
entry = {
|
|
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
"agent": self.agent_name,
|
|
"session": self.session_id,
|
|
"action": action,
|
|
"level": level,
|
|
"data": data
|
|
}
|
|
|
|
# Standard format for visibility
|
|
message = f"Structured data: {action}"
|
|
self._format_log(level, message, action)
|
|
|
|
# JSON format for parsing
|
|
print(f"[STRUCTURED] {json.dumps(entry)}")
|
|
|
|
# Persist if critical
|
|
if level in ["ERROR", "CRITICAL"]:
|
|
self._persist_critical(f"[STRUCTURED] {json.dumps(entry, indent=2)}")
|
|
|
|
return entry
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
# Initialize at agent start
|
|
logger = AgentLogger(
|
|
agent_name="implementation-worker",
|
|
session_id=session_id,
|
|
session_state_issue=SESSION_STATE_ISSUE,
|
|
owner=owner,
|
|
repo=repo
|
|
)
|
|
|
|
# Log with current action context
|
|
logger.set_action("setup")
|
|
logger.info("Validating credentials")
|
|
logger.info("Loading reference materials")
|
|
|
|
# Log with specific action
|
|
logger.info("Repository cloned successfully", action="clone")
|
|
|
|
# Log warnings
|
|
logger.warning("Retry attempt 2 of 3", action="push")
|
|
|
|
# Log errors (automatically persisted)
|
|
logger.error("Failed to merge PR: CI checks failing", action="merge")
|
|
|
|
# Log debug info
|
|
logger.debug(f"Queue depth: {len(queue)}", action="dispatch")
|
|
```
|
|
|
|
### Structured Logging
|
|
|
|
```python
|
|
# Log complex data for analysis
|
|
logger.structured("worker_dispatched", {
|
|
"issue_number": 123,
|
|
"worker_session": worker_session_id,
|
|
"priority": "high",
|
|
"attempt": 1
|
|
})
|
|
|
|
# Log performance metrics
|
|
logger.structured("operation_complete", {
|
|
"operation": "subtask_implementation",
|
|
"duration_seconds": 145.3,
|
|
"lines_changed": 230,
|
|
"files_modified": 5
|
|
}, level="INFO")
|
|
|
|
# Log errors with context
|
|
logger.structured("merge_failed", {
|
|
"pr_number": 456,
|
|
"reason": "insufficient_approvals",
|
|
"current_approvals": 1,
|
|
"required_approvals": 2
|
|
}, level="ERROR")
|
|
```
|
|
|
|
### Integration with Error Handling
|
|
|
|
```python
|
|
from shared.error_handling import safe_operation_with_recovery
|
|
from shared.logging import AgentLogger
|
|
|
|
logger = AgentLogger("implementation-worker", session_id)
|
|
|
|
def clone_with_logging(repo_url, target_dir):
|
|
logger.set_action("clone")
|
|
|
|
def do_clone():
|
|
logger.info(f"Starting clone of {repo_url}")
|
|
result = git_clone(repo_url, target_dir)
|
|
logger.info("Clone completed successfully")
|
|
return result
|
|
|
|
def recovery(error_type, error):
|
|
logger.warning(f"Recovery attempt for {error_type}: {error}")
|
|
# Recovery logic...
|
|
|
|
try:
|
|
return safe_operation_with_recovery(
|
|
"clone repository",
|
|
do_clone,
|
|
recovery
|
|
)
|
|
except OperationFailedError as e:
|
|
logger.error(f"Clone failed after all attempts: {e}")
|
|
raise
|
|
```
|
|
|
|
### Context Manager for Actions
|
|
|
|
```python
|
|
class LoggedAction:
|
|
"""Context manager for action-scoped logging."""
|
|
|
|
def __init__(self, logger: AgentLogger, action: str):
|
|
self.logger = logger
|
|
self.action = action
|
|
self.previous_action = None
|
|
|
|
def __enter__(self):
|
|
self.previous_action = self.logger.current_action
|
|
self.logger.set_action(self.action)
|
|
self.logger.info(f"Starting {self.action}")
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
if exc_type:
|
|
self.logger.error(f"{self.action} failed: {exc_val}")
|
|
else:
|
|
self.logger.info(f"{self.action} completed")
|
|
|
|
# Restore previous action
|
|
self.logger.set_action(self.previous_action)
|
|
|
|
# Don't suppress exceptions
|
|
return False
|
|
|
|
# Usage
|
|
with LoggedAction(logger, "test_execution"):
|
|
run_tests() # Logs automatically scoped to this action
|
|
```
|
|
|
|
## Log Aggregation Patterns
|
|
|
|
### Health Signals
|
|
|
|
```python
|
|
def log_health_signal(logger: AgentLogger, metrics: Dict[str, Any]):
|
|
"""Log standardized health signal."""
|
|
|
|
logger.structured("health_signal", {
|
|
"cycle": metrics.get("cycle", 0),
|
|
"active_workers": metrics.get("active_workers", 0),
|
|
"queue_depth": metrics.get("queue_depth", 0),
|
|
"failure_rate": metrics.get("failure_rate", 0),
|
|
"status": "active"
|
|
}, level="INFO")
|
|
```
|
|
|
|
### Operation Summaries
|
|
|
|
```python
|
|
def log_operation_summary(logger: AgentLogger, operation: str,
|
|
start_time: float, success: bool,
|
|
details: Optional[Dict] = None):
|
|
"""Log operation completion with timing."""
|
|
|
|
duration = time.time() - start_time
|
|
|
|
data = {
|
|
"operation": operation,
|
|
"duration_seconds": round(duration, 2),
|
|
"success": success
|
|
}
|
|
|
|
if details:
|
|
data.update(details)
|
|
|
|
level = "INFO" if success else "ERROR"
|
|
logger.structured(f"{operation}_complete", data, level=level)
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Always initialize logger at agent start** - Don't create multiple instances
|
|
2. **Set action context** - Use `set_action()` for logical operation groups
|
|
3. **Be consistent with action names** - Use standard names across agents
|
|
4. **Log state changes** - Entry/exit of major operations
|
|
5. **Include relevant context** - But avoid logging sensitive data
|
|
6. **Use appropriate levels** - INFO for normal, WARNING for recoverable, ERROR for failures
|
|
7. **Structure complex data** - Use `structured()` for analysis-friendly logs
|
|
|
|
## Standard Action Names
|
|
|
|
Use these standardized action names for consistency:
|
|
|
|
- **init** - Agent initialization
|
|
- **setup** - Environment setup
|
|
- **clone** - Repository cloning
|
|
- **implement** - Code implementation
|
|
- **test** - Test execution
|
|
- **review** - Code review
|
|
- **merge** - PR merging
|
|
- **dispatch** - Worker dispatch
|
|
- **monitor** - Health monitoring
|
|
- **cleanup** - Resource cleanup
|
|
|
|
## Anti-Patterns to Avoid
|
|
|
|
```python
|
|
# ❌ WRONG - Inconsistent format
|
|
print(f"Starting work on issue {issue_number}")
|
|
|
|
# ✓ CORRECT - Use logger
|
|
logger.info(f"Starting work on issue {issue_number}")
|
|
|
|
# ❌ WRONG - No action context
|
|
logger.info("Failed") # Failed what?
|
|
|
|
# ✓ CORRECT - Clear action context
|
|
logger.error("Repository clone failed", action="clone")
|
|
|
|
# ❌ WRONG - Logging credentials
|
|
logger.info(f"Using PAT: {pat}")
|
|
|
|
# ✓ CORRECT - Log presence, not value
|
|
logger.info("Forgejo PAT validated")
|
|
|
|
# ❌ WRONG - Unstructured complex data
|
|
logger.info(f"Stats: workers={w}, queue={q}, failed={f}")
|
|
|
|
# ✓ CORRECT - Structured for parsing
|
|
logger.structured("pool_stats", {
|
|
"workers": w,
|
|
"queue_depth": q,
|
|
"failures": f
|
|
})
|
|
```
|
|
|
|
## Integration Requirements
|
|
|
|
All agents MUST:
|
|
|
|
1. Import and initialize AgentLogger at startup
|
|
2. Use ONLY the logger for output (no raw print statements)
|
|
3. Set appropriate action context
|
|
4. Log all state transitions
|
|
5. Persist ERROR level logs to Forgejo
|
|
6. Use structured logging for metrics and complex data |