Logging
Logging uses Pino with a request-scoped context. The LoggerInterceptor creates a requestId at the start of each HTTP request, stores it in AsyncLocalStorage, and the Pino logger mixin includes it in every log line emitted during that request.
How it works
Section titled “How it works”- The interceptor starts a timer and reads the client IP.
- It generates a
requestId(or accepts one from thex-request-idheader). - It runs the request inside an
AsyncLocalStoragecontext. - Pino’s
mixinreads the store and addsrequestIdto the log line. - On response, the interceptor logs the duration and status code.
This means a single request spans multiple log lines with the same ID, which is useful for tracing errors through guards, services, and repositories.
Using the logger
Section titled “Using the logger”LoggerServiceBuilder creates a NestJS-compatible logger backed by Pino:
const logger = app.get(LoggerServiceBuilder).build();app.useLogger(logger);Inside any provider, use NestJS’s standard Logger or inject the same builder. The requestId is picked up automatically if the call is within the request context.
Log format
Section titled “Log format”In development, pino-pretty prints readable logs:
[2025-...] INFO (12345): Request started from IP: ..., Path: /api/v1/authentication/login, Method: POST at ... requestId: "abc-123"[2025-...] INFO (12345): Response completed in 23ms 🔥 with Status:200 requestId: "abc-123"In production, Pino outputs JSON:
{ "level": 30, "time": 1234567890123, "pid": 12345, "hostname": "...", "requestId": "abc-123", "msg": "Response completed in 23ms 🔥 with Status:200"}Adding context
Section titled “Adding context”The LoggerStore interface currently only stores requestId. You can extend it with userId, tenantId, or other fields and update the interceptor to populate them. Anywhere you inject ASYNC_STORAGE, you can read the current store with als.getStore().
LogContext decorator
Section titled “LogContext decorator”The LogContext decorator wraps a method and auto-logs entry, duration, success, and errors. It is useful for background jobs or service methods where you want a consistent log envelope without manual log calls. The decorator expects the class to have a logger property.
Adding transports
Section titled “Adding transports”Pino supports transports for file output, log aggregation, or cloud providers. You can add a transport by changing the transport option in LoggerServiceBuilder. For production, the default configuration leaves transport undefined so Pino writes JSON to stdout, which is the best default for containerized environments.
Log level
Section titled “Log level”Set the LOG_LEVEL environment variable to change the minimum level. The default is info. Valid levels follow Pino’s defaults: fatal, error, warn, info, debug, trace, silent.