---
title: Logging
description: How services write logs.
---

- Log through pino, never `console`. Take `logger` from `@openperf/server/logging` and make one child per component, named in kebab-case: `logger.child({ module: "ingest-drainer" })`. In `api`, use Nest's `Logger`, but never its `log()` level.
- Pass fields first and a static message second, with errors as `err`: `log.error({ table, err: error }, "Draining paused")`.
- A request writes one record when it ends. Add facts to it instead of logging again: `PinoLogger.assign()` in Nest apps, `assignLogContext()` in Bun apps. A failed request adds its error there as `err`.
- Only an app's `main.ts` configures logging. Library code takes no logger and no callback that only logs.
- Wrap pg-boss handlers in `handleJobs` from `@openperf/server/jobs`, so failures are logged once with their `queue` and `jobId`.
- In tests, assert logs with `recordLogs()` from `@openperf/server/logging/testing`.
