Unstructured log messages ("User login failed") are hard to search and analyze at scale. Structured logging (consistent, machine-parseable formats) makes logs dramatically more useful for both humans and tooling.
Unstructured vs Structured Logging
# Unstructured
"User login failed for [email protected] from 203.0.113.10 at 2026-08-26 10:15:00"
# Structured (JSON)
{"timestamp": "2026-08-26T10:15:00Z", "level": "warn", "event": "login_failed", "user": "[email protected]", "ip": "203.0.113.10"}
The structured version is trivially searchable/filterable by any specific field (e.g. "show me all failed logins from this IP"), while the unstructured version requires fragile text-pattern matching.
Principle 1 — Use a Consistent Format (JSON Is the Common Standard)
Adopt JSON (or another structured format) consistently across your application — mixing structured and unstructured log lines undermines the benefit for whichever portion remains unstructured.
Principle 2 — Include Standard Fields on Every Log Line
{
"timestamp": "2026-08-26T10:15:00Z",
"level": "info",
"service": "api-server",
"environment": "production",
"message": "..."
}
Consistent baseline fields (timestamp, level, service, environment) make cross-service log correlation and filtering much simpler.
Principle 3 — Include Trace/Request IDs
{
"trace_id": "abc123",
"request_id": "req-456",
...
}
See How to Correlate Logs, Metrics, and Traces During an Incident — this single field connects a log line to its full distributed trace context.
Principle 4 — Use Appropriate Log Levels Consistently
| Level | Use For |
|---|---|
| DEBUG | Detailed diagnostic info, typically disabled in production |
| INFO | Normal operational events worth recording |
| WARN | Unexpected but non-fatal conditions |
| ERROR | Failures requiring attention |
Inconsistent level usage (everything logged as ERROR, or genuine errors logged as INFO) undermines the ability to filter effectively by severity.
Principle 5 — Never Log Sensitive Data
Passwords, full credit card numbers, API keys/tokens, and similar sensitive data should never appear in logs — audit logging code specifically for this, since accidental sensitive data logging is a surprisingly common and serious mistake.
Principle 6 — Use a Structured Logging Library
Most languages have mature structured logging libraries handling JSON formatting, log levels, and standard field injection automatically — use one rather than manually constructing JSON strings, which is error-prone and inconsistent.
Example: Structured Logging in Node.js
const pino = require('pino')();
pino.info({ event: 'user_login', userId: user.id, ip: req.ip }, 'User logged in successfully');
Principle 7 — Keep Messages Human-Readable Too
Structured fields make logs machine-parseable, but a clear message field still matters for humans quickly scanning logs — don't sacrifice readability entirely for structure.
Principle 8 — Log Errors with Full Context, Not Just the Error Message
logger.error({ err, orderId, userId }, 'Failed to process order');
Include relevant business context (order ID, user ID) alongside the technical error, dramatically speeding up investigation compared to a bare stack trace alone.
Common Errors
Logs technically structured but inconsistent field naming across services — establish and document a shared logging convention/schema across your team or organization to keep cross-service log analysis genuinely useful.
Continue Reading
- How to Correlate Logs, Metrics, and Traces During an Incident
- How to Set Up Centralized Logging with the ELK Stack (Elasticsearch, Logstash, Kibana)
- How to Set Up Centralized Logging with Grafana Loki (Lightweight Alternative)
Browse more articles in Advanced Observability & Incident Management.