Structured logs

Play this lesson

Two million lines a day

Shard Garden's match servers write logs like this:

Player Moss joined garden 7f3a (eu)

Priya, who runs observability, needs every line about garden 7f3a from yesterday. The servers wrote two million lines, in five different sentence styles. A search for "7f3a" also finds level names, chat and random IDs.

Sentences are made for people. At this size, Priya needs logs made for computers.

Structured logs

OpenTelemetry defines a log as a timestamped text record, structured (recommended) or unstructured.

{"timestamp":"2026-10-11T19:04:12.338Z", "level":"INFO", "service":"match-server", "event":"garden.join", "garden_id":"7f3a"}

Now "every garden.join in garden 7f3a" is an exact question.

Quick check

A log line written as valid JSON is always a structured log.

Show the answer

False

OpenTelemetry says structure comes from a stable schema, not from being JSON. If one server writes garden_id and another writes gardenId, a search for one misses the other.

When, where, who and what

OWASP, an open security project, says every log event must record when, where, who and what:

Bug hunt

garden-events.js
log.info('garden.join', {  garden_id: g.id });log.info('garden.leave', {  gardenId: g.id });log.info('garden.save', {  garden_id: g.id });

Priya searches for every event in garden 7f3a, and the leaves are missing. Which line is the bug?

  1. Line 2
  2. Line 4
  3. Line 6
Show the answer

Line 4

Line 4 writes gardenId while the others write garden_id. The schema must be the same everywhere, or searches quietly miss events.

Logs that know their trace

An OpenTelemetry log record has fields like Timestamp, SeverityText (the log level), Body, Attributes (extra details) and Resource (which service sent it), plus TraceId and SpanId.

When the OpenTelemetry SDK is running, it links each log to the active trace automatically. So from one "save failed" line, Priya can jump to the whole journey of that request across every service.

Players can write in your logs

Some fields come from players: level names, garden names, chat. A player could name a level Moss, then a line break, then ERROR admin deleted all gardens.

In a plain text log, that line break makes a fake second line. This is log injection. OWASP says to sanitise all event data, including carriage returns, line feeds and delimiter characters, and to encode it correctly for the log format. A structured logger stores the whole name as one value.

Quick check

A player names a level with a line break followed by a fake ERROR message. What stops it faking a log line?

  1. Logging the name as one encoded value inside a structured log
  2. Logging it at DEBUG level
  3. Writing the name in capital letters
Show the answer

Logging the name as one encoded value inside a structured log

Encoded as one field, the line break is part of the value, not the start of a new line. OWASP lists sanitising carriage returns, line feeds and delimiters for exactly this.

Not every line matters equally

Structured logs can be searched. The next questions: which events deserve a log at all, how loudly, and what must never be written down.

Bolt says: Next: log levels, and the things no log may ever hold.

Fact-checked 11 October 2026