import { Injectable , Inject , Scope } from '@nestjs/common' ; import pino from 'pino' ; import { Params , isPassedLogger , PARAMS_PROVIDER_TOKEN } from './params' ; import { storage } from './storage' ; type PinoMethods = Pick < pino . Logger , 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' > ; /**
 * This is copy of pino.LogFn but with possibilty to make method override.
 * Current usage works:
 *
 *  trace(msg: string, ...args: any[]): void;
 *  trace(obj: object, msg?: string, ...args: any[]): void;
 *  trace(...args: Parameters<LoggerFn>) {
 *    this.call('trace', ...args);
 *  }
 *
 * But if change local LoggerFn to pino.LogFn – this will say that overrides
 * are incompatible
 */ type LoggerFn = ( ( msg : string , ... args : any [ ] ) => void ) | ( ( obj : object , msg ? : string , ... args : any [ ] ) => void ) ; let outOfContext ; export function __resetOutOfContextForTests ( ) { outOfContext = undefined ; PinoLogger . root = undefined ; } @ Injectable ( { scope : Scope . TRANSIENT } ) export class PinoLogger implements PinoMethods { /**
     * root is the most root logger that can be used to change params at runtime.
     * Accessible only when `useExisting` is not set to `true` in `Params`.
     * Readonly, but you can change it's properties.
     */ static readonly root : pino . Logger ; private context = '' ; private readonly contextName : string ; constructor ( @ Inject ( PARAMS_PROVIDER_TOKEN ) { pinoHttp , renameContext } ) { if ( ! outOfContext ) { if ( Array . isArray ( pinoHttp ) ) { outOfContext = pino ( ... pinoHttp ) ; } else if ( isPassedLogger ( pinoHttp ) ) { outOfContext = pinoHttp . logger ; } else if ( typeof pinoHttp === 'object' && 'stream' in pinoHttp && typeof pinoHttp . stream !== 'undefined' ) { outOfContext = pino ( pinoHttp , pinoHttp . stream ) ; } else { outOfContext = pino ( pinoHttp ) ; } } this . contextName = renameContext || 'context' ; } trace ( msg , ... args ) ; trace ( obj , msg ? , ... args ) ; trace ( ... args ) { this . call ( 'trace' , ... args ) ; } debug ( msg , ... args ) ; debug ( obj , msg ? , ... args ) ; debug ( ... args ) { this . call ( 'debug' , ... args ) ; } info ( msg , ... args ) ; info ( obj , msg ? , ... args ) ; info ( ... args ) { this . call ( 'info' , ... args ) ; } warn ( msg , ... args ) ; warn ( obj , msg ? , ... args ) ; warn ( ... args ) { this . call ( 'warn' , ... args ) ; } error ( msg , ... args ) ; error ( obj , msg ? , ... args ) ; error ( ... args ) { this . call ( 'error' , ... args ) ; } fatal ( msg , ... args ) ; fatal ( obj , msg ? , ... args ) ; fatal ( ... args ) { this . call ( 'fatal' , ... args ) ; } setContext ( value ) { this . context = value ; } private call ( method , ... args ) { if ( this . context ) { if ( isFirstArgObject ( args ) ) { const firstArg = args [ 0 ] ; if ( firstArg instanceof Error ) { args = [ Object . assign ( { [ this . contextName ] : this . context } , { err : firstArg } ) , ... args . slice ( 1 ) , ] ; } else { args = [ Object . assign ( { [ this . contextName ] : this . context } , firstArg ) , ... args . slice ( 1 ) , ] ; } } else { args = [ { [ this . contextName ] : this . context } , ... args ] ; } } this . logger [ method ] ( ... args ) ; } public get logger ( ) : pino . Logger { return storage . getStore ( ) ?. logger || outOfContext ! ; } public assign ( fields ) { const store = storage . getStore ( ) ; if ( ! store ) { throw new Error ( `${ PinoLogger . name }: unable to assign extra fields out of request scope` ) ; } store . logger = store . logger . child ( fields ) ; } } function isFirstArgObject ( args ) { return typeof args [ 0 ] === 'object' ; }