HOW-TO · RAG
How to Implement Logging for Agent Debugging
Target environment
Ubuntu 24.04 · Ollama 0.4.x
PREREQUISITES
Agent system running, logging library (structlog, loguru), Python 3.10+
What this does
Structured logging captures agent decisions, LLM inputs/outputs, tool calls, errors, and timing — providing a searchable record for debugging incorrect agent behavior.
Steps
- Set up structured logging with structlog.
import structlog
structlog.configure(
processors=[
structlog.stdlib.add_log_level,
structlog.dev.ConsoleRenderer()
],
context_class=dict,
)
log = structlog.get_logger()
- Log agent lifecycle events. Every step of the agent loop should produce a log entry.
def agent_step(task: str, turn: int) -> str:
log.info("agent.step.start", task=task, turn=turn)
log.info("agent.llm.call", prompt_length=len(task))
response = llm.invoke(task)
log.info("agent.llm.response", finish_reason=response.finish_reason, token_count=len(response.content))
if response.tool_calls:
for tc in response.tool_calls:
log.info("agent.tool.call",
tool=tc.function.name,
args=tc.function.arguments,
tool_call_id=tc.id
)
result = execute_tool(tc.function.name, json.loads(tc.function.arguments))
log.info("agent.tool.result", tool=tc.function.name, success="error" not in result)
log.info("agent.step.complete", turn=turn)
return response.content
- Add JSON logging for production. Switch to JSON renderer for log aggregation.
structlog.configure(
processors=[
structlog.stdlib.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer()
]
)
- Log with correlation IDs. Trace requests across multiple turns.
import uuid
def run_agent(user_input: str):
correlation_id = str(uuid.uuid4())
log = structlog.get_logger().bind(correlation_id=correlation_id)
log.info("agent.session.start", input=user_input)
result = agent_loop(user_input)
log.info("agent.session.end", result=result)
return result
- Configure exception logging. Capture full tracebacks for errors.
import traceback
try:
result = execute_tool(tool_name, args)
except Exception as e:
log.error("agent.tool.error",
tool=tool_name,
error=str(e),
traceback=traceback.format_exc()
)
- Set log levels per module. Reduce noise from third-party libraries.
import logging
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("openai").setLevel(logging.WARNING)
# Agent logs stay at DEBUG
Verification
python -c "
import structlog
log = structlog.get_logger()
log.info('test.message', key='value')
print('Logging works')
# Expected: 2026-05-29 ... test.message key=value OR Logging works (JSON output)
"
Common failures
- Sensitive data in logs. LLM inputs may contain PII. Add a processor that redacts emails, API keys, and personal names before logging.
- Log noise. Logging every token or every retry attempt produces gigabytes per hour. Use DEBUG level for verbose events and INFO for decisions.
- Async context loss. Coroutine logs may lose the correlation_id binding. Use
structlog.threadlocal.bind_threadlocal()or pass the logger explicitly. - Version mismatch - The installed package or runtime differs from the command shown; check the version first and rerun the smallest verification command.
- Local environment drift - Another service, virtual environment, model, or path is being used; print the active binary path and configuration before changing the guide steps.
Related guides
- How to Set Up Agent Observability and Tracing
- How to Debug Agent Reasoning and Tool Selection