diff --git a/api/src/logger/logger.service.ts b/api/src/logger/logger.service.ts index 70a429a6..cf6bc358 100644 --- a/api/src/logger/logger.service.ts +++ b/api/src/logger/logger.service.ts @@ -9,9 +9,80 @@ import { ConsoleLogger, Inject, Injectable, Scope } from '@nestjs/common'; import { INQUIRER } from '@nestjs/core'; +type TLog = 'log' | 'warn' | 'error' | 'verbose' | 'debug'; + @Injectable({ scope: Scope.TRANSIENT }) export class LoggerService extends ConsoleLogger { constructor(@Inject(INQUIRER) private parentClass: object) { super(parentClass.constructor.name); } + + log(message: any, ...args: any[]) { + super.log(message); + this.logArguments('log', args); + } + + error(message: any, ...args: any[]) { + super.error(message); + this.logArguments('error', args); + } + + warn(message: any, ...args: any[]) { + super.warn(message); + this.logArguments('warn', args); + } + + debug(message: any, ...args: any[]) { + super.debug(message); + this.logArguments('debug', args); + } + + verbose(message: any, ...args: any[]) { + super.verbose(message); + this.logArguments('verbose', args); + } + + private safeStringifyReplacer() { + const seen = new WeakSet(); + return (key: string, value: any) => { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) return '[Circular Reference]'; + seen.add(value); + } + return value; + }; + } + + private handleError(type: TLog, error: Error) { + const errorInfo = { + message: error.message, + name: error.name, + stack: error.stack, + }; + super[type](JSON.stringify(errorInfo, this.safeStringifyReplacer())); + } + + private logArguments(type: TLog, args: any[]) { + const isDevMode = process.env.NODE_ENV?.includes('dev'); + if (isDevMode) { + args.forEach((arg) => { + if (arg instanceof Error) { + this.handleError(type, arg); + } else if (typeof arg === 'object' && arg !== null) { + super[type](JSON.stringify(arg, this.safeStringifyReplacer())); + } else { + super[type](arg); + } + }); + } else { + // In production we do not stringify the arguments for performance reasons, except for errors + args.forEach((arg) => { + if (arg instanceof Error) { + this.handleError(type, arg); + } else { + super[type](arg); + } + }); + } + } }