Skip to content
sign in

==> read the decisions

ADR-0018: Logs are structured, and the format is shared

Status: Accepted (2026-10-14). The fleet side is nixos2 ADR-0017. Implemented 2026-10-15.

Every host's journal already reaches Loki on the hub, labelled by host, unit and syslog level. What the platform and user services write into it is free text: console.log lines, a different shape in each file, and nothing that ties the API's side of a request to the user service's side of it.

  • @cs/log in the shared library writes one JSON object per line to stdout, which systemd puts in the journal. Fields: ts, level, msg, service (cs-api, cs-web, cs-user-<user>), tenant, project, req; for a request also method, route, status, ms and door.
  • One request id, end to end. The API creates it, passes it to the user's service host in a header, and returns it in the response (x-cs-request). One id finds both halves of a request.
  • The platform logs every request once, from a single middleware; contexts log through the same library.
  • User code logs through ctx.log, already bound to tenant, project and request. A plain console.log in user code is wrapped by the service host into the same format, so nothing escapes unstructured, and the host logs every forwarded request itself, so a request is visible even if the user's code logs nothing.
  • Never logged: tokens, cookies, passwords, invite links, request or response bodies.
  • Grafana can show request rate, errors and latency per tenant and project, and a request's whole path by its id.
  • Which fields become Loki labels is the fleet's call (nixos2 ADR-0017): only fields with few values, never the request id.