# Canonical Log Lines

> Emit one wide, structured log record per request, so each request can be queried as a single event.

Emit **one structured log record per request (or job, or loop) per service**, at the end of the request, instead of many log lines along the way.

- **Put everything on it:** user and tenant IDs, plan, feature flags, cart size, cache hits, the outcome, and the error type and message. High-cardinality fields are fine.
- **Put environment context on the OpenTelemetry resource,** such as `service.version`, `deployment.environment.name`, and the commit, not on each record.
- **Send it through the OpenTelemetry Logs API or a logging bridge** while the request's span is active, so the record carries its trace and span IDs.
- **Use one logger,** configured at startup and used everywhere. Two levels are enough: info and error.
- **Don't log unstructured strings,** and use the same field names across services.

Adapted from Stripe's [Canonical Log Lines](https://stripe.com/blog/canonical-log-lines) and Boris Tane's [logging-best-practices](https://github.com/boristane/agent-skills/tree/main/skills/logging-best-practices) (MIT).

## Next steps

- [Send OpenTelemetry Data](https://telemetrymachine.com/docs/send-opentelemetry) - Configure an OpenTelemetry exporter or Collector

---

All pages: https://telemetrymachine.com/docs/llms.txt
