Structured logging versus plain text logs
When to use structured logging and when plain text logs still make sense for developers and operations
A plain text log can be perfectly readable:
2026-05-05T14:30:45Z ERROR orders Database connection failed requestId=req_12345
The difficulty appears when software needs to answer questions such as:
Show ERROR events from orders
for request req_12345
during the last 15 minutes.
If service, level, and requestId are only fragments inside a
sentence, the log pipeline has to parse them reliably.
Structured logging records those values as fields instead.
{
"timestamp": "2026-05-05T14:30:45Z",
"level": "error",
"service": "orders",
"requestId": "req_12345",
"message": "Database connection failed",
"retry": true
}
In production, the important difference is whether downstream systems receive stable fields they can query.
Structured Logs Give Fields Stable Meaning
A structured log entry separates the human-readable message from machine-readable attributes.
The message can remain:
Database connection failed
while fields carry the details:
service = orders
requestId = req_12345
retry = true
A log backend can then filter, aggregate, alert, or correlate without extracting values from prose.
JSON is a common serialization format for structured logs, but structured logging is the underlying idea. A logging API can represent fields structurally even if a collector later exports them in another format.
Plain Text Works for Direct Human Reading
Plain text has real advantages for small programs.
A developer running a script locally may prefer:
Downloaded 42 files in 3.8s
over a larger JSON object.
There may be no centralized ingestion system, no dashboards, and no need to aggregate the event across hundreds of processes. In that setting, machine-queryable fields add little.
Plain text becomes awkward when downstream systems need to extract
identifiers consistently. Stable key=value fragments can help, but at
that point the application is already moving toward a structured
convention.
Structured Fields Improve Search and Correlation
Suppose an API request passes through several services.
If each service records a traceId or request identifier as a field, a
log query can retrieve related entries without depending on the wording
of each message.
traceId = abc123
↓
gateway
orders-api
payments-api
The same principle applies to error codes, deployment versions, regions, customer identifiers, and feature flags.
This is where structured logs fit naturally with metrics and distributed traces: the fields provide stable points for correlation.
Choose Structured Fields Deliberately
Structured entries are often larger than minimal text messages. High-volume fields can also increase index cost.
That makes field selection important.
Useful common fields might include:
timestamp
severity / level
service
message
traceId
spanId
requestId
error.type
Then add domain fields only when they help explain the event.
Avoid attaching full request bodies, raw headers, credentials, tokens, or large objects merely because the logger accepts an object. Structured sensitive data is still sensitive data.
Keep a Human Message Alongside the Fields
Structured logs still need a concise message that a person can scan quickly.
This:
{
"level": "info",
"orderId": "ord_42",
"status": "created",
"message": "Order created"
}
works for both consumers.
A person can scan Order created. A query can filter on orderId or
status.
The message should describe the event rather than repeat every field:
Good:
"Payment failed after all retries"
Less useful:
"Payment failed for order ord_42 using provider x after 3 retries in production"
The second version duplicates information that belongs in fields.
Standardize Field Names Before They Spread
Structured logging becomes frustrating when every service invents its own vocabulary:
requestId
request_id
request-id
reqId
Choose a small shared schema and document it.
OpenTelemetry’s log data model, for example, separates timestamps, severity, trace context, body, resource information, and attributes. Teams do not have to reproduce that model exactly, but an established convention can prevent avoidable naming drift.
Schema consistency also makes cross-service dashboards and alerts easier to maintain.
Migration Can Be Incremental
A service does not need to replace every log statement in one release.
Start with high-value operational events and preserve the existing message:
{
"level": "error",
"service": "payments",
"requestId": "req_42",
"message": "Payment failed"
}
Then standardize shared identifiers, add trace correlation where available, and move enrichment such as environment or deployment metadata into common logging middleware or the collection pipeline.
Measure ingestion volume after the change. Structured logging improves queryability, but unnecessary fields and duplicate events can still make the logging bill larger.
Choose Based on How the Logs Will Be Used
For a local utility whose logs are read directly by one developer, plain text may be the simpler choice.
For a production service whose logs feed search, alerts, dashboards, or distributed investigations, structured fields remove a large amount of parsing and naming ambiguity.
The choice follows how the logs will be consumed:
mainly direct human reading
→ plain text can be enough
machine search / aggregation / correlation
→ structured logging usually pays off
Either format still needs good event selection, sensible severity levels, redaction, and retention. Structure makes log data easier to operate on; it does not make poor logging decisions disappear.
More Articles Like This
Log Levels: When to Use Debug, Info, Warn, and Error
JSON logging best practices

Correlation ID vs Trace ID: What's the Difference?
What Is Distributed Tracing? A Practical Guide for Developers
What Is YAML? How YAML Configuration Files Work
