Logs
The API logs through Pino: readable lines on your machine, one JSON object per line in production.
What they look like
Section titled “What they look like”While you develop:
[10:02:14.454] INFO: [Bootstrap] API listening on port 3000, under /api[10:02:18.004] WARN: GET /api/me 401 {"responseTime":5}[10:03:27.096] WARN: [Auth] User not foundThe name in brackets is who logged it; a line without one is a request. In production:
{"level":"warn","time":1790517813195,"reqId":"3c15d9bd-…","req":{"method":"GET","url":"/api/me"},"res":{"statusCode":401},"responseTime":4,"msg":"GET /api/me 401"}Filter on level error and fatal for failures, on reqId to follow one request, on context for
one part of the API.
Log from your code
Section titled “Log from your code”Use NestJS’s Logger; it goes through Pino:
private readonly logger = new Logger(ReportsService.name);
this.logger.log(`Report sent to workspace ${workspaceId}`);Never log a secret, token, password, cookie or email body. Don’t log an error and rethrow it.
Don’t use console.log in the API: it bypasses the logger and isn’t JSON in production.
How much is logged
Section titled “How much is logged”Set LOG_LEVEL. Left empty it is debug while you develop, info in production and
silent in tests.
Finding a customer’s request
Section titled “Finding a customer’s request”Every response carries an x-request-id header, and the same id is on the request’s log line
and on what your code logs while handling it. Ask the customer for it. An id from a proxy in
front of the API is kept.
Better Auth’s lines (marked [Auth]) carry no id: find them by time.
With traces on, lines written during a request or a job also carry
trace_id and span_id in production.
With error reporting on, the report of a failed request carries the same id.
What is left out
Section titled “What is left out”Request lines have no headers and no query string, on purpose: those hold session cookies and
sign-in tokens. Keep it that way if you change createHttpLogger in
apps/api/src/logging/logger.ts; its test checks that secrets stay out.
/api/health is not logged, since hosts ask for it every few seconds.
When the AI assistant fails, the reason is printed by the AI SDK itself, outside Pino, so in production that line is not JSON.