import { Injectable, OnApplicationShutdown } from '@nestjs/common'; import { PinoLogger } from 'nestjs-pino'; type LogLevel = 'debug' | 'error' | 'info' | 'warn'; type LogContext = Record; const allowedFields = new Set([ 'asOf', 'attempts', 'brokerageAccountId', 'channelState', 'clientRequestId', 'code', 'count', 'durationMs', 'environment', 'errorName', 'errorStack', 'found', 'grpcCode', 'headersReceived', 'invalidFields', 'method', 'networkMs', 'operation', 'queueWaitMs', 'requestId', 'route', 'service', 'status', 'trackingId', 'userId', ]); @Injectable() export class StructuredLogger implements OnApplicationShutdown { constructor(private readonly pino: PinoLogger) {} info(message: string, context?: LogContext): void { this.write('info', message, context); } event(level: LogLevel, message: string, context?: LogContext): void { this.write(level, message, context); } onApplicationShutdown(): void { this.info('application.stopped'); } private write( level: LogLevel, message: string, context: LogContext = {}, ): void { // eslint-disable-next-line security/detect-object-injection -- The log level is restricted to the internal LogLevel union. this.pino[level]( this.safeContext(context), message, ); } private safeContext(context: LogContext): LogContext { return Object.fromEntries( Object.entries(context).filter( ([key, value]) => allowedFields.has(key) && value !== undefined, ), ); } }