Logging

Built-In For Day One, Pino For Production

Logging

Nest ships a Logger you can use immediately. For production, swap in nestjs-pino for fast structured JSON logs.

4 min read Level 2/5 #nestjs#logging#pino
What you'll learn
  • Inject Logger and use it from services
  • Replace the default logger with a custom LoggerService
  • Use nestjs-pino for structured logs in production

Out of the box, Nest gives you a Logger class with sensible defaults. That is enough for development, but in production you want structured JSON, correlation IDs, and predictable performance — which is where nestjs-pino comes in.

The Built-In Logger

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

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

  create(dto: unknown) {
    this.logger.log('Creating order');
    this.logger.warn('Stock low');
    this.logger.error('Failed to charge card', new Error().stack);
  }
}

The class name appears as the log context, which makes greppable logs a breeze.

Filtering Levels

In main.ts you can suppress noisy debug logs in production:

const app = await NestFactory.create(AppModule, {
  logger: process.env.NODE_ENV === 'production'
    ? ['log', 'warn', 'error']
    : ['log', 'warn', 'error', 'debug', 'verbose'],
});

Structured Logging With Pino

npm i nestjs-pino pino-http pino-pretty
import { LoggerModule } from 'nestjs-pino';

@Module({
  imports: [
    LoggerModule.forRoot({
      pinoHttp: {
        level: process.env.LOG_LEVEL ?? 'info',
        transport: process.env.NODE_ENV !== 'production'
          ? { target: 'pino-pretty' }
          : undefined,
      },
    }),
  ],
})
export class AppModule {}

Then in main.ts swap in the pino logger:

import { Logger } from 'nestjs-pino';

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

You now get one JSON line per request, automatic request IDs, and far lower CPU overhead than the default logger.

Caching →