Back to Blog

AI Agent Logging Best Practices: Complete Guide

Ultrion TeamJuly 18, 202612 min read

AI Agent Logging Best Practices: Complete Guide

How to implement logging that actually helps debugging and compliance.


Good logging is the difference between spending 5 minutes or 5 hours debugging a production issue. For AI agents, logging is even more critical β€” and more complex. This guide covers everything you need.


Why AI Agent Logging Is Different

Traditional logging: "Request received, processing, response sent."

AI agent logging needs: "Why did the agent choose that tool? What context was provided? What was the model's reasoning? How much did it cost? Was the output correct?"


What to Log

The Complete Logging Schema

interface AgentLog {
  // Identification
  conversationId: string;
  messageId: string;
  userId: string;
  sessionId: string;

  // Request
  timestamp: string;
  input: {
    userMessage: string;
    contextProvided: any;
    toolsAvailable: string[];
  };

  // Model
  model: {
    name: string;
    version: string;
    temperature: number;
    maxTokens: number;
  };

  // Processing
  processing: {
    durationMs: number;
    tokensUsed: {
      input: number;
      output: number;
      total: number;
    };
    cost: {
      input: number;
      output: number;
      total: number;
      currency: string;
    };
    toolCalls: Array<{
      tool: string;
      input: any;
      output: any;
      durationMs: number;
      success: boolean;
    }>;
    reasoningTrace?: string;
  };

  // Output
  output: {
    response: string;
    finishReason: string; // stop, length, tool_call
    safetyFlags: string[];
  };

  // Quality
  quality: {
    userFeedback?: "positive" | "negative";
    autoScore?: number;
    flagged?: boolean;
  };

  // Metadata
  metadata: {
    source: "api" | "web" | "integration";
    version: string;
    environment: "production" | "staging";
  };
}

Implementation

Structured Logger Setup

import structlog
import json

# Configure structlog
structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.processors.JSONRenderer(),
    ],
    wrapper_class=structlog.stdlib.BoundLogger,
    logger_factory=structlog.stdlib.LoggerFactory(),
)

logger = structlog.get_logger("ai_agent")

Logging the Full Request Lifecycle

class AgentLogger:
    def __init__(self):
        self.logger = structlog.get_logger("agent")

    async def log_request(self, conversation_id, user_message, agent_response):
        """Log a complete request-response cycle."""
        self.logger.info(
            "agent_request_completed",
            conversation_id=conversation_id,
            # Input
            input_message=user_message[:500],  # Truncate for log size
            input_length=len(user_message),
            # Output
            output_length=len(agent_response.text),
            output_preview=agent_response.text[:200],
            # Performance
            duration_ms=agent_response.duration_ms,
            # Cost
            tokens_input=agent_response.usage.input_tokens,
            tokens_output=agent_response.usage.output_tokens,
            cost_eur=agent_response.usage.cost,
            # Tools
            tools_called=[t.name for t in agent_response.tool_calls],
            tool_results=[{
                "tool": t.name,
                "success": t.success,
                "duration_ms": t.duration,
            } for t in agent_response.tool_calls],
            # Model
            model=agent_response.model,
            # Quality
            safety_flags=agent_response.safety_flags,
        )

    async def log_error(self, conversation_id, error, context):
        """Log errors with full context for debugging."""
        self.logger.error(
            "agent_request_failed",
            conversation_id=conversation_id,
            error_type=type(error).__name__,
            error_message=str(error),
            # Context for debugging
            user_message=context.get("user_message", "")[:200],
            model=context.get("model"),
            tools_available=context.get("tools", []),
            # Stack trace
            exc_info=True,
        )

Log Levels for AI Agents

Level When to Use Example
DEBUG Detailed tracing for development "Tool options considered: [search, calculate, translate]"
INFO Normal operations "Request processed in 2.3s, cost €0.003"
WARNING Potential issues "Token usage high: 4500/8000"
ERROR Failures requiring attention "LLM API timeout after 30s"
CRITICAL System-level failures "Budget exhausted, disabling agent"

Log Storage Strategy

Hot Tier (7-30 days)

  • Full logs with all details
  • Stored in Elasticsearch/Loki
  • Used for real-time debugging

Warm Tier (30-90 days)

  • Summarized logs (key fields only)
  • Stored in PostgreSQL
  • Used for trend analysis

Cold Tier (90+ days)

  • Aggregated metrics only
  • Stored in S3/Archive
  • Used for compliance audits
class LogRetention:
    SCHEDULE = {
        "full_logs": {"retention": "30d", "storage": "elasticsearch"},
        "summarized": {"retention": "90d", "storage": "postgresql"},
        "aggregated": {"retention": "2y", "storage": "s3"},
    }

    async def archive_old_logs(self):
        """Move logs through retention tiers."""
        # Move full logs to summarized after 30 days
        old_logs = await self.es.query(
            index="agent_logs",
            filter={"range": {"timestamp": {"lt": "now-30d"}}},
        )

        for log in old_logs:
            summary = self.summarize(log)
            await self.postgres.insert("log_summaries", summary)

        await self.es.delete(
            index="agent_logs",
            filter={"range": {"timestamp": {"lt": "now-30d"}}},
        )

Querying Logs for Debugging

Find Issues Quickly

class LogQuery:
    # Find slow requests
    async def find_slow_requests(self, threshold_ms=10000):
        return await self.logs.query("""
            SELECT conversation_id, duration_ms, model, tokens_output
            FROM agent_logs
            WHERE duration_ms > %s
            ORDER BY duration_ms DESC
            LIMIT 50
        """, threshold_ms)

    # Find expensive conversations
    async def find_expensive_conversations(self, min_cost=1.0):
        return await self.logs.query("""
            SELECT conversation_id,
                   SUM(cost_eur) as total_cost,
                   COUNT(*) as messages
            FROM agent_logs
            WHERE timestamp > NOW() - INTERVAL '24 hours'
            GROUP BY conversation_id
            HAVING SUM(cost_eur) > %s
            ORDER BY total_cost DESC
        """, min_cost)

    # Find failed tool calls
    async def find_tool_failures(self):
        return await self.logs.query("""
            SELECT * FROM agent_logs
            WHERE tool_results @> '[{"success": false}]'
            AND timestamp > NOW() - INTERVAL '24 hours'
        """)

    # Find quality issues
    async def find_negative_feedback(self):
        return await self.logs.query("""
            SELECT * FROM agent_logs
            WHERE user_feedback = 'negative'
            ORDER BY timestamp DESC
            LIMIT 20
        """)

Compliance Logging

GDPR Audit Trail

class GDPRLogger:
    async def log_data_processing(self, event):
        """Log for GDPR Article 22 (automated decision-making)."""
        await self.db.insert("gdpr_audit", {
            "timestamp": datetime.utcnow(),
            "user_id": event.user_id,
            "processing_type": "automated_decision",
            "decision": event.decision,
            "logic_involved": event.reasoning,
            "data_used": event.data_sources,
            "model": event.model,
            "human_review_available": True,
            "retention_until": datetime.utcnow() + timedelta(days=365),
        })

Common Logging Mistakes

  1. Logging too much β€” Don't log every token; log meaningful events
  2. Not logging enough β€” Missing context makes debugging impossible
  3. No correlation ID β€” Can't trace a request across services
  4. Logging PII β€” Never log passwords, API keys, or personal data
  5. No structure β€” Unstructured logs are unsearchable
  6. Synchronous logging β€” Blocks the event loop; use async
  7. No retention policy β€” Logs grow forever, costing money

Conclusion

Effective logging is the foundation of production AI agent operations. By implementing structured logging with the right fields, retention policies, and query capabilities, you can debug issues in minutes instead of hours.


Learn More

Explore logging tools on SkillExchange.

Newsletter

Enjoying this article?

Get weekly insights on building and selling AI skills, MCP tools, and creator economics. Join 2,000+ AI builders and creators.

No spam. Unsubscribe anytime.

Related Articles

Ready to try AI skills?

Browse the marketplace and discover skills for your AI agents.

Browse Skills