JSON logs with structlog or stdlib logging
A log line is either prose or data. Prose is written for humans reading a terminal. Data is written for machines, shipping tools, and your future self at 2am. For any service that crosses a network, pick data. JSON is the boring, universal default.
def sql_node(state: dict) -> dict:
print("[sql_node] Generating and executing SQL …")
result = run_sql_tool(
user_question=state["user_message"],
conversation_history=state["conversation_history"],
)
print(f"[sql_node] SQL: {result['sql']}")
if result["error"]:
print(f"[sql_node] ERROR: {result['error']}")
else:
print(f"[sql_node] {len(result['rows'])} rows returned.")
return {"sql_result": result}Four print calls per SQL turn. Readable on your laptop, useless in a log shipper.
import logging
import structlog
logging.basicConfig(format="%(message)s", level=logging.INFO)
structlog.configure(
processors=[
structlog.processors.add_log_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.JSONRenderer(),
],
wrapper_class=structlog.make_filtering_bound_logger(logging.INFO),
)
log = structlog.get_logger("agent")
# Usage
log.info("sql_node.start", route="sql", user_id=user_id)
log.info("sql_node.done", rows=len(rows), duration_ms=elapsed_ms)structlog wraps the stdlib logger and produces JSON with consistent keys. Every log line becomes grep-friendly and dashboard-ready.
{"event": "sql_node.start", "route": "sql", "user_id": "u_482", "level": "info", "timestamp": "2026-04-14T10:22:05.112Z"}
{"event": "sql_node.done", "rows": 12, "duration_ms": 84.3, "level": "info", "timestamp": "2026-04-14T10:22:05.197Z"}The same two events, now queryable. You can filter by event name, aggregate on duration_ms, or pipe the stream into any log backend.
For a service that is already making LLM calls measured in seconds, JSON encoding costs fractions of a millisecond per line. The cost is real only if you log inside a tight loop. In that case, sample or aggregate before logging. For LLM apps, default to JSON and move on.
Quiz: Quiz
Loading practice…