Logging
pino writes the files, pino-roll rotates them, and node-cron ships them to S3 every night.
Why pino
pino is Fastify's built-in logger, and the backend's own logs use it too. It writes newline-delimited JSON, one object per line, which any log tool can read later.
Fastify's logs record requests but not who made them, and Drizzle doesn't log changes at all. Bones adds both on top of pino, in backend/src/lib/logging/:
requests.loggets every HTTP request, and a line per tRPC call.db-changes.loggets every insert, update, and delete, through Drizzle's logger hook.@fastify/request-contextcarries the request id and user from the request down to the database layer, so no function has to pass them along.
Why pino-roll
pino-roll rotates each file daily or at 20 MB, whichever comes first, and keeps 30 files. It puts the date in every file name. That's what lets the archiver tell a closed file from an open one: anything dated before today is done.
Why node-cron
node-cron runs the archiver inside the backend process, at five past midnight by default. The archiver uploads each closed file to S3, then deletes it locally. A failed upload leaves the file for the next night.
It's the backend's only scheduled job. Set LOG_ARCHIVE_CRON to change the schedule, or leave S3_LOG_BUCKET unset to keep logs local.
Used in
- Logging — what gets logged, and what's redacted.
- Environment variables