Structured logging
Logging JSON objects with named fields instead of sentences, so you can filter and group your logs instead of grepping them.
See it
What it is
Structured logging means each log line is a machine-parseable object (usually one JSON document per line) with named fields, instead of an English sentence you later attack with regex. 'User 42 checkout failed after 1200ms' becomes { level: 'error', msg: 'checkout failed', user_id: 42, duration_ms: 1200 }. Now 'show me every failed checkout over one second, grouped by user' is a query, not an afternoon.
Use a logger built for it: pino or winston in Node, slog in Go, structlog in Python, zap in the JVM-adjacent world. The strongest version is the canonical log line: one fat event per request carrying route, status, duration, user, tenant, and release, which answers most questions without joining anything. Bind a child logger per request so shared fields are attached automatically instead of by hand.
Gotcha: field names drift. If half the codebase writes userId and the other half writes user_id, your queries silently return half the truth, so pin down a small field schema early. Also, raw JSON is unreadable in a terminal, which is why teams abandon this on day two: run a pretty printer (pino-pretty) in development and keep the machine format for production only.
Ask AI for it
Replace every console.log in this codebase with a structured logger (pino). Emit one JSON object per line with the fields: time, level, msg, service, env, release, request_id, user_id, route, status, duration_ms. Add HTTP middleware that creates a child logger bound to request_id and user_id, and emits exactly one canonical log line per request on finish. Configure a redaction list for authorization headers, cookies, password, and email. Use pino-pretty in development and raw JSON to stdout in production. Never interpolate values into the msg string, put them in fields.