Safeguard
Developer Tools•

Structured logging: the fields every log line should carry

Which fields belong on every structured log line: when, where, who, what, outcome and a correlation ID, with a starter schema you can adopt today.

Safeguard Security Team
3 min read

Every structured log line should answer four questions: when it happened, where in your system, who or what acted, and what the outcome was. Add one identifier that ties the line to its request. That small schema makes logs searchable, alertable and useful in an incident without extra parsing.

Why free text stops working

A line like "payment failed for order" is readable by a person and almost useless to a query. Once you have more than a few services, you want to ask questions such as "show every failed payment for this tenant in the last hour". That needs fields, not sentences.

Structured logging means each event is a set of named fields, usually JSON, written one event per line. The message stays short and human. Everything a query might filter on goes in its own field.

The starter schema

The OWASP logging guidance says each entry should answer "when, where, who and what", and may carry an interaction identifier to correlate related events. Turn that into fields:

  • timestamp: when, in UTC, in a standard format
  • service and version: where the code runs
  • environment: production, staging and so on
  • level: how serious the event is
  • event: a stable short name, such as payment_declined
  • actor: an internal user or service ID, not an email address
  • outcome: success or failure
  • request_id or trace_id: the correlation identifier
  • message: a short sentence for humans

Here is one line in that shape.

{"ts":"2026-10-19T09:14:03.221Z","service":"billing","version":"1.8.2","env":"prod","level":"warn","event":"payment_declined","actor":"u_48213","outcome":"failure","request_id":"req_7f3a","message":"card declined by issuer"}

Keep event names stable

The event field is the one you will alert on, so treat it like an API. Pick lowercase names with a category prefix, and do not rename them casually. If a dashboard counts payment_declined and someone ships payment_failed instead, the chart drops to zero and nobody is told.

OWASP publishes a logging vocabulary for security events that uses the same idea, with a category followed by an action. You can borrow its naming even if you do not adopt its field list.

Put variable data in fields, not the message

Do not build the message by pasting values into a sentence. Put the order ID in an order_id field. Two benefits follow: you can filter on it, and you can see at a glance which values you are about to write to disk, which helps when someone asks whether personal data ends up in logs.

Decide the schema once

Wrap your logger in a small shared library so every service emits the same keys. Teams that let each service pick its own field names end up with user, userId, uid and user_id in one search index. A shared wrapper also gives you one place to add redaction later.

Quick check

Should logs be JSON?

Usually yes for services that feed a log platform, because it is easy to parse. Local development can print a friendlier format as long as the fields are the same.

How many fields is too many?

Start with the nine above. Add a field only when you can name the query that needs it, and check whether it carries personal data first.

Sources

Related: what to log in a web app and what never to log.

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.