Skip to content

Observability & Monitoring

Overview

You can't fix what you can't see. Observability is the foundation of production systems.


Structured Logging

Agent Execution Logs

class StructuredAgentLogging:
 """Log every agent action in structured format"""

 def log_execution_step(self, step_data):
 """Log structured step data"""

 log_entry = {
 'timestamp': time.time(),
 'request_id': step_data.request_id,
 'agent_id': step_data.agent_id,
 'step_number': step_data.step_number,
 'action': step_data.action,
 'input': step_data.input,
 'output': step_data.output,
 'latency_ms': step_data.latency_ms,
 'tokens_used': step_data.tokens,
 'cost': step_data.cost,
 'status': 'success'| 'error'| 'timeout',
 'metadata': {
 'model': step_data.model,
 'temperature': step_data.temperature
 }
 }

 # Log to structured storage
 self.structured_logger.info(log_entry)

 def query_logs(self, request_id):
 """Retrieve all logs for a request"""

 logs = self.structured_logger.filter(
 request_id=request_id
).order_by('timestamp')

 return logs

Trace Collection

End-to-End Request Tracing

class RequestTracing:
 """Track request through entire system"""

 def __init__(self):
 self.tracer = Tracer() # e.g., Jaeger

 def trace_request(self, request):
 """Create trace for full request lifecycle"""

 with self.tracer.start_span("handle_request") as span:
 span.set_tag("request_id", request.id)
 span.set_tag("user_id", request.user_id)

 # Validation
 with self.tracer.start_span("validate"):
 self.validate(request)

 # Routing
 with self.tracer.start_span("route"):
 agent = self.router.select(request)

 # Execution
 with self.tracer.start_span("execute"):
 result = agent.execute(request)

 # Monitoring
 with self.tracer.start_span("monitor"):
 self.monitor(result)

 return result

 def view_trace(self, trace_id):
 """Visualize full request trace"""

 # Show Gantt chart of operations
 trace = self.tracer.get_trace(trace_id)
 self.display_trace_chart(trace)

Metrics & Dashboards

Key Production Metrics

class ProductionMetrics:
 """Track production health"""

 def __init__(self):
 self.metrics = {
 # Throughput
 'requests_per_second': Counter(),
 'requests_total': Counter(),

 # Latency
 'latency_p50': Histogram(),
 'latency_p95': Histogram(),
 'latency_p99': Histogram(),

 # Quality
 'accuracy': Gauge(),
 'error_rate': Gauge(),

 # Cost
 'cost_per_request': Histogram(),
 'monthly_cost': Gauge(),

 # Safety
 'policy_violations': Counter(),
 'safety_issues': Counter()
 }

 def record_request(self, request, result):
 """Record metrics after request"""

 self.metrics['requests_per_second'].inc()
 self.metrics['latency_p95'].observe(result.latency)

 if result.success:
 self.metrics['accuracy'].set(
 self.calculate_accuracy(result)
)
 else:
 self.metrics['error_rate'].inc()

 self.metrics['cost_per_request'].observe(result.cost)

-

Alerting Strategies

Smart Alerting

class AlertingStrategy:
 """Only alert on real issues"""

 def __init__(self):
 self.alert_rules = [
 {
 'name': 'high_error_rate',
 'metric': 'error_rate',
 'threshold': 0.05,
 'duration': '5m',
 'severity': 'critical'
 },
 {
 'name': 'high_latency_p99',
 'metric': 'latency_p99',
 'threshold': 5000, # 5 seconds
 'duration': '10m',
 'severity': 'warning'
 },
 {
 'name': 'cost_spike',
 'metric': 'cost_per_request',
 'threshold': 1.0, # $1/request
 'duration': '1m',
 'severity': 'warning'
 },
 {
 'name': 'safety_violation',
 'metric': 'policy_violations',
 'threshold': 1,
 'duration': '0m', # Immediate
 'severity': 'critical'
 }
]

 def check_alerts(self):
 """Evaluate alert rules"""

 for rule in self.alert_rules:
 current_value = self.get_metric(
 rule['metric'],
 duration=rule['duration']
)

 if current_value > rule['threshold']:
 self.send_alert(
 title=rule['name'],
 severity=rule['severity'],
 metric=rule['metric'],
 value=current_value,
 threshold=rule['threshold']
)

Post-Incident Analysis

Incident Review Process

class PostIncidentAnalysis:
 """Learn from incidents"""

 def analyze_incident(self, incident_id):
 """Comprehensive incident analysis"""

 incident = self.incidents.get(incident_id)

 analysis = {
 'timeline': self.build_timeline(incident),
 'root_cause': self.identify_root_cause(incident),
 'contributing_factors': self.find_contributing_factors(incident),
 'impact': self.quantify_impact(incident),
 'recovery_actions': self.identify_recovery_actions(incident),
 'preventive_measures': self.suggest_preventions(incident)
 }

 # Generate report
 report = self.generate_report(analysis)

 # Share findings
 self.notify_team(report)
 self.add_to_knowledge_base(report)

 def build_timeline(self, incident):
 """Extract timeline from logs"""

 logs = self.structured_logger.filter(
 request_ids=incident.affected_requests
).order_by('timestamp')

 timeline = []
 for log in logs:
 timeline.append({
 'time': log.timestamp,
 'action': log.action,
 'status': log.status,
 'error': log.error
 })

 return timeline

-

3 Warnings

Warning 1: Too Much Logging

# WRONG
# Log everything at high verbosity
for step in agent.steps:
 logger.debug(f"Step: {step}")
 logger.debug(f"Input: {step.input}")
 logger.debug(f"Output: {step.output}")
 logger.debug(f"Tokens: {step.tokens}")
 # Logs grow to GBs/day

# RIGHT
# Log only important events
logger.info({
 'event': 'step_complete',
 'step_number': step.number,
 'status': step.status,
 'latency': step.latency
})
# Logs stay manageable

Warning 2: No Aggregation

# WRONG
# Store every log event individually
for event in events:
 store.save(event) # Millions of rows

# Hard to analyze later

# RIGHT
# Aggregate metrics
metrics.latency_histogram.observe(latency)
metrics.error_counter.inc()
metrics.cost_gauge.set(cost)

# Queryable metrics, not raw logs

Warning 3: Missing Context

# WRONG
logger.error(f"Request failed")
# No context! Which request? Why failed?

# RIGHT
logger.error(
 "Request failed",
 extra={
 'request_id': request.id,
 'user_id': request.user_id,
 'error': error,
 'stage': 'execution',
 'agent': agent.id
 }
)
# Full context for debugging

-

Last Updated: August 9, 2026