Observability Beginner By Samson Tanimawo, PhD Published Dec 10, 2026 8 min read

Log Levels Beyond INFO/DEBUG: Structured Logging That Aids Triage

Most logs are unstructured strings nobody can query at 3am. Structured logging takes one quarter to roll out and pays back forever.

Why text logs fail under triage

Unstructured text logs work for human reading. They fail when the question is ‘show me all errors from tenant X in the last hour’, grep does not understand ‘errors’ or ‘tenant X.’ You end up greping for vague substrings and missing things.

Structured logs (JSON, with named fields) make the same query a one-liner. level=error AND tenant_id="X" AND ts > -1h. The investment is one-time; the dividend is every incident.

The five levels worth keeping

The structured fields that matter

Every log line should have: ts, level, msg, service, trace_id, span_id. Add tenant_id, user_id, req_id for the request-scoped context.

The trace_id field is the killer one, it lets you jump from log to trace in your observability tool with one click. Without it, the two are separate worlds.

The three-line setup

In Node.js: npm install pino; replace console.log with logger.info({ tenant_id, ... }, ‘message’). Three lines.

In Python: stdlib logging + python-json-logger formatter. Same idea; three lines.

In Go: slog from the standard library. Two lines.

Every modern language has a structured logger that is one import away. The investment is the change in writing style, not the library.

Antipatterns

What to do this week

Three moves. (1) Pick one service; switch to a structured logger this week. (2) Add trace_id propagation if you have tracing set up. (3) Update your runbook to use structured queries instead of grep.