nestjs-pino is a free, open source monitoring & observability project written in TypeScript and released under MIT. It has 1,540 GitHub stars, 109 forks and 12 open issues, and was last pushed 4 days ago. On this registry it ranks #194 of 271 tracked projects in Monitoring & Observability, with 5 head-to-head comparisons available.

What is nestjs-pino?

nestjs-pino is a platform-agnostic logging module for NestJS, built on Pino, that binds request context into every log line — aimed at NestJS teams that want structured JSON logs without rewriting their existing logging calls.

What it is

nestjs-pino is a TypeScript logging module that plugs Pino and pino-http into a NestJS application. It lives in the NestJS ecosystem on Node.js, and its central promise is stated in its own tagline: request context in every log. Registration happens through LoggerModule.forRoot(...) or forRootAsync(...), and because the module is @Global(), both Logger and PinoLogger become injectable everywhere after that single root registration. The project ships under the MIT licence and is documented for version 5, with earlier majors maintained on separate branches.

The concrete problem it solves is the loss of request context in application logs, plus the cost of wiring an HTTP-aware logger by hand. It replaces NestJS's built-in ConsoleLogger as the application logger, and it packages the pino-http middleware wiring that a team would otherwise assemble itself. Its NativeLogger is a drop-in replacement for ConsoleLogger that produces identical JSON output — same field names, same argument handling, same error format — with the same message, context, level, timestamp, pid, and stack fields, but powered by Pino and with automatic request context binding.

Key capabilities

  • NativeLogger as a drop-in replacement for NestJS's built-in ConsoleLogger, emitting identical JSON, so existing new Logger(MyService.name) calls keep working unchanged.
  • Request context attached to every log through the pino-http middleware registered by the module.
  • Logger as the Pino-native logger, where extra arguments are Pino interpolation values: this.logger.log('foo %s', 'bar') produces {"msg":"foo bar"}.
  • NativeLogger matching ConsoleLogger argument parsing, where the last string is the context and the remaining strings become separate log entries.
  • Structured params on NestJS 12, where plain objects after the message merge into a single params field (ConsoleLoggerOptions.structuredParams, on by default): this.logger.log('foo', { a: 1 }, { b: 2 }) produces {"message":"foo","params":{"a":1,"b":2}}.
  • nativeLoggerOptions as a ready-made preset passed through pinoHttp, and error handling that keeps stack traces: this.logger.error('msg', stackTrace, 'Ctx') produces {"message":"msg","stack":"Error: ...","context":"Ctx"}.
  • A published compatibility matrix: v5 targets NestJS 11.0.8+ and 12.0.2+, pino 10, pino-http 11, and Node.js >=22.12, while v4 covers NestJS 8 through 11, pino 7.5+ through 10, and Node.js >=14.

Who uses it and how

  • NestJS applications already logging JSON through ConsoleLogger with { json: true } that want Pino's performance and request context without touching logging call sites.
  • Services running NestJS 11.0.8+ or 12.0.2+ on Node.js >=22.12, with older stacks pinned to the v4 branch.
  • Applications bootstrapped with bufferLogs: true and app.useLogger(app.get(NativeLogger)) to route NestJS framework logs through Pino.
  • Teams that need per-request correlation in logs but do not want to thread request identifiers through service code manually.
  • A contributor base drawn in partly through the hacktoberfest topic, with 1540 stars, 109 forks, and 12 open issues.

Getting started

Install the nestjs-pino package, then register LoggerModule.forRoot({ pinoHttp: nativeLoggerOptions }) once in the root module, optionally creating the app with NestFactory.create(AppModule, { bufferLogs: true }) and app.useLogger(app.get(NativeLogger)).

How it compares

Among the tools the facts name, nestjs-pino sits between NestJS's built-in ConsoleLogger, which is the thing it replaces, and the underlying pino and pino-http packages, which it wires together for the NestJS dependency injection model. It is not a separate logging backend; it is the NestJS integration layer over Pino, and its differentiator is producing ConsoleLogger-identical JSON while adding request context.

When to use it — and when not to

A self-hoster or application owner takes on the Pino and pino-http dependency chain and its version matrix, which v5 pins to Node.js >=22.12 and NestJS 11.0.8+/12.0.2+, so projects on older runtimes must stay on v4. The documented footgun is real: importing the bare LoggerModule into a feature module instantiates it again, registers the pino-http middleware twice, and makes every request log twice, with no compile error, injection failure, or warning (issue #3074). Teams that do not need structured logs, request context, or a Pino pipeline should keep the built-in ConsoleLogger instead.

project readme (upstream, from github) — read inline

Bombed Vovchansk, Ukraine
"Vovchansk (2024-06-02) 1513" by National Police of Ukraine (Liut Brigade) is licensed under CC BY 4.0.

This is Vovchansk, Ukraine, the city where the father of this library’s author was born. This is how it looks now, after the Russian invasion. If you find this library useful and would like to thank the author, please consider donating any amount via one of the following links:
Armed Forces of Ukraine"The Come Back Alive" foundation
Thanks for your support! 🇺🇦

NestJS-Pino

npm npm GitHub branch checks state Code Coverage 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 the documentation for v5. Compatibility with earlier majors:

nestjs-pino NestJS pino pino-http Node.js
v5 11.0.8+, 12.0.2+ 10 11 >=22.12
v4 8, 9, 10, 11 7.5+, 8, 9, 10 6.4+, 7, 8, 9, 10, 11 >=14
v1 [!WARNING]

Register LoggerModule only via forRoot(...) / forRootAsync(...), and only once, in the root module. Never add the bare LoggerModule class to a feature module's imports, not even just to inject PinoLogger. Because LoggerModule is @Global(), both Logger and PinoLogger are already available everywhere after the single root registration, so you never need to re-import it. A bare import instantiates the module a second time, which registers the pino-http middleware again and makes every request log twice. The failure is completely silent: no compile error, no injection failure, no warning (#3074).

Drop-in replacement: NativeLogger

NativeLogger is a drop-in replacement for NestJS's built-in ConsoleLogger. It produces identical JSON output — same field names, same argument handling, same error format — but powered by pino with request context in every log.

If you're already using ConsoleLogger with { json: true } and want to switch to pino without changing any of your logging code, this is for you:

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));

That's it. Your existing new Logger(MyService.name) calls throughout the codebase will work exactly as before — same message, context, level, timestamp, pid, and stack fields — but now with pino's performance and automatic request context binding.

ConsoleLogger JSON output:

{"level":"log","pid":17580,"timestamp":1765305000999,"message":"Hello World","context":"AppService"}

NativeLogger + nativeLoggerOptions output:

{"level":"log","pid":17580,"timestamp":1765305000999,"message":"Hello World","context":"AppService"}
How it differs from Logger
  • Logger (pino-native): treats extra arguments as pino interpolation values. this.logger.log('foo %s', 'bar'){"msg":"foo bar"}
  • NativeLogger (NestJS-native): parses arguments the way ConsoleLogger does. this.logger.log('foo', 'bar', 'Ctx') → two logs, {"message":"foo","context":"Ctx"} and {"message":"bar","context":"Ctx"}
What matches ConsoleLogger exactly
  • Argument parsing: last string = context, rest = separate log entries
  • Structured params: on NestJS 12, plain objects after the message are merged into a single params field on one entry (ConsoleLoggerOptions.structuredParams, on by default) — this.logger.log('foo', { a: 1 }, { b: 2 }){"message":"foo","params":{"a":1,"b":2}}. On NestJS 11 each of them is a separate entry. NativeLogger follows the ConsoleLogger of the NestJS version you actually have, so out of the box there is nothing to configure — see below to override it
  • Error handling: this.logger.error('msg', stackTrace, 'Ctx'){"message":"msg","stack":"Error: ...","context":"Ctx"}
  • Error objects: this.logger.log(new Error('oops')) → full error+stack as message string
  • Exception handler: thrown errors logged with full stack in message field
  • Object messages: this.logger.log({ foo: 'bar' }){"message":{"foo":"bar"}}
  • Field names (with nativeLoggerOptions): message, timestamp, pid, level, context, stack
Keeping your ConsoleLogger options

If your application configures ConsoleLogger rather than relying on its defaults, pass the same values to keep the output identical after the switch:

LoggerModule.forRoot({
  pinoHttp: nativeLoggerOptions,
  nativeLogger: {
    // NestJS 12 default is `true`, NestJS 11 has no such option and behaves
    // as `false`. Omit it to follow the ConsoleLogger you actually have.
    structuredParams: true,
    // Spread params into the root of the record instead of nesting them
    // under `params`. NestJS default is `false`.
    flattenParams: true,
  },
});
{"level":"log","message":"foo","context":"AppService","a":1,"b":2}

Unlike NestJS, both options are honoured on every supported NestJS version — the collecting is

readme truncated — read the full docs on github

Frequently asked questions

Is nestjs-pino free to use?

nestjs-pino is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does nestjs-pino do?

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

What is nestjs-pino written in?

nestjs-pino is primarily written in TypeScript. Its source is publicly available at https://github.com/iamolegga/nestjs-pino, and it has 1,540 GitHub stars.