Structured logging: make your logs searchable
Plain text logs are easy to write and painful to search. Structured JSON logs with levels and consistent fields turn finding one failed order into a single query.
A log line written for a human looks like this:
Order failed for user 4821 product 77: card declined
It reads well. It searches badly. To find every failed order for one user you need a regular expression, and it breaks the day someone rewords the message.
The same event as a structured log:
{
"level": "error",
"time": "2026-09-04T10:15:02Z",
"msg": "Order failed",
"userId": 4821,
"productId": 77,
"reason": "card_declined",
"requestId": "b7f3c2"
}
Now every value is a field. "All errors for user 4821 in the last hour" is a filter, not a text hunt.
Use a logging library
Do not build this with console.log and string concatenation. Every language has a library that does it properly.
| Language | Common choices |
|---|---|
| Node.js | Pino, Winston |
| Python | The standard logging module with a JSON formatter, structlog |
| Java | Logback or Log4j 2 with a JSON encoder |
| Go | The standard log/slog package |
In Node.js with Pino:
import pino from "pino";
const logger = pino({ level: process.env.LOG_LEVEL ?? "info" });
logger.error({ userId, productId, reason: "card_declined" }, "Order failed");
Use levels on purpose
| Level | Use it for |
|---|---|
debug |
Detail that helps during development. Off in production. |
info |
Normal events worth recording: order placed, job finished. |
warn |
Something unexpected that the system handled. |
error |
A failure that needs attention. |
If everything is an error, nothing is.
Fields worth adding to every line
- a timestamp in UTC,
- the service name and environment,
- a request or correlation ID, passed along to every service the request touches,
- the user or account ID, where your privacy rules allow it.
The request ID is the most valuable of these. It lets you pull every line that belongs to one request, across all services.
What never goes in a log
Passwords, access tokens, card numbers and personal data do not belong in logs. Most libraries can redact named fields automatically. Configure that once instead of trusting every developer to remember.
The payoff
A customer reports a failed order. With structured logs you filter by their user ID and level: error, see the reason and the request ID, and follow that ID through the payment service. Minutes, not an afternoon.
Logs are one of three signals. See how they work with metrics and traces in microservice observability.