@seta-labs/nestjs-logging
v0.4.0
Published
Structured logging for NestJS APIs: pino behind the Nest Logger, one JSON line per request with request id, credential redaction, error serialization and an opt-in catch-all exception filter with a stable error body
Readme
@seta-labs/nestjs-logging
Structured logging for NestJS APIs, as BLUEPRINT.md → Logging requires: pino behind the Nest Logger, one JSON line per HTTP request with requestId, credential redaction, error serialization with sanitized stacks, and an opt-in catch-all exception filter with a stable error body.
Install
pnpm add @seta-labs/nestjs-logging nestjs-pino pino pino-http
pnpm add -D pino-pretty # local `LOG_FORMAT=pretty` onlyWire it
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { AllExceptionsFilter, LoggingModule } from '@seta-labs/nestjs-logging';
@Module({
imports: [LoggingModule.forRoot({ service: 'my-api' })],
// Explicit: adopting the logger must not change the HTTP error body by itself.
providers: [{ provide: APP_FILTER, useClass: AllExceptionsFilter }],
})
export class AppModule {}// main.ts
import { NestFactory } from '@nestjs/core';
import { installLogger } from '@seta-labs/nestjs-logging';
const app = await NestFactory.create(AppModule, { bufferLogs: true });
installLogger(app);Then, everywhere: new Logger(MyService.name) from @nestjs/common, data object first and constant message second (logger.warn({ orderId }, 'payment declined')), errors under err.
Behaviour
LOG_FORMAT:json(default in production) orpretty(default elsewhere, needspino-pretty).LOG_LEVEL: pino level,silentunderNODE_ENV=test,infootherwise.- Every record:
level, ISOtime,message,context,requestId(andservicewhenforRoot({ service })is set). - One line per request: method, url, status,
responseTime; health probes (/health,/health/ready) excluded,/health/statuslogged; 4xx atwarn, 5xx aterror. x-request-idreused from the edge when present (≤128 chars), minted otherwise, echoed on the response.- Redaction of
password,token,secret,apiKey,api_key,authorization,cookieat any depth;user:pass@hostmasked in error messages and stacks. AllExceptionsFilter:HttpExceptionkeeps status and payload (ValidationPipe arrays included); anything else becomes a neutral 500 logged with the stack. Body:statusCode,error,message,requestId,path,timestamp, plus any domain field the exception object carries (throw new ConflictException({ message, retryAt })→retryAtin the body); the six standard keys cannot be overridden.
createLoggerConfig(options) and the helpers (serializeError, requestId, httpLogLevel, …) are exported for apps that need to extend the configuration.
