2HAASStart a project
All posts

Tech Tips

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.

The 2HAAS team2 min read

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.

Keep reading

  1. Tech Tips2 min read

    Speed up Docker builds with layer caching

    Slow Docker builds delay every pull request. Order your Dockerfile for the layer cache, shrink the build context and use BuildKit cache mounts to cut build time.

  2. Guides2 min read

    Microservice observability: logs, metrics and traces

    When a request crosses ten services, one log file cannot explain a slow checkout. Logs, metrics and traces each answer a different question. Here is how they fit.

  3. Tech Tips2 min read

    Keep API keys out of your frontend and your repo

    An API key in frontend code or a Git commit is a public key. Where secrets belong, how a backend proxy protects them, and what to do the moment one leaks.

Tell us what’s in the way.

A few lines is enough. You get a candid yes, a candid no, or a sharper question.

  • Working software in week four
  • A reply in one business day
  • Handover is the deliverable

Prefer email? [email protected]

The problem, who it is for, and what is in the way right now.

One business day to a real reply. No sales sequence, no call required.