GithubHelp home page GithubHelp logo

iamolegga / nestjs-pino Goto Github PK

View Code? Open in Web Editor NEW
1.2K 5.0 97.0 5.81 MB

Platform agnostic logger for NestJS based on Pino with REQUEST CONTEXT IN EVERY LOG

License: MIT License

TypeScript 94.60% JavaScript 5.40%
nestjs nest pino logging logger hacktoberfest

nestjs-pino's Introduction

War in Ukraine
Children wait in a bomb shelter in Mariupol, Ukraine. AP
Help save their lives by donating:
Armed Forces of UkraineUkrainian volunteers
Thanks for your support!

NestJS-Pino

npm npm GitHub branch checks state Known Vulnerabilities Libraries.io Dependabot Supported platforms: Express & Fastify

✨✨✨ Platform agnostic logger for NestJS based on Pino with REQUEST CONTEXT IN EVERY LOG ✨✨✨


This is documentation for v2+ which works with NestJS 8+.
Please see documentation for the previous major version which works with NestJS < 8 here.


Install

npm i nestjs-pino pino-http

Example

Firstly, import module with LoggerModule.forRoot(...) or LoggerModule.forRootAsync(...) only once in root module (check out module configuration docs below):

import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [LoggerModule.forRoot()],
})
class AppModule {}

Secondly, set up app logger:

import { Logger } from 'nestjs-pino';

const app = await NestFactory.create(AppModule, { bufferLogs: true });
app.useLogger(app.get(Logger));

Now you can use one of two loggers:

// NestJS standard built-in logger.
// Logs will be produced by pino internally
import { Logger } from '@nestjs/common';

export class MyService {
  private readonly logger = new Logger(MyService.name);
  foo() {
    // All logger methods have args format the same as pino, but pino methods
    // `trace` and `info` are mapped to `verbose` and `log` to satisfy
    // `LoggerService` interface of NestJS:
    this.logger.verbose({ foo: 'bar' }, 'baz %s', 'qux');
    this.logger.debug('foo %s %o', 'bar', { baz: 'qux' });
    this.logger.log('foo');
  }
}

Usage of the standard logger is recommended and idiomatic for NestJS. But there is one more option to use:

import { PinoLogger, InjectPinoLogger } from 'nestjs-pino';

export class MyService {
  constructor(
    private readonly logger: PinoLogger
  ) {
    // Optionally you can set context for logger in constructor or ...
    this.logger.setContext(MyService.name);
  }

  constructor(
    // ... set context via special decorator
    @InjectPinoLogger(MyService.name)
    private readonly logger: PinoLogger
  ) {}

  foo() {
    // PinoLogger has same methods as pino instance
    this.logger.trace({ foo: 'bar' }, 'baz %s', 'qux');
    this.logger.debug('foo %s %o', 'bar', { baz: 'qux' });
    this.logger.info('foo');
  }
}

Output:

// Logs by app itself
{"level":30,"time":1629823318326,"pid":14727,"hostname":"my-host","context":"NestFactory","msg":"Starting Nest application..."}
{"level":30,"time":1629823318326,"pid":14727,"hostname":"my-host","context":"InstanceLoader","msg":"LoggerModule dependencies initialized"}
{"level":30,"time":1629823318327,"pid":14727,"hostname":"my-host","context":"InstanceLoader","msg":"AppModule dependencies initialized"}
{"level":30,"time":1629823318327,"pid":14727,"hostname":"my-host","context":"RoutesResolver","msg":"AppController {/}:"}
{"level":30,"time":1629823318327,"pid":14727,"hostname":"my-host","context":"RouterExplorer","msg":"Mapped {/, GET} route"}
{"level":30,"time":1629823318327,"pid":14727,"hostname":"my-host","context":"NestApplication","msg":"Nest application successfully started"}

// Logs by injected Logger and PinoLogger in Services/Controllers. Every log
// has it's request data and unique `req.id` (by default id is unique per
// process, but you can set function to generate it from request context and
// for example pass here incoming `X-Request-ID` header or generate UUID)
{"level":10,"time":1629823792023,"pid":15067,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"host":"localhost:3000","user-agent":"curl/7.64.1","accept":"*/*"},"remoteAddress":"::1","remotePort":63822},"context":"MyService","foo":"bar","msg":"baz qux"}
{"level":20,"time":1629823792023,"pid":15067,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"host":"localhost:3000","user-agent":"curl/7.64.1","accept":"*/*"},"remoteAddress":"::1","remotePort":63822},"context":"MyService","msg":"foo bar {\"baz\":\"qux\"}"}
{"level":30,"time":1629823792023,"pid":15067,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"host":"localhost:3000","user-agent":"curl/7.64.1","accept":"*/*"},"remoteAddress":"::1","remotePort":63822},"context":"MyService","msg":"foo"}

// Automatic logs of every request/response
{"level":30,"time":1629823792029,"pid":15067,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"host":"localhost:3000","user-agent":"curl/7.64.1","accept":"*/*"},"remoteAddress":"::1","remotePort":63822},"res":{"statusCode":200,"headers":{"x-powered-by":"Express","content-type":"text/html; charset=utf-8","content-length":"12","etag":"W/\"c-Lve95gjOVATpfV8EL5X4nxwjKHE\""}},"responseTime":7,"msg":"request completed"}

Comparison with others

There are other Nestjs loggers. Key purposes of this module are:

  • to be idiomatic NestJS logger
  • to log in JSON format (thanks to pino - super fast logger) why you should use JSON
  • to log every request/response automatically (thanks to pino-http)
  • to bind request data to the logs automatically from any service on any application layer without passing request context (thanks to AsyncLocalStorage)
  • to have another alternative logger with same API as pino instance (PinoLogger) for experienced pino users to make more comfortable usage.
Logger Nest App logger Logger service Auto-bind request data to logs
nest-winston + + -
nestjs-pino-logger + + -
nestjs-pino + + +

Configuration

Zero configuration

Just import LoggerModule to your module:

import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [LoggerModule.forRoot()],
  ...
})
class MyModule {}

Configuration params

The following interface is using for the configuration:

interface Params {
  /**
   * Optional parameters for `pino-http` module
   * @see https://github.com/pinojs/pino-http#api
   */
  pinoHttp?:
    | pinoHttp.Options
    | DestinationStream
    | [pinoHttp.Options, DestinationStream];

  /**
   * Optional parameter for routing. It should implement interface of
   * parameters of NestJS built-in `MiddlewareConfigProxy['forRoutes']`.
   * @see https://docs.nestjs.com/middleware#applying-middleware
   * It can be used for both disabling automatic req/res logs (see above) and
   * removing request context from following logs. It works for all requests by
   * default. If you only need to turn off the automatic request/response
   * logging for some specific (or all) routes but keep request context for app
   * logs use `pinoHttp.autoLogging` field.
   */
  forRoutes?: Parameters<MiddlewareConfigProxy['forRoutes']>;

  /**
   * Optional parameter for routing. It should implement interface of
   * parameters of NestJS built-in `MiddlewareConfigProxy['exclude']`.
   * @see https://docs.nestjs.com/middleware#applying-middleware
   * It can be used for both disabling automatic req/res logs (see above) and
   * removing request context from following logs. It works for all requests by
   * default. If you only need to turn off the automatic request/response
   * logging for some specific (or all) routes but keep request context for app
   * logs use `pinoHttp.autoLogging` field.
   */
  exclude?: Parameters<MiddlewareConfigProxy['exclude']>;

  /**
   * Optional parameter to skip pino configuration in case you are using
   * FastifyAdapter, and already configure logger in adapter's config. The Pros
   * and cons of this approach are described in the FAQ section of the
   * documentation:
   * @see https://github.com/iamolegga/nestjs-pino#faq.
   */
  useExisting?: true;

  /**
   * Optional parameter to change property name `context` in resulted logs,
   * so logs will be like:
   * {"level":30, ... "RENAME_CONTEXT_VALUE_HERE":"AppController" }
   */
  renameContext?: string;
}

Synchronous configuration

Use LoggerModule.forRoot method with argument of Params interface:

import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [
    LoggerModule.forRoot({
      pinoHttp: [
        {
          name: 'add some name to every JSON line',
          level: process.env.NODE_ENV !== 'production' ? 'debug' : 'info',
          // install 'pino-pretty' package in order to use the following option
          transport: process.env.NODE_ENV !== 'production'
            ? { target: 'pino-pretty' }
            : undefined,


          // and all the other fields of:
          // - https://github.com/pinojs/pino-http#api
          // - https://github.com/pinojs/pino/blob/HEAD/docs/api.md#options-object


        },
        someWritableStream
      ],
      forRoutes: [MyController],
      exclude: [{ method: RequestMethod.ALL, path: 'check' }]
    })
  ],
  ...
})
class MyModule {}

Asynchronous configuration

With LoggerModule.forRootAsync you can, for example, import your ConfigModule and inject ConfigService to use it in useFactory method.

useFactory should return object with Params interface or undefined

Here's an example:

import { LoggerModule } from 'nestjs-pino';

@Injectable()
class ConfigService {
  public readonly level = 'debug';
}

@Module({
  providers: [ConfigService],
  exports: [ConfigService]
})
class ConfigModule {}

@Module({
  imports: [
    LoggerModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (config: ConfigService) => {
        await somePromise();
        return {
          pinoHttp: { level: config.level },
        };
      }
    })
  ],
  ...
})
class TestModule {}

Asynchronous logging

In essence, asynchronous logging enables even faster performance by pino.

Please, read pino asynchronous mode docs first. There is a possibility of the most recently buffered log messages being lost in case of a system failure, e.g. a power cut.

If you know what you're doing, you can enable it like so:

import pino from 'pino';
import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [
    LoggerModule.forRoot({
      pinoHttp: {
        stream: pino.destination({
          dest: './my-file', // omit for stdout
          minLength: 4096, // Buffer before writing
          sync: false, // Asynchronous logging
        }),
      },
    }),
  ],
  ...
})
class MyModule {}

See pino.destination

Testing a class that uses @InjectPinoLogger

This package exposes a getLoggerToken() function that returns a prepared injection token based on the provided context. Using this token, you can provide a mock implementation of the logger using any of the standard custom provider techniques, including useClass, useValue and useFactory.

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

Logger/PinoLogger class extension

Logger and PinoLogger classes can be extended.

// logger.service.ts
import { Logger, PinoLogger, Params, PARAMS_PROVIDER_TOKEN } from 'nestjs-pino';

@Injectable()
class LoggerService extends Logger {
  constructor(
    logger: PinoLogger,
    @Inject(PARAMS_PROVIDER_TOKEN) params: Params
  ) {
    ...
  }
  // extended method
  myMethod(): any {}
}

import { PinoLogger, Params, PARAMS_PROVIDER_TOKEN } from 'nestjs-pino';

@Injectable()
class LoggerService extends PinoLogger {
  constructor(
    @Inject(PARAMS_PROVIDER_TOKEN) params: Params
  ) {
    // ...
  }
  // extended method
  myMethod(): any {}
}


// logger.module.ts
@Module({
  providers: [LoggerService],
  exports: [LoggerService],
  imports: [LoggerModule.forRoot()],
})
class LoggerModule {}

Notes on Logger injection in constructor

Since logger substitution has appeared in NestJS@8 the main purpose of Logger class is to be registered via app.useLogger(app.get(Logger)). But that requires some internal breaking change, because with such usage NestJS pass logger's context as the last optional argument in logging function. So in current version Logger's methods accept context as a last argument.

With such change it's not possible to detect if method was called by app internaly and the last argument is context or Logger was injected in some service via constructor(private logger: Logger) {} and the last argument is interpolation value for example.

Assign extra fields for future calls

You can enrich logs before calling log methods. It's possible by using assign method of PinoLogger instance. As Logger class is used only for NestJS built-in Logger substitution via app.useLogger(...) this feature is only limited to PinoLogger class. Example:

@Controller('/')
class TestController {
  constructor(
    private readonly logger: PinoLogger,
    private readonly service: MyService,
  ) {}

  @Get()
  get() {
    // assign extra fields in one place...
    this.logger.assign({ userID: '42' });
    return this.service.test();
  }
}

@Injectable()
class MyService {
  private readonly logger = new Logger(MyService.name);

  test() {
    // ...and it will be logged in another one
    this.logger.log('hello world');
  }
}

Due to the limitation of the underlying pino-http PinoLogger.assign cannot extend Request completed logs.

Change pino params at runtime

Pino root instance with passed via module registration params creates a separate child logger for every request. This root logger params can be changed at runtime via PinoLogger.root property which is the pointer to logger instance. Example:

@Controller('/')
class TestController {
  @Post('/change-loggin-level')
  setLevel() {
    PinoLogger.root.level = 'info';
    return null;
  }
}

Expose stack trace and error class in err property

By default, pino-http exposes err property with a stack trace and error details, however, this err property contains default error details, which do not tell anything about actual error. To expose actual error details you need you to use a NestJS interceptor which captures exceptions and assigns them to the response object err property which is later processed by pino-http:

import { LoggerErrorInterceptor } from 'nestjs-pino';

const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggerErrorInterceptor());

Migration

v1

  • All parameters of v.0 are moved to pinoHttp property (except useExisting).
  • useExisting now accept only true because you should already know if you want to use preconfigured fastify adapter's logger (and set true) or not (and just not define this field).

v2

Logger substitution

A new more convenient way to inject a custom logger that implements LoggerService has appeared in recent versions of NestJS (mind the bufferLogs field, it will force NestJS to wait for logger to be ready instead of using built-in logger on start):

// main.ts
import { Logger } from 'nestjs-pino';
// ...
  const app = await NestFactory.create(AppModule, { bufferLogs: true });
  app.useLogger(app.get(Logger));
// ...

Note that for standalone applications, buffering has to be flushed using app.flushLogs() manually after custom logger is ready to be used by NestJS (refer to this issue for more details):

// main.ts
import { Logger } from 'nestjs-pino';

// ... 
  const app = await NestFactory.createApplicationContext(AppModule, { bufferLogs: true });
  app.useLogger(app.get(Logger));
  app.flushLogs();
// ...

In all the other places you can use built-in Logger:

// my-service.ts
import { Logger } from '@nestjs/common';
class MyService {
  private readonly logger = new Logger(MyService.name);
}

To quote the official docs:

If we supply a custom logger via app.useLogger(), it will actually be used by Nest internally. That means that our code remains implementation agnostic, while we can easily substitute the default logger for our custom one by calling app.useLogger().

That way if we follow the steps from the previous section and call app.useLogger(app.get(MyLogger)), the following calls to this.logger.log() from MyService would result in calls to method log from MyLogger instance.


This is recommended to update all your existing Logger injections from nestjs-pino to @nestjs/common. And inject it only in your main.ts file as shown above. Support of injection of Logger (don't confuse with PinoLogger) from nestjs-pino directly in class constructors is dropped.


Since logger substitution has appeared the main purpose of Logger class is to be registered via app.useLogger(app.get(Logger)). But that requires some internal breaking change, because with such usage NestJS pass logger's context as the last optional argument in logging function. So in current version Logger's methods accept context as the last argument.

With such change it's not possible to detect if method was called by app internaly and the last argument is context or Logger was injected in some service via constructor(private logger: Logger) {} and the last argument is interpolation value for example. That's why logging with such injected class still works, but only for 1 argument.

NestJS LoggerService interface breaking change

In NestJS@8 all logging methods of built-in LoggerService now accept the same arguments without second context argument (which is set via injection, see above), for example: log(message: any, ...optionalParams: any[]): any;. That makes usage of built-in logger more convenient and compatible with pino's logging methods. So this is a breaking change in NestJS, and you should be aware of it.

In NestJS <= 7 and nestjs-pino@1 when you call this.logger.log('foo', 'bar'); there would be such log: {..."context":"bar","msg":"foo"} (second argument goes to context field by desing). In NestJS 8 and nestjs-pino@2 (with proper injection that shown above) same call will result in {..."context":"MyService","msg":"foo"}, so context is passed via injection, but second argument disappear from log, because now it treats as interpolation value and there should be placeholder for it in message argument. So if you want to get both foo and bar in log the right way to do this is: this.logger.log('foo %s', 'bar');. More info can be found in pino docs.

FAQ

Q: How to disable automatic request/response logs?

A: check out autoLogging field of pino-http that are set in pinoHttp field of Params


Q: How to pass X-Request-ID header or generate UUID for req.id field of log?

A: check out genReqId field of pino-http that are set in pinoHttp field of Params


Q: How does it work?

A: It uses pino-http under hood, so every request has it's own child-logger, and with help of AsyncLocalStorage Logger and PinoLogger can get it while calling own methods. So your logs can be grouped by req.id.


Q: Why use AsyncLocalStorage instead of REQUEST scope?

A: REQUEST scope can have perfomance issues. TL;DR: it will have to create an instance of the class (that injects Logger) on each request, and that will slow down your response times.


Q: I'm using old nodejs version, will it work for me?

A: Please check out history of this feature.


Q: What about pino built-in methods/levels?

A: Pino built-in methods names are not fully compatible with NestJS built-in LoggerService methods names, and there is an option which logger you use. Here is methods mapping:

pino method PinoLogger method NestJS built-in Logger method
trace trace verbose
debug debug debug
info info log
warn warn warn
error error error
fatal fatal fatal (since [email protected])

Q: Fastify already includes pino, and I want to configure it on Adapter level, and use this config for logger

A: You can do it by providing useExisting: true. But there is one caveat:

Fastify creates logger with your config per every request. And this logger is used by Logger/PinoLogger services inside that context underhood.

But Nest Application has another contexts of execution, for example lifecycle events, where you still may want to use logger. For that Logger/PinoLogger services use separate pino instance with config, that provided via forRoot/forRootAsync methods.

So, when you want to configure pino via FastifyAdapter there is no way to get back this config from fastify and pass it to that out of context logger.

And if you will not pass config via forRoot/forRootAsync out of context logger will be instantiated with default params. So if you want to configure it with the same options for consistency you have to provide the same config to LoggerModule configuration too. But if you already provide it to LoggerModule configuration you can drop useExisting field from config and drop logger configuration on FastifyAdapter, and it will work without code duplication.

So this property (useExisting: true) is not recommended, and can be useful only for cases when:

  • this logger is not using for lifecycle events and application level logging in NestJS apps based on fastify
  • pino is using with default params in NestJS apps based on fastify

All the other cases are lead to either code duplication or unexpected behavior.


Do you use this library?
Don't be shy to give it a star! ★

Also if you are into NestJS you might be interested in one of my other NestJS libs.

nestjs-pino's People

Contributors

0xslipk avatar actions-user avatar antspk avatar bmeverett avatar dependabot-preview[bot] avatar dependabot[bot] avatar dougludlow avatar exsesx avatar github-actions[bot] avatar gyanendrokh avatar hotsezus avatar iamolegga avatar katafractari avatar mentos1386 avatar mergify[bot] avatar mksmtn avatar rstreefland avatar songkeys avatar tomups avatar trymoto avatar ttoomm318 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

nestjs-pino's Issues

[QUESTION] Fetch request ID

Im using below code to generate request ID:-

shared.module.ts

import {
  Module,
  HttpModule
} from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { APP_FILTER } from '@nestjs/core';
import { LoggerModule } from 'nestjs-pino';
import * as config from 'config';

import { CorrelationIdGeneratorService } from './services/helpers/generators/correlation-id-generator.service';


@Module({
  imports: [
    MongooseModule.forFeature([{ name: 'Device', schema: DeviceSchema }]),
    MongooseModule.forFeature([{ name: 'File', schema: FileSchema }]),
    HttpModule,
    LoggerModule.forRootAsync({
      providers: [
        CorrelationIdGeneratorService,
      ],
      inject: [CorrelationIdGeneratorService],
      useFactory: (correlationIdGeneratorService: CorrelationIdGeneratorService) => {
        return {
          pinoHttp: {
            genReqId: (request) => {
              correlationIdGeneratorService.generateCorrelationId(request);
              console.log('Im here 1',correlationIdGeneratorService.getCorrelationId());
              return correlationIdGeneratorService.getCorrelationId();
            },
            level: config.get('log.LEVEL'),
            useLevelLabels: true,
            base: null,
            timestamp: () => {
              return `, "time":"${new Date().toISOString()}"`;
            },
          },
        };
      }
    })
  ],
  providers: [
    CorrelationIdGeneratorService,
  ],
  exports: [
    CorrelationIdGeneratorService,
  ]
})
export class SharedModule { }

Now the issue is, i need to fetch the correlationId in another module for which i have exported the CorrelationIdGeneratorService from shared.module.ts and imported in my consuming modules.

Now in the consuming module im getting correlationId as undefined.

It seems that the object of CorrelationIdGeneratorService passed in loggermodule is different from the object passed in my consuming module. Please help, how can i share the singleton instance of correlation id throughout my code.

Below is the code for CorrelationIdGeneratorService
correlation-id-generator.service.ts

import { Injectable } from '@nestjs/common';
import * as config from 'config';

import { UniqueIdGeneratorService } from './unique-id-generator.service';

@Injectable()
export class CorrelationIdGeneratorService {
    private correlationId: string;
    constructor(private readonly uniqueIdGeneratorService: UniqueIdGeneratorService) {}

    public generateCorrelationId(request: any) {
        this.correlationId = request?.headers?.[config.get('httpHeaders.CORRELATION_ID')] ||
        this.uniqueIdGeneratorService.generate(
            config.get('correlationId.VALID_CHARS'),
            config.get('correlationId.LENGTH')
        );
        console.log('Ammy 2', this.correlationId)
    }

    public getCorrelationId(): string {
        return this.correlationId;
    }
}

[BUG]

What is the current behavior?

TS2322: Type '(configService: ConfigurationService) => Promise<{ useExisting: false; }>' is not assignable to type '(...args: any[]) => Params | Promise<Params>'.
  Type 'Promise<{ useExisting: false; }>' is not assignable to type 'Params | Promise<Params>'.
    Type 'Promise<{ useExisting: false; }>' is not assignable to type 'Promise<Params>'.
      Type '{ useExisting: false; }' is not assignable to type 'Params'.
        Types of property 'useExisting' are incompatible.
          Type 'false' is not assignable to type 'true'.

14       useFactory: async (configService: ConfigurationService) => ({

Please provide the steps to reproduce.

LoggerModule.forRootAsync({
      useFactory: async (configService: ConfigurationService) => ({
        useExisting: false,
      }),
      inject: [ConfigurationService],
    }),

What is the expected behavior?
Should be able to set to false

Please mention other relevant information such as Node.js version and Operating System.
in params.ts

export interface Params {
    pinoHttp?: pinoHttp.Options | DestinationStream | [pinoHttp.Options, DestinationStream];
    exclude?: Parameters<MiddlewareConfigProxy["exclude"]>;
    forRoutes?: Parameters<MiddlewareConfigProxy["forRoutes"]>;
    useExisting?: true; 
    renameContext?: string;
}

should be?

export interface Params {
    pinoHttp?: pinoHttp.Options | DestinationStream | [pinoHttp.Options, DestinationStream];
    exclude?: Parameters<MiddlewareConfigProxy["exclude"]>;
    forRoutes?: Parameters<MiddlewareConfigProxy["forRoutes"]>;
    useExisting?: boolean;
    renameContext?: string;
}

[BUG] Basic configuration with Usage as NestJS app logger doesn't work

What is the current behavior?

Basic configuration as described by Usage as NestJS app logger doesn't work.

Please provide the steps to reproduce.

On a NestJS 6.10.10 project, just :

import { Logger } from "nestjs-pino";

const app = await NestFactory.create(MyModule, { logger: false });
app.useLogger(app.get(Logger));

gives:

Error: Nest could not find Logger element (this provider does not exist in the current context)
    at ContainerScanner.getWrapperCollectionPairByHost (/myproject/node_modules/@nestjs/core/injector/container-scanner.js:34:19)
    at ContainerScanner.findInstanceByToken (/myproject/node_modules/@nestjs/core/injector/container-scanner.js:20:40)
    at ContainerScanner.find (/myproject/node_modules/@nestjs/core/injector/container-scanner.js:13:21)
    at NestApplication.find (/myproject/node_modules/@nestjs/core/nest-application-context.js:184:38)
    at NestApplication.get (/myproject/node_modules/@nestjs/core/nest-application-context.js:43:25)
    at /myproject/node_modules/@nestjs/core/nest-factory.js:112:40
    at Function.run (/myproject/node_modules/@nestjs/core/errors/exceptions-zone.js:8:13)
    at Proxy.<anonymous> (/myproject/node_modules/@nestjs/core/nest-factory.js:111:46)
    at bootstrap (/myproject/dist/src/main.js:9:23)
    at processTicksAndRejections (internal/process/task_queues.js:85:5)

What is the expected behavior?

Please mention other relevant information such as Node.js version and Operating System.

NodeJS: 12.9.1
NestJS: 6.10.10

[QUESTION] Does the configuration of pino-http also configure pino?

Hello,

I'm using the syntax shown in https://github.com/iamolegga/nestjs-pino#asynchronous-configuration

@Module({
  imports: [
    LoggerModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (config: ConfigService) => {
        return {
          pinoHttp: {
            level: config.level,
            prettyPrint: true
          },
        };
      }
    })
  ],
  ...
})
class TestModule {}

I get this kind of logs:

{"level":40,"time":1612819694266,"pid":37211,"hostname":"...","context":"..Service","msg":"a message"}
[1612819713135] INFO  (my-app/37211 on truc): request completed
    res: {
      "statusCode": 201,
      "headers": {
        "x-powered-by": "Express",
        "access-control-allow-origin": "*",
        "content-type": "application/vnd.api+json; charset=utf-8",
        "content-length": "91",
        "etag": "W/\"5b-otKW0l0jfPtZ563iVaysTjm6m5s\""
      }
    }
    responseTime: 12
  ...

Some of them are pretty-printed, some others aren't.

I also tried to force pino configuration:

...
      useFactory: async (config: ConfigService) => {
        return {
          pinoHttp: {
            logger: pino({
              level: config.level,
              prettyPrint: true
            },
            level: config.level,
            prettyPrint: true
          },
      }
    })
  ],
  ...
})
class TestModule {}

But no change.

How to get the same configuration for all logs?

Thank you

[QUESTION] customize pino parameters

In some cases, we need to customized pino/pino-http default params. How to configure default options globally like this?

`
{
genReqId: generateLogId, //pino-http ID generation

prettyPrint: isDevMode ? { colorize: true } : undefined // dev env pino-pretty
}
`

[BUG] Doesn't work on Nest 6.7

What is the current behavior?
Nest framework logs are not in JSON format:
image

However, injecting PinoLogger into services and using that, those logs are in JSON just fine.

Please provide the steps to reproduce.
Simply install following readme instructions into the root module:

@Module({
  imports: [
    LoggerModule.forRoot(),
    UsersModule,
  ],
  controllers: [AppController],
})
export class AppModule {}

What is the expected behavior?
Nest framework logs should be in JSON.

Please mention other relevant information such as Node.js version and Operating System.
Node version v12.13.0, OS is Ubuntu latest LTR.

According to the Nest docs:
https://docs.nestjs.com/techniques/logger#logger

You can only replace the logger in NestFactory.create(...);, which happens in src/main.ts in my application.

Thanks you for this!

I had a hard time finding a clean and easy way to log HTTP requests with Nest. Thank you ❤️

[QUESTION] How to inject nestjs-pino logger into Module configuration?

I am new to NestJS and NodeJS. I am using nestjs-pino in my application.

In my AppModule class I am importing the LoggerModule. Then when I configure TypeOrmModule I want to log something with nestjs-pino. How can I inject the nestjs-pino logger into my module configuration?

@Module({
  imports: [
    LoggerModule.forRoot({
      pinoHttp: {
        autoLogging: false
      }
    }),
    TypeOrmModule.forRootAsync({
      useFactory: async () => {
        const host = process.env.DATABASE_HOST || "localhost";
        const port = Number(process.env.DATABASE_PORT) || 5432;
        const username = process.env.DATABASE_USER || "postgres";
        const password = process.env.DATABASE_PASSWORD || "mysecretpassword";
        const database = process.env.DATABASE_DATABASE || "postgres";
        // TODO use nestjs-pino here
        console.log(`Connecting to database ${database} on ${host}:${port} with user ${username}`);
        return {
          type: "postgres", host: host, port: port, username: username, password: password, database: database, synchronize: true
        };
      }
    })
  ],
  controllers: [AppController, HealthController],
  providers: [AppService]
})
export class AppModule {
}

[QUESTION] How to use Pino for entire application with configuration options?

Hi!

First of all, thanks for this module, it is really appreciated.

I am trying to use Pino Logger throughout the application; perhaps I am wrong about it, but what I am doing is to add the following code to app.module.ts:

@Module({
    imports: [
        ConfigModule.forRoot(),
        LoggerModule.forRoot({
            pinoHttp: { prettyPrint: true, useLevelLabels: true }, // <--
        }),
        // ...
    ],
    // ...
})
export class AppModule {}

And, in main.ts:

import { NestFactory } from '@nestjs/core';
import { Logger } from 'nestjs-pino';

async function bootstrap() {
  // Create the application.
  const app = await NestFactory.create(AppModule, { logger: false });

  // Get Pino Logger instance.
  const logger = app.get(Logger);

  // Use Pino as the main logger!
  app.useLogger(logger);

  // Start the server!
  await app.listen(3030);
}

// Initialize application!
bootstrap();

The issue is that, if I import LoggerModule and call forRoot (or forRootAsync by injecting ConfigService etc), without any Pino options (prettyPrint or useLevelLabels) it works perfectly, however when adding even one option, it hangs the entire application without even booting!

  1. Is this the way to use the Logger throughout the Application?
  2. If it is, am I providing configuration options in a wrong format?

I would like to use Pino as the main logging system for the entire application, no exclusions.

I am using NestJS v7.

Again, thanks for this library! And thanks in advance for any commentary this might receive :)

[FEATURE REQUEST] allow string to be passed to `pinoHttp.stream` option

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Allow user to pass only a string to pinoHttp options, instead of a DestinationString. Forcing them to import pino to do a (recommended) pino.destination(string)

Describe the solution you'd like
A clear and concise description of what you want to happen.
Allow to pass a string as destination to pinoHttp option stream and do in nestjs-pino the pino.destination(string) transformation.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.
Did not see any other way to do so

Additional context
Add any other context or screenshots about the feature request here.
I didn't want to import pino as a main dependency since i'll be using nestjs-pino, this way we can used the pino dependency contained in nestjs-pino instead.

What are your thought about this ?

I can try to make a PR for this if needed :)

[FEATURE REQUEST] use "name" instead of "context" for the context

Is your feature request related to a problem? Please describe.
I just migrated from another library I used to use name: {ModuleName} for the context.
The main reason I did choose this, is that the default key used by pino-pretty

I would like to change the context key to use name instead.

Describe the solution you'd like
I think it would be a good idea to have an extra option at the initialization to define the key used for the context.

Describe alternatives you've considered
I tried to implement it, but I didn't weel understood the typing of the options. So I don't know how to add the option.
My code is here : https://github.com/Nhacsam/nestjs-pino/tree/add-param-to-context-key

[QUESTION] How can I retrieve the 'req.id' from each request?

I have a microservice architecture, where all external requests are http, while internal ones are gRPC.
Now for logging, I want to have a unique id for each http request, so that I can have a correlation between my logs in different microservices.

Is there a way to retrieve the 'req.id', so that I can provide it as metadata in my clientGrpc call?

[QUESTION] "failed with status code 500" output instead of detailed error message

When I'm throwing an error from Nest application nestjs-pino always prints in the error message "failed with status code 500" even though I'm throwing an error with a detailed message any idea how to solve this?

this is how I setup nestjs-pino

    LoggerModule.forRoot({
      pinoHttp: {
        prettyPrint: { colorize: true, translateTime: true },
        serializers: {
          req: (req) => ({
            ...req,
            headers: undefined,
          }),
          res: (res) => ({
            ...res,
            headers: undefined,
          }),
        },
      },
    }),

and this is how I throw the error

import {InternalServerErrorException} from '@nestjs/common';
throw new InternalServerErrorException("This is my detailed error");

And nestjs-pino output

   res: {
      "statusCode": 500
    }
    err: {
      "type": "Error",
      "message": "failed with status code 500",
      "stack":
          Error: failed with status code 500
              at ServerResponse.onResFinished (/Users/shahaf/Source/muil/muil/node_modules/pino-http/logger.js:73:38)
              at ServerResponse.clsBind (/Users/shahaf/Source/muil/muil/node_modules/cls-hooked/context.js:172:17)
              at ServerResponse.emit (node:events:388:22)
              at ServerResponse.EventEmitter.emit (node:domain:532:15)
              at ServerResponse.emitted (/Users/shahaf/Source/muil/muil/node_modules/emitter-listener/listener.js:134:21)
              at onFinish (node:_http_outgoing:800:10)
              at callback (node:internal/streams/writable:557:21)
              at afterWrite (node:internal/streams/writable:503:5)
              at afterWriteTick (node:internal/streams/writable:490:10)
              at processTicksAndRejections (node:internal/process/task_queues:79:21)
    }
    responseTime: 94
    req: {
      "id": 1,
      "method": "POST",
      "url": "/api/test/test",
      "remoteAddress": "::1",
      "remotePort": 61221
    }

Logo is missing

Okay, so i think this project can better communicate its value through some visuals.

[QUESTION] How to log req.id only?

It's overloaded to log req object with every log. Is there a way to just print req.id only?
Of course, the pinoHttp.autoLogging should be turned on. the request completed log and error logs should contain req and req completely.
Thanks in advance.

[QUESTION] Is it possible to set a request Id manually?

In my case, we receive a specific Id per request, it goes through several systems and we use it to log and track the request cycle.

Is it possible to manually change the request Id? If so, what would be the best way to do this?

[QUESTION] Extend PinoLogger class

Hi guys! I would like to extend the Pino logger to be able to inject my own business logic in some cases (for example logger.error).

So decided to build a logger module on top of nestjs-pino.

Here are my two files

logger.service.ts

import { Injectable } from '@nestjs/common';
import { PinoLogger } from 'nestjs-pino';

@Injectable()
export class LoggerService extends PinoLogger {
  error(message: any, trace?: string, context?: string, ...args: any[]) {
    // My business logic here
    super.error(message, trace);
  }
}

logger.module.ts

import { Global, Module } from '@nestjs/common';
import { LoggerService } from './logger.service';
import { LoggerModule as PinoModule } from 'nestjs-pino';

@Global()
@Module({
  imports: [PinoModule.forRoot()],
  providers: [LoggerService],
  exports: [LoggerService],
})
export class LoggerModule {}

I am importing the module in app.module.ts.
My issue is when I start the application, I got this error at module initialization.

Error: Nest can't resolve dependencies of the LoggerService (?). Please make sure that the argument pino-params at index [0] is available in the LoggerModule context.

Potential solutions:
- If pino-params is a provider, is it part of the current LoggerModule?
- If pino-params is exported from a separate @Module, is that module imported within LoggerModule?
  @Module({
    imports: [ /* the Module containing pino-params */ ]
  })

I cannot figured out why I have this message, I am well imported the PinoModule in the loggerModule.

Thanks for any help 😸

[FEATURE REQUEST] Customize requests messages

Hello,

Is your feature request related to a problem? Please describe.
I would like the ability to customize the messages automatically logged by pino-http (request completed and request errored).

Describe the solution you'd like
Since version 5.0.0 of pino-http, two new options were added: customSuccessMessage and customErrorMessage to do this. So in the end my feature request is only updating the dependencies and publishing a new version. However, the types package is not up to date, so it might not be possible right now.

Describe alternatives you've considered
I haven't considered any alternative yet.

Additional context
Nothing to add.

Thank you!

[QUESTION] How to change context of "automatic logs of every request/response"?

As discussed in the docs:

// Logs by app itself
{"level":30,"time":1570470154387,"pid":17383,"hostname":"my-host","context":"RoutesResolver","msg":"AppController {/}: true","v":1}
{"level":30,"time":1570470154391,"pid":17383,"hostname":"my-host","context":"RouterExplorer","msg":"Mapped {/, GET} route true","v":1}
{"level":30,"time":1570470154405,"pid":17383,"hostname":"my-host","context":"NestApplication","msg":"Nest application successfully started true","v":1}

// Logs by injected Logger and PinoLogger in Services/Controllers
// Every log has it's request data and unique `req.id` (per process)
{"level":30,"time":1570470161805,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"context":"AppController","msg":"getHello()","v":1}
{"level":30,"time":1570470161805,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"context":"MyService","msg":"getWorld([])","v":1}

// Automatic logs of every request/response
{"level":30,"time":1570470161819,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"res":{"statusCode":304,"headers":{...}},"responseTime":15,"msg":"request completed","v":1}

I'm talking about these last logs in the list, "automatic logs of every request/response". (The ones where "msg":"request completed"...)

I need to add a "context" to these logs, e.g. "Http", so that I can filter them out of my log aggregator.

How can I set "context":"Http" on these logs?

[QUESTION] Logging ExecutionContext's data

Does this library support logging the user associated with the request?

My application uses a custom @nestjs/passport strategy to handle authentication:

class MyStrategy extends PassportStrategy(BasicStrategy, 'custom') {
  async validate(username: string, password: string): Promise<IUser> {...}
}

When I need to access the user in a guard, for example, I do the following:

class PermissionGuard implements CanActivate {
  canActivate(context: ExecutionContext) {
    const args = context.switchToHttp();

    const request = args.getRequest();
    const user = request.user as IUser;
    ...
  }
}

Is there a way to automatically add the user to the log context?

[FEATURE REQUEST] Support autoLogging ignorePaths feature of pino-http

Is your feature request related to a problem? Please describe.
The latest version of pino-http (4.4.0) includes a new param for autoLogging called ignorePaths which allows you to specify an array of paths to ignore. We need this to prevent logging for our health-check endpoints.

There are two issues I discovered when trying to see if I could get this working with nestjs-pino.

  1. The typescript definitions for pino-http haven't been updated to include this new option. I have created a pull request to add it here (DefinitelyTyped/DefinitelyTyped#43137)

  2. The underlying pino-http feature uses the req.url property to compare against the configured ignore paths. This is a problem because req.url in nest class middlewares gets rewritten with the route mount point stripped. So it is always / regardless of which route is hit. I believe this is a byproduct of how app.use() works in express

Describe the solution you'd like
Somehow figure out how this feature could be implemented.

Describe alternatives you've considered
As a more proactive measure I've put up a PR against pino-http that if accepted would make this feature request irrelevant. It would allow for us to specify the current path through a function passed as an autoLogging option. This would allow us to use req.originalUrl instead of req.url. pinojs/pino-http#80

[QUESTION] Why `PinoLogger` instance don't show the error object

When I use a PinoLogger instance to display some error object like this.logger.error(Error('foo')) I get this:

{
  "level": 50,
  "time": 1590422389076,
  "pid": 27787,
  "hostname": "--",
  "name": "app",
  "req": {
    "id": 1,
    "method": "GET",
    "url": "--",
    "headers": {
      "host": "localhost:3000",
      "user-agent": "insomnia/7.1.1",
      "accept": "*/*"
    },
    "remoteAddress": "127.0.0.1",
    "remotePort": 52126
  },
  "context": "ExceptionsHandler"
}

seems like the arg isn't serialized by pino.stdSerializers.err because

this.logger.error( pino.stdSerializers.err(Error('foo')) )

works as expected (with { type, message, stack }). This happen even when I explicity define the serializers. Is it the right behavior?

Great lib btw!

[FEATURE REQUEST] Log function start and end automatically

Is your feature request related to a problem? Please describe.
Avoid writing logs on each function. Lets say I want to write function entry and exit log in 'INFO' level, so I can have a option like a decorator which I can mention on top of function something like @loggable, then the logs with function argument will be printed on function start and function end.

Describe the solution you'd like
It would avoid writing tedious log line on each function.

Describe alternatives you've considered
NA

Additional context
Might be there are packages existing for Java on the same lines.

[QUESTION] Use a custom wrapper class to log requests to files

Hi guys,

I want the following setup for my NestJS application:

  1. HTTP requests are logged to a dedicated file, using nestjs-pino. They'll be sent to the ELK logging facilities by Filebeat or whatever.
  2. Additionally, Logger Pino object is available for manual invocation across the project (this.logger.log("...")).
  3. Default NestJS stdout logger is enabled.

How do I achieve that?

Say, I let the "middleware" thing initialize like this:

@Module({
  imports: [ LoggerModule.forRoot({ autologging: false, pinoHttp: { ... } }) ],
  ...
})
export class AppModule {}

Is there a way I can re-create the middleware manually, where I can use my custom class (as per NestJS documentation) which in turn uses Pino logger configured by the LoggerModule, but directs its output to a file?

Thank you very much.

[BUG] Having installed nestjs-pino, app fails

What is the current behavior?

Running my nest app fails

Output from the console

ts-node -r tsconfig-paths/register src/main.ts | pino-pretty -t

1: 0x8dc510 node::Abort() [node]
2: 0x96f439 [node]
3: 0xb62a3f [node]
4: 0xb635a9 v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [node]
5: 0x1db7cbadbe1d
Aborted (core dumped)

Please provide the steps to reproduce.

run the above command

What is the expected behavior?

Application starts and logs correctly

Please mention other relevant information such as Node.js version and Operating System.

Behaviour only happens on ubuntu
Node v12

Core dump

  • thread #1, name = 'node', stop reason = signal SIGABRT
    • frame #0: 0x00007ff48c0f7e97
      frame #1: 0x000000000096f439 nodenode::i18n::Initialize(v8::Local<v8::Object>, v8::Local<v8::Value>, v8::Local<v8::Context>, void*) + 1401 frame #2: 0x0000000000b62a3f nodev8::internal::CompilationCacheRegExp::Lookup(v8::internal::Handlev8::internal::String, v8::base::Flags<v8::internal::JSRegExp::Flag, int>) + 335
      frame #3: 0x0000000000b635a9 nodev8::internal::CompilationCache::PutEval(v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::Context>, v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::FeedbackCell>, int) + 89 frame #4: 0x00003b61a755be1d frame #5: 0x00003b61a75118d5 frame #6: 0x00003b61a7e7e798 frame #7: 0x00003b61a7541811 frame #8: 0x00003b61a751f83a frame #9: 0x00003b61a7504ba1 frame #10: 0x0000000000e73f0e nodev8::internal::ParserBasev8::internal::Parser::ParseForAwaitStatement(v8::internal::ZoneList<v8::internal::AstRawString const*>, v8::internal::ZoneList<v8::internal::AstRawString const>) + 94
      frame #11: 0x0000000000f8705b nodev8::internal::Runtime_StringCharCodeAt(int, unsigned long*, v8::internal::Isolate*) + 1115 frame #12: 0x0000000000b62a3f nodev8::internal::CompilationCacheRegExp::Lookup(v8::internal::Handlev8::internal::String, v8::base::Flags<v8::internal::JSRegExp::Flag, int>) + 335
      frame #13: 0x0000000000b635a9 nodev8::internal::CompilationCache::PutEval(v8::internal::Handle<v8::internal::String>, v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::Context>, v8::internal::Handle<v8::internal::SharedFunctionInfo>, v8::internal::Handle<v8::internal::FeedbackCell>, int) + 89 frame #14: 0x00003b61a755be1d frame #15: 0x00003b61a75118d5 frame #16: 0x00003b61a750ee75 frame #17: 0x00003b61a75092c1 frame #18: 0x0000000000e725d3 nodev8::internal::Parser::CloseTemplateLiteral(v8::internal::Parser::TemplateLiteral**, int, v8::internal::Expression
      ) + 307
      frame #19: 0x0000000000aff8e9 nodev8::internal::Builtin_Impl_ArrayPrototypeFill(v8::internal::BuiltinArguments, v8::internal::Isolate*) + 1705 frame #20: 0x0000000000affaa1 nodev8::internal::Builtin_Impl_Stats_ArrayPrototypeFill(int, unsigned long*, v8::internal::Isolate*) + 97
      frame #21: 0x00000000008abf39 nodenode::AsyncWrap::GetConstructorTemplate(node::Environment*) + 297 frame #22: 0x00000000008ddbd7 nodenode::AsyncHooks::AsyncHooks() + 1911
      frame #23: 0x00000000008a6569 nodenode::AsyncWrap::DestroyAsyncIdsCallback(node::Environment*) + 361 frame #24: 0x00000000008b8a7c nodenode::cares_wrap::(anonymous namespace)::ChannelWrap::New(v8::FunctionCallbackInfov8::Value const&) + 156
      frame #25: 0x0000000000a54e0e nodereport::PrintVersionInformation(report::JSONWriter*) + 11438 frame #26: 0x0000000000a5a5c8 nodevoid report::JSONWriter::json_keyvalue<char [5], char [10]>(char const (&) [5], char const (&) [10]) + 376
      frame #27: 0x0000000000a4a21b nodenode::crypto::ClientHelloParser::ParseHeader(unsigned char const*, unsigned long) + 219 frame #28: 0x00000000008e6f45 nodenapi_get_array_length + 357
      frame #29: 0x00000000008e5239 node`napi_get_property + 537
      frame #30: 0x00007ff48c0dab97

[QUESTION] How to log the res.body

By default, only responseTime, statusCode and headers are logged.

"res":{"statusCode":304,"headers":{...}},"responseTime":15

Is there a configuration to enable logging res.body?

[FEATURE REQUEST] Provide context per class

Is your feature request related to a problem? Please describe.

I'm using globally imported pino logger module. Every time when I make a log line I would like to know from which class the log was created. Right now, I have to include the context manually. Like the following lines in the README.md:
https://github.com/iamolegga/nestjs-pino/blob/master/README.md#L56
https://github.com/iamolegga/nestjs-pino/blob/master/README.md#L72

Currently, we can set base context per configuration of logger module. That's too broad for my use case. Even if I migrate my logger module import from global to per module import, I'd prefer finer level of granularity in defining base context. (But not as fine as log line :) )

Describe the solution you'd like

I would like to write log lines using injected logger without manually providing class context on each log line. I'd like the context to be either set up once per class (injection) or somehow dynamically configured to use class name (or whatever else) per module configuration.

I'm probably off on these, but my superficial suggestions would be:

  • configure the module with an option to always fork a child logger for each class it's injected into OR
  • expose child functionality so we can fork (child) a logger manually. Then we could set it up with context in each class constructor (or wherever we deem useful)

[BUG] System logs skipped on startup

Hi,

I am using pino with configuration as illustrated below:

// app.module.ts
import { Module } from '@nestjs/common';
import { LoggerModule } from 'nestjs-pino';
import { AppController } from './app.controller';
import { AppService } from './app.service';

import * as pino from 'pino';

const logger = pino({
  level: process.env.NODE_ENV !== 'prod' ? 'debug' : 'info',
},pino.destination({
  minLength: 4096,
  sync: false,
}));

setInterval(function() {
  logger.flush();
}, 5000).unref();

@Module({
  imports: [
    LoggerModule.forRoot({
      pinoHttp: {
        logger,
        autoLogging: true,
        serializers: {
          err: pino.stdSerializers.err,
          req: r => {
            return {
              id: r.id,
              method: r.method,
              url: r.url,
            };
          },
          res: pino.stdSerializers.res,
        }
      },
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

and

// main.ts
import { NestFactory } from '@nestjs/core';
import { Logger } from 'nestjs-pino';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { logger: false });
  app.useLogger(app.get(Logger));
  await app.listen(3000);
}
bootstrap();

But the logging output missed the system logging part.

> nest start --watch
[10:11:49 PM] Starting compilation in watch mode...

[10:11:53 PM] Found 0 errors. Watching for file changes.

{"level":30,"time":1604671913986,"pid":28435,"hostname":"2070iMac","context":"RoutesResolver","msg":"AppController {}:"}
{"level":30,"time":1604671913987,"pid":28435,"hostname":"2070iMac","context":"RouterExplorer","msg":"Mapped {, GET} route"}
{"level":30,"time":1604671913989,"pid":28435,"hostname":"2070iMac","context":"NestApplication","msg":"Nest application successfully started"}

It is expected to output as many as system logging output, which has logging form modules like NestFactory and InstanceLoader. Below is the output from the default logger.

[10:33:59 PM] Starting compilation in watch mode...

[10:34:00 PM] Found 0 errors. Watching for file changes.

[Nest] 30928   - 11/06/2020, 10:34:01 PM   [NestFactory] Starting Nest application...
[Nest] 30928   - 11/06/2020, 10:34:01 PM   [InstanceLoader] LoggerModule dependencies initialized +15ms
[Nest] 30928   - 11/06/2020, 10:34:01 PM   [InstanceLoader] LoggerCoreModule dependencies initialized +1ms
[Nest] 30928   - 11/06/2020, 10:34:01 PM   [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 30928   - 11/06/2020, 10:34:01 PM   [RoutesResolver] AppController {}: +6ms
[Nest] 30928   - 11/06/2020, 10:34:01 PM   [RouterExplorer] Mapped {, GET} route +1ms
[Nest] 30928   - 11/06/2020, 10:34:01 PM   [NestApplication] Nest application successfully started +4ms

Here are some sample code to reproduce this issue: https://github.com/wuminqi/example-pino-nestjs

Can you kindly offer a help on this?

Thanks.

[QUESTION] Performance and async_hooks

Thank you guys for your good job with this library.
Pino is a great logger, and nestjs-pino provides a very nice integration for NestJS.

However, I'm interested to know your point of view about performance question we can read about async_hooks which is still in experimental status, and with the main reason of performance impact.
In addition to this (or linked with this), async_hooks is not recommended for production deployment.

As you mentioned in nestjs-pino docs, REQUEST scope can have performance issues. It's true and I fully agree.
But async_hooks have also some performance impact too.

It will be very friendly if you could share your opinion on this topic.
Thank you very much.

[FEATURE REQUEST]

Is your feature request related to a problem? Please describe.
I'm looking for a way to add the Application name into each log.

Describe the solution you'd like
I would like a new parameter in forRoot module method in order to give an App name.

Describe alternatives you've considered
I would like to get access to Pino instance logger used in the module in order to create a child logger with this property. This option is maybe more powerful.

Additional context
This kind of information is especially useful when a save all the logs of all micro-services in the same space.

[BUG] When context is supplied to Logger, message gets stringified

What is the current behavior?

Recommended way to use nestjs logger is to instantiate the singleton class Logger from @nestjs/common and supply context to it (https://stackoverflow.com/a/52907695/4601673), like so:

import { Logger } from '@nestjs/common';

class MyService implements OnModuleInit {
  logger = new Logger(MyService.name);
  onModuleInit() {
    this.logger.log({ msg: 'test' });
  }
}

Using this pattern with nestjs-pino works quite well, but there is a catch.
When context is supplied, message is sent as a second argument to pino here: https://github.com/iamolegga/nestjs-pino/blob/master/src/Logger.ts#L35.
This results in message being treated as the format string here: https://github.com/pinojs/pino/blob/master/lib/tools.js#L52.
So if message is an object, it gets stringified into the "msg" field of the logged json object, like so:

{"level":30,"time":1598681218911,"pid":92,"hostname":"sse-sandbox-hxfsk","context":"AppService","msg":"{\"msg\":\"test\"} "}

If context is not supplied, the message is supplied as the first argument and pino correctly logs the message as an object.

Please provide the steps to reproduce.

Here is the minimal codesandbox based on nestjs-typescript-starter with pino-logger applied and used from AppService.
https://codesandbox.io/s/nestjs-pino-logger-context-forked-hxfsk?file=/src/app.service.ts

There are some issues with displaying logging output though :(

What is the expected behavior?

I expect message to be logged the same way it is logged when context is not supplied (i.e. merged into root object), like so:

{"level":30,"time":1598681218911,"pid":92,"hostname":"sse-sandbox-hxfsk","context":"AppService","msg":"test"}

Please mention other relevant information such as Node.js version and Operating System.

I do not think this is relevant, but I am using node v12.18.2 and OS Windows 10 Pro 2004.

P.S.

If this is not an intended behavior, I can make a PR to fix it by merging message into context object in all of the methods of Logger class. I would also like to add this usage example to the documentation.

[QUESTION] How to add userId for each authenticated request

Hello,

I am using your library in my project. Thanks for your work, it is really helpful!

We are using a Passport local strategy for authentication (https://docs.nestjs.com/security/authentication#implementing-passport-local). If the authentication is performed correctly, the user object will be added on the request object that it will be later passed to the controllers.

What I would like to do is to add the userId value to each request information that is bundled together with each log. It seems that the serializer methods are called before this auth layer is called and I did not find a way to do this in the documentation.

Do you have any advice on how to implement this?

Thank you!

Log to FILE or DB

Hello!

How can I implement log to file or database?

I use global log as bellow:

async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: false,
});
app.useLogger(app.get(Logger));
await app.listenAsync(process.env.PORT || 3000);
}

bootstrap().then();

How can I ignore specific API routes from being logged?

Hi! Awesome module, thanks!

I have a question though. I've got a Nest.js application and here's my main.ts file:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger } from 'nestjs-pino/dist';

async function bootstrap() {
  process.env.NODE_ENV = process.env.NODE_ENV || 'DEV';
  const app = await NestFactory.create(AppModule, {logger: false});
  app.useLogger(app.get(Logger));
  await app.listen(process.env.PORT || 3000);
}
bootstrap();

After I set app.useLogger(...) I can see all the requests coming into the app in stdout. However I have a health check endpoint, which I'd like to prevent from being logged. How can I achieve it with this module?

Thanks in advance.

[BUG] Path exclusion doesn't work as expected with predefined paths

What is the current behavior?

Implementation of route exclusion (see #122 ) does not allow conditional exclusion of predefined paths without explicitly specyfing all of the applications controllers in forRoutes attribute. This issue is caused by a known limitation of Nest interoperability with functional middlewares (please see nestjs #853 for more details).

Please provide the steps to reproduce.

Nestjs-pino module configuration like the one specified below doesn't work as expected, that is GET requests to /live and /ready paths still leave log traces in applications stdout.

{
    forRoutes: [{ path: '*', method: RequestMethod.ALL }],
    exclude: [
        { path: 'live', method: RequestMethod.GET },
        { path: 'ready', method: RequestMethod.GET },
    ],
}

What is the expected behavior?

This is not explicitly stated anywhere, but a good implementation would allow conditional exclusion of predefined routes from request logging without explicitly passing all of the apps controllers, as this approach is cumbersome and error prone. Wrapping logger middleware with path exclusion logic as suggested in #853 might be a good solution.

Please mention other relevant information such as Node.js version and Operating System.

package version
node v12
nest v6+
nestjs-pino v1.1.3

[QUESTION] Usage with pino-caller

I would like to use pino-caller to log the line number where the log method was invoked. Is it currently possible to use the returned pino-caller logger instance when configuring this module?

[FEATURE REQUEST] Support GCP logging

Is your feature request related to a problem? Please describe.

A clear and concise description of what you want to happen.
Is it possible to support GCP logging like they have integration with Bunyan and Winston?

When using pino on GCP I see that it's formating logs well to make them supported in Stackdriver and Logging but it's not setting Trace field:( Also there is option to support logs related to request with section "Formatting Request Logs"

Describe the solution you'd like
Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.