import { Injectable, Logger as NestLogger, Scope } from "@nestjs/common"; // 1. Define an interface for the settings interface IMyLoggerSettings { minLevel: number; // Example: 0=debug, 1=info, 2=warn, 3=error displayTimestamp: boolean; logFormat: "pretty" | "json" | "simple"; // Add any other settings you need, mimicking tslog or your own requirements // e.g., name?: string; displayFunctionName?: boolean; etc. } // Define log level constants (optional but recommended for readability) const LogLevel = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, }; /** * This logger acts as a bridge between Nest.js Logger and log calls originating * from platform libraries. It forwards logs to NestLogger, allowing centralization * (e.g., sending to Axiom) and adds an optional prefix for context. */ @Injectable({ scope: Scope.TRANSIENT }) // TRANSIENT ensures getSubLogger provides truly independent instances if needed elsewhere export class Logger { // Use NestLogger for the actual logging output private readonly nestLogger = new NestLogger("LoggerBridge"); // Prefix to add to messages for this instance private prefix = ""; // Add a public `settings` property, typed with the interface public settings: IMyLoggerSettings; // Define default settings private static readonly defaultSettings: IMyLoggerSettings = { minLevel: process?.env?.LOGGER_BRIDGE_LOG_LEVEL ? Number(process.env.LOGGER_BRIDGE_LOG_LEVEL) : LogLevel.INFO, // Default to INFO level displayTimestamp: true, logFormat: "pretty", }; constructor(userSettings?: Partial) { // Merge default settings with user-provided settings // User settings override defaults this.settings = { ...Logger.defaultSettings, ...userSettings, }; } /** * Creates a new LoggerBridge instance with a specific prefix. * Useful for providing context from different parts of a library. * @param options - Configuration for the sub-logger. * @param options.prefix - An array of strings to join as the log prefix. * @returns A new LoggerBridge instance with the specified prefix. */ getSubLogger(options: { prefix?: string[] }): Logger { const newLogger = new Logger(); // Set the prefix for the *new* instance newLogger.prefix = options?.prefix?.join(" ") ?? ""; return newLogger; } // --- Public logging methods --- info(...args: any[]) { this.settings.minLevel <= 1 && this.logInternal("log", ...args); } warn(...args: any[]) { this.settings.minLevel <= 2 && this.logInternal("warn", ...args); } error(...args: any[]) { this.settings.minLevel <= 3 && this.logInternal("error", ...args); } debug(...args: any[]) { this.settings.minLevel === 0 && this.logInternal("debug", ...args); } trace(...args: any[]) { this.settings.minLevel === 0 && this.logInternal("verbose", ...args); } fatal(...args: any[]) { // Prepend FATAL: to the message and log as error const fatalMessage = `fatal: ${this.formatArgsAsString(args)}`; this.settings.minLevel <= 3 && this.logInternal("error", fatalMessage); } silly(...args: any[]) { const sillyMessage = `silly: ${this.formatArgsAsString(args)}`; this.settings.minLevel === 0 && this.logInternal("verbose", sillyMessage); } // --- Internal logging implementation --- /** Formats arguments into a single string */ private formatArgsAsString(args: any[]): string { return args .map((arg) => { if (typeof arg === "string") { return arg; } // Attempt to stringify non-string arguments try { return JSON.stringify(arg); } catch (e) { return "[Unserializable Object]"; } }) .join(" "); // Use space as separator, adjust if needed } /** Central method to forward logs to NestLogger */ private logInternal(level: "log" | "warn" | "error" | "debug" | "verbose", ...args: any[]) { try { // Format message from potentially multiple arguments const formattedMessage = this.formatArgsAsString(args); // Prepend the prefix if it exists const message = this.prefix ? `${this.prefix} ${formattedMessage}` : formattedMessage; // Call the corresponding NestLogger method switch (level) { case "log": this.nestLogger.log(message); break; case "warn": this.nestLogger.warn(message); break; case "error": this.nestLogger.error(message); break; case "debug": this.nestLogger.debug(message); break; case "verbose": this.nestLogger.verbose(message); break; } } catch (err) { this.nestLogger.error( `Could not bridge log message. Error: ${err instanceof Error ? err.message : String(err)}` ); } } }