CodeRigor

FREE LESSON

Structured logs with a schema

Parse a deterministic log stream into validated events without turning arbitrary text into evidence.

A log event is a claim made by a producer at a particular point in a program. Useful investigation starts by preserving what that claim actually contains. A sentence such as “checkout failed again” is readable, but its fields are unclear: is checkout a service, an operation or a customer? A structured event gives each role a documented name. Stable event names such as request.completed support queries; attributes carry values such as route or outcome.

This course investigates deterministic local fixtures. You will build parsers and evidence transformations using Python's standard library. You will not start a service, configure a logging backend, export telemetry or establish operational readiness. Native C++ microbenchmarks measure another population and environment; a short loop's runtime does not substitute for request observability.

An event earns a schema
start / 2
JSON decoding is followed by exact fields, severity and attribute validation. Invalid records stop with physical line context.
An event earns a schema
JSON decoding is followed by exact fields, severity and attribute validation. Invalid records stop with physical line context.

Our first schema has service, level, event and attributes. JSON syntax is only the first boundary. A JSON array is syntactically valid but is not an event object here. An object with an extra field is also rejected rather than silently becoming an undocumented schema version. Attributes use string values in this small fixture; later lessons deliberately use richer, separately contracted records.

Python
import json
row = json.loads('{"service":"checkout","level":"warn","event":"request.failed","attributes":{"route":"/pay"}}')
print(row["level"].upper(), row["event"], row["attributes"]["route"])

The severity vocabulary is INFO, WARN and ERROR. Normalize case because the contract permits it, then validate membership. Do not guess that an unknown value means INFO. Preserve service and event identifiers verbatim: undocumented trimming or renaming can merge distinct producers. Severity is the producer's classification, not evidence of the customer impact or root cause.

Normalize one documented field
start / 2
The parser uppercases severity while preserving service and event labels; attribute values are not automatically made safe.
Normalize one documented field
The parser uppercases severity while preserving service and event labels; attribute values are not automatically made safe.

A default JSON decoder silently keeps the last value for a repeated key. That is a poor evidence policy when two service fields disagree. The build uses object_pairs_hook to reject duplicates before dictionary construction, including duplicates inside attributes. It also rejects nonstandard constants instead of accepting NaN-like values accidentally. These checks address input interpretation; they cannot verify that the producer told the truth.

Process each physical line once and retain its original line number. Ignore whitespace-only lines, but count them toward error locations. If physical line three fails after an ignored blank, report line three. Raise a concise exception without embedding the entire record: a useful location should not copy unknown secrets into a second output channel. Fail the transformation instead of returning an apparently complete prefix.

In the exercises, first explain which boundary each invalid record violates. Then predict what the default JSON decoder does, diagnose severity and duplicate-key defects, and complete the parser. Test empty input, invalid shapes, mixed-case severity and nested duplicate keys. A successful parser gives later analysis well-defined records; it does not redact sensitive values or prove that missing events never happened.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Watch the introduction to logs/metrics/traces, then the 9:05 logs-plus-manual example. The build here validates offline records; it does not install or export OpenTelemetry.