nestjs-pino

repository·master·Indexed 23 days ago

https://github.com/iamolegga/nestjs-pino

A platform-agnostic logger for NestJS based on Pino. It provides automatic request context binding using AsyncLocalStorage for structured JSON logging in microservices. The library supports the standard NestJS Logger interface, a specialized PinoLogger for direct API access, and a NativeLogger as a drop-in replacement for ConsoleLogger. Version 4.6.1.

Tokens
7.7K
Snippets
18
Records
45
Agent score
80%

What's inside nestjs-pino

  1. How nestjs-pino handles request context

    master

    The library uses pino-http and Node.js AsyncLocalStorage to manage request context.

    Every request is assigned its own child-logger. This allows Logger and PinoLogger services to retrieve the request-specific logger automatically when calling logging methods, enabling logs to be grouped by req.id without the performance overhead of NestJS REQUEST scope injection.

  2. Using useExisting with Fastify

    master

    If you are using the Fastify adapter and want to use the logger already configured on the Fastify instance, you can set useExisting: true in your configuration.

    Warning: This is generally not recommended because:

    1. Fastify creates a logger per request, but NestJS lifecycle events (like onModuleInit) run outside of a request context.
    2. If you use useExisting: true, the logger used during NestJS lifecycle events will use default parameters unless you also provide the same configuration to LoggerModule via forRoot/forRootAsync.

    It is better to provide the configuration directly to LoggerModule and omit useExisting to ensure consistency across both request-scoped and application-scoped logging.

  3. Quickstart: Set up nestjs-pino in NestJS

    master

    To use nestjs-pino, follow these three steps:

    1. Register the module: Import LoggerModule.forRoot() or LoggerModule.forRootAsync() exactly once in your root module (e.g., AppModule).
    2. Configure the app logger: In your main.ts, set up the Nest application to use the logger by using app.useLogger(app.get(Logger)). Ensure bufferLogs: true is set in NestFactory.create.
    3. Use the logger: You can use the standard NestJS Logger from @nestjs/common or the specialized PinoLogger from nestjs-pino.
    WARNING

    Register LoggerModule only via forRoot(...) / forRootAsync(...), and only once, in the root module. Because LoggerModule is @Global(), it is available everywhere. Re-importing it in feature modules will cause pino-http middleware to register twice, resulting in every request being logged twice.

    import { LoggerModule } from 'nestjs-pino';
    
    @Module({
      imports: [
        LoggerModule.forRoot(),
      ],
    })
    class AppModule {}
    
    // In main.ts
    import { Logger } from 'nestjs-pino';
    
    const app = await NestFactory.create(AppModule, { bufferLogs: true });
    app.useLogger(app.get(Logger));
  4. Use NativeLogger as a drop-in replacement for ConsoleLogger

    master

    If you are currently using NestJS ConsoleLogger with { json: true } and want to switch to Pino without changing your logging code, use NativeLogger. It produces identical JSON output (field names, argument handling, error formats) but adds Pino's performance and automatic request context binding.

    Key Differences:

    • Logger (pino-native): Treats extra arguments as pino interpolation values (e.g., log('foo %s', 'bar') -> {"msg":"foo bar"}).
    • NativeLogger (NestJS-native): Treats each argument as a separate log entry (e.g., log('foo', 'bar') -> two logs: {"message":"foo"} and {"message":"bar"}).
    import { NativeLogger, nativeLoggerOptions } from 'nestjs-pino';
    
    @Module({
      imports: [LoggerModule.forRoot({ pinoHttp: nativeLoggerOptions })],
    })
    class AppModule {}
    
    const app = await NestFactory.create(AppModule, { bufferLogs: true });
    app.useLogger(app.get(NativeLogger));
  5. Expose error details in logs using LoggerErrorInterceptor

    master

    By default, pino-http provides a generic err property. To ensure actual error details and stack traces are exposed in your logs, you must use the LoggerErrorInterceptor. Register it as a global interceptor in your application.

    import { LoggerErrorInterceptor } from 'nestjs-pino';
    
    const app = await NestFactory.create(AppModule);
    app.useGlobalInterceptors(new LoggerErrorInterceptor());
  6. Substitute the NestJS default logger with nestjs-pino

    master

    To use nestjs-pino as the primary logger for your entire NestJS application, you should register it via app.useLogger(). This allows your application code to remain implementation-agnostic by using the standard @nestjs/common Logger.

    When performing this substitution, it is recommended to set bufferLogs: true in your NestFactory.create call. This forces NestJS to buffer logs until the custom logger is ready, preventing loss of early startup logs.

    For standalone applications, you must manually call app.flushLogs() after the logger is ready to ensure buffered logs are emitted.

    // main.ts (Standard Application)
    import { Logger } from 'nestjs-pino';
    
    const app = await NestFactory.create(AppModule, { bufferLogs: true });
    app.useLogger(app.get(Logger));
    
    // main.ts (Standalone Application)
    import { Logger } from 'nestjs-pino';
    
    const app = await NestFactory.createApplicationContext(AppModule, { bufferLogs: true });
    app.useLogger(app.get(Logger));
    app.flushLogs();
  7. Mock the logger for testing

    master

    When testing classes that use @InjectPinoLogger, use the getLoggerToken(context) function to retrieve the correct injection token. This allows you to provide a mock implementation using standard NestJS provider techniques like useValue or useFactory.

      const module: TestingModule = await Test.createTestingModule({
        providers: [
          MyService,
          {
            provide: getLoggerToken(MyService.name),
            useValue: mockLogger,
          },
        ],
      }).compile();
  8. Configure LoggerModule with zero configuration

    master

    To use the default settings, simply import LoggerModule and call LoggerModule.forRoot() in your module imports.

    import { LoggerModule } from 'nestjs-pino';
    
    @Module({
      imports: [LoggerModule.forRoot()],
      ...
    })
    class MyModule {}
  9. Use the standard NestJS Logger in services

    master

    Once you have substituted the logger in main.ts using app.useLogger(app.get(Logger)), you should inject the standard Logger from @nestjs/common into your classes. This keeps your services decoupled from the specific logging implementation.

    Note: Do not inject Logger from nestjs-pino directly into class constructors. Instead, use the built-in NestJS Logger.

    // my-service.ts
    import { Logger } from '@nestjs/common';
    
    class MyService {
      private readonly logger = new Logger(MyService.name);
    }
  10. Configure pino-http options via pinoHttp

    master

    The library uses pino-http under the hood. You can control advanced logging behaviors by passing options to the pinoHttp field in your configuration.

    Common tasks include:

    • Disabling automatic request/response logs: Use the autoLogging field from pino-http options.
    • Custom Request IDs: Use the genReqId field from pino-http options to pass an X-Request-ID header or generate a UUID for the req.id field.
  11. Configure LoggerModule parameters

    master

    The LoggerModule.forRoot() and LoggerModule.forRootAsync() methods accept a Params object. Key configuration options include:

    • pinoHttp: Optional parameters for the underlying pino-http module. Can be pinoHttp.Options, a DestinationStream, or a tuple [pinoHttp.Options, DestinationStream].
    • forRoutes: Routing configuration compatible with NestJS MiddlewareConfigProxy['forRoutes']. Used to enable/disable automatic request/response logs or request context for specific routes.
    • exclude: Routing configuration compatible with NestJS MiddlewareConfigProxy['exclude']. Used to exclude specific routes from automatic logging or context attachment.
    • useExisting: A boolean. If true, skips pino configuration when using FastifyAdapter if the logger is already configured in the adapter.
    • renameContext: A string. Changes the property name used for the log context (defaults to context).