Skip to content
Optimal Lab

DevOps · interactive

Structured logging

A customer says checkout failed. Find every error for user 42. With free-text logs, each developer wrote it differently, so the search misses. With structured logs, every event has the same named fields, and the question is a query.

$ grep "ERROR" app.log | grep "user 42"

  1. 2026-10-13 14:02:11 INFO checkout started for user 42 cart=3 items
  2. 2026-10-13 14:02:12 INFO payment ok user=17 amount 25.00
  3. 2026-10-13 14:02:12 ERROR Payment failed for customer #42: card declined (req a91f)
  4. 2026-10-13 14:02:13 WARN retrying payment, uid 42
  5. 2026-10-13 14:02:14 ERROR checkout: payment declined user_id: 42

0 matches (but there are two real errors for user 42)

The errors say “customer #42” and “user_id: 42”, not “user 42”. Free text forces you to guess every spelling, and you'll miss some.

Log format

Step 1 of 4

  1. 1

    Log events, not sentences

    A message plus key-value fields: level, service, user_id, request_id, error code.

    logger.error({ user_id, reason }, "payment failed")

  2. 2

    Pass a request id everywhere

    Generate it at the edge and include it in every log line and downstream call. One id finds the whole story.

    Trace ids from OpenTelemetry work too.

  3. 3

    Ship to a searchable store

    Loki, Elasticsearch, CloudWatch or Datadog index the fields, so filters and counts are instant.

    Dashboards and alerts on log fields too.

  4. 4

    Levels, sampling, no secrets

    Keep debug off in production, sample noisy logs, and never log passwords, tokens or full card numbers.

    Redact at the logger.

In production

Tools and technologies

Log platforms index structured fields.

Not tied to one language — use these names when you search docs or design a system.