Good logs are logs a machine can read. This episode covers the principles of structured logging, what should and shouldn't be logged, the best logging libraries per language, and correlating logs with TraceID to complete the observability pillar.

Loki can store unstructured logs, but you'll struggle to extract meaning from them. Structured logging — writing logs as key-value pairs or JSON — is the practice that turns logs from raw text into data that can be queried, aggregated, and correlated.
This episode covers the principles of structured logging, what should and shouldn't be recorded, a comparison of logging libraries per language, and techniques for correlating logs with TraceID. The quality of the logs in episode 10 will be largely determined by the practices you learn now.
Structured logs always take the form of named fields. Each event becomes a single JSON object with a timestamp, level, and context:
{
"ts": "2026-08-10T11:20:05Z",
"level": "error",
"service": "checkout",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"order_id": "ord-1042",
"error": "payment gateway timeout",
"retry_count": 3
}Note the consistency of field names: ts, level, service, trace_id are used across all services so cross-service queries stay consistent.
Excessive levels in production inflate log volume and storage costs without added value.
A few categories of events worth recording:
import structlog
logger = structlog.get_logger()
def create_order(order_id):
logger.info("order.created", order_id=order_id, total=150000)The logger.info("order.created", ...) call produces JSON logs with fields that can be queried directly in Loki.
Never record:
{
"ts": "2026-08-10T11:20:05Z",
"level": "info",
"event": "login.success",
"password_hash": "jangan-pernah",
"card_number": "4111-1111-1111-1111"
}Fields like password_hash above must never appear in logs. The impact of such data leaks will be discussed further in episode 33.
zap and zerolog — both fast and support JSON output.logback and log4j2 with JSON pattern encoders.structlog and python-json-logger.winston and pino — pino is famously very lightweight.pip install structlogAfter pip install structlog, you can directly use the create_order example above.
The greatest value of structured logging appears when logs are correlated with traces. Every service should inject the TraceID into every log line:
gateway -> orders -> payment -> loki
trace_id="4bf92f..." unified across all servicesWith a consistent trace_id in all logs, you can trace the entire journey of a request with a single query in episode 18.
Tip
The golden rule: every log line should be able to answer the question "which request triggered it?". If not, add a trace_id or request_id — if one already exists, never remove it.
In episode 12 you understood the principles of structured logging with JSON and consistent field names, what is and isn't worth logging, the best libraries per programming language, and the importance of correlating logs with TraceID and Request ID.
The key takeaways:
In the next episode 13 we'll discuss distributed tracing with Tempo — an object-storage-based trace backend, its component architecture, span and sampling concepts, and a comparison with Jaeger. Now the third pillar of observability takes the stage.