Structured Logging Quick Reference
Quick reference for using structured logging in AgentFarm.
Setup
from farm.utils import configure_logging, get_logger
# At application startup
configure_logging(environment="development", log_level="INFO")
# In your module
logger = get_logger(__name__)
Basic Logging
# Event-style logging (preferred)
logger.info("event_name", key1=value1, key2=value2)
# Common levels
logger.debug("detailed_info", ...) # Diagnostic information
logger.info("important_event", ...) # General events
logger.warning("warning_event", ...) # Warnings
logger.error("error_event", ...) # Errors
logger.critical("critical_event", ...) # Critical failures
Context Binding
from farm.utils import bind_context, unbind_context, log_context
# Global context (persists across all logs)
bind_context(simulation_id="sim_001")
logger.info("event") # Includes simulation_id
unbind_context("simulation_id")
# Scoped context (automatic cleanup)
with log_context(simulation_id="sim_001"):
logger.info("event") # Includes simulation_id
# Auto-cleaned up here
# Logger-level binding (permanent for this logger)
my_logger = logger.bind(component="spatial_index")
my_logger.info("event") # Always includes component
Context Managers
from farm.utils import log_simulation, log_step, log_experiment
# Simulation
with log_simulation(simulation_id="sim_001", num_agents=100):
run_simulation()
# Step
with log_step(step_number=42, simulation_id="sim_001"):
process_step()
# Experiment
with log_experiment(experiment_id="exp_001", experiment_name="test"):
run_experiment()
Decorators
from farm.utils import log_performance, log_errors
# Performance logging
@log_performance(operation_name="compute", slow_threshold_ms=100.0)
def my_function():
pass
# Error logging
@log_errors()
def risky_function():
pass
Specialized Loggers
from farm.utils import AgentLogger, LogSampler
# Agent logger
agent_logger = AgentLogger(agent_id="agent_001", agent_type="system")
agent_logger.log_action("move", success=True, reward=0.5)
agent_logger.log_interaction("share", target_id="agent_002")
agent_logger.log_death(cause="starvation")
# Log sampling (10% of events)
sampler = LogSampler(sample_rate=0.1)
if sampler.should_log():
logger.debug("high_frequency_event")
Error Logging
try:
risky_operation()
except Exception as e:
logger.error(
"operation_failed",
error_type=type(e).__name__,
error_message=str(e),
exc_info=True, # Include traceback
)
Common Patterns
Simulation Start
with log_simulation(simulation_id="sim_001", num_agents=100, num_steps=1000):
logger.info("environment_initialized", grid_size=(100, 100))
for step in range(num_steps):
with log_step(step_number=step):
process_step()
Agent Actions
agent_logger = AgentLogger(agent_id, agent_type)
agent_logger.log_action(
action_type="move",
success=True,
reward=0.5,
position=(10, 20),
)
Database Operations
logger.info("database_query_started", query_type="select", table="agents")
# ... execute query
logger.info("database_query_completed", duration_ms=45.2, rows_returned=100)
Performance Tracking
@log_performance(operation_name="spatial_index_rebuild", slow_threshold_ms=100.0)
def rebuild_index():
# Will automatically log duration and warn if > 100ms
pass
Migration Quick Guide
# OLD
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format="...")
logger.info(f"Agent {agent_id} moved to {pos}")
logger.error(f"Error: {e}", exc_info=True)
# NEW
from farm.utils import get_logger, configure_logging
configure_logging(environment="development", log_level="INFO")
logger = get_logger(__name__)
logger.info("agent_moved", agent_id=agent_id, position=pos)
logger.error("error_occurred", error_type=type(e).__name__,
error_message=str(e), exc_info=True)
Best Practices
✅ DO:
- Use event names:
logger.info("simulation_started", ...) - Include rich context:
logger.info("event", agent_id=id, position=pos, ...) - Bind context early:
bind_context(simulation_id=sim_id) - Use appropriate log levels
- Sample high-frequency logs:
LogSampler(sample_rate=0.1)
❌ DON’T:
- Use f-strings:
logger.info(f"Event: {value}") - Log messages without context:
logger.info("Something happened") - Log sensitive data:
logger.info("login", password=pwd) - Log large objects without truncation
- Skip error context:
logger.error("failed")
Output Formats
Development Console
2025-10-01T12:34:56Z [info] simulation_started simulation_id=sim_001 num_agents=100
Production JSON
{"timestamp": "2025-10-01T12:34:56Z", "level": "info", "event": "simulation_started", "simulation_id": "sim_001", "num_agents": 100}
Common Fields
Include these fields for consistency:
| Field | When | Example |
|---|---|---|
simulation_id |
All simulation logs | "sim_001" |
step or step_number |
Per-step logs | 42 |
agent_id |
Agent-related logs | "agent_123" |
agent_type |
Agent logs | "system" |
error_type |
Error logs | "ValueError" |
error_message |
Error logs | "Invalid input" |
duration_ms |
Performance logs | 123.45 |
operation |
Performance logs | "rebuild_index" |
Configuration Options
configure_logging(
environment="development", # "development", "production", "testing"
log_dir="logs", # Directory for log files (None = console only)
log_level="INFO", # "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
json_logs=False, # True for JSON output
enable_colors=True, # Colored console output
include_caller_info=True, # Include file/line/function info
)
CLI Arguments (run_simulation.py)
python run_simulation.py \
--log-level DEBUG \
--json-logs \
--environment production \
--steps 1000
Troubleshooting
| Problem | Solution |
|---|---|
| Logs not appearing | Call configure_logging() first |
| Context not persisting | Use bind_context() or context managers |
| Too many logs | Use LogSampler or increase log level |
| Performance impact | Sample high-frequency logs, use DEBUG level |
Links
- Full Guide: logging_guide.md
- Examples: farm/utils/logging/README.md