import IAppender from './appenders/IAppender'; import {LoggingLevel, LoggingLevelMapping} from './LoggingLevel'; import type {Threshold} from './Threshold'; export class Logger { private readonly _category: string; private readonly _threshold: Threshold; private readonly _appenders: IAppender[]; constructor({category, threshold, appenders}: {category: string; threshold: Threshold; appenders: IAppender[]}) { this._category = category; this._threshold = threshold; this._appenders = appenders; } public info(message: string, ...optionalParameters: unknown[]): void { if (this._threshold.value < LoggingLevel.Info) { return; } this.log(LoggingLevel.Info, message, ...optionalParameters); } public warn(message: string, ...optionalParameters: unknown[]): void { if (this._threshold.value < LoggingLevel.Warning) { return; } this.log(LoggingLevel.Warning, message, ...optionalParameters); } public error(message: string, ...optionalParameters: unknown[]): void { if (this._threshold.value < LoggingLevel.Error) { return; } this.log(LoggingLevel.Error, message, ...optionalParameters); } public fatal(message: string, ...optionalParameters: unknown[]): void { if (this._threshold.value < LoggingLevel.Fatal) { return; } this.log(LoggingLevel.Fatal, message, ...optionalParameters); } public debug(message: string, ...optionalParameters: unknown[]): void { if (this._threshold.value < LoggingLevel.Debug) { return; } this.log(LoggingLevel.Debug, message, ...optionalParameters); } public trace(message: string, ...optionalParameters: unknown[]): void { if (this._threshold.value < LoggingLevel.Trace) { return; } this.log(LoggingLevel.Trace, message, ...optionalParameters); } private log(level: LoggingLevel, message: string, ...optionalParameters: unknown[]): void { const timestamp = new Date().toISOString(); const formattedMessage = this.formatMessage(message, ...optionalParameters); const levelString = LoggingLevelMapping.convertLoggingLevelToLoggingLevelType(level); // Safely call appenders with error handling this._appenders.forEach(appender => { try { appender.append(timestamp, level, this._category, `[${levelString}] ${formattedMessage}`); } catch (error) { // Fallback to console if appender fails console.error(`[Logger] Appender failed for category ${this._category}:`, error); console.log(`${timestamp} [${this._category}] [${levelString}] ${formattedMessage}`); } }); } private formatMessage(message: string, ...optionalParameters: unknown[]): string { if (optionalParameters.length === 0) { return message; } // More efficient parameter substitution let result = message; let paramIndex = 0; // Replace all {} placeholders with parameters while (result.includes('{}') && paramIndex < optionalParameters.length) { const paramString = this.parameterToString(optionalParameters[paramIndex]); result = result.replace('{}', paramString); paramIndex++; } // Append remaining parameters if any if (paramIndex < optionalParameters.length) { const remainingParams = optionalParameters.slice(paramIndex) .map(param => this.parameterToString(param)) .join(' '); result += ` ${remainingParams}`; } return result; } private parameterToString(param: unknown): string { if (param === null) return 'null'; if (param === undefined) return 'undefined'; if (typeof param === 'object') { try { return JSON.stringify(param); } catch { return String(param); } } return String(param); } }