import { Inject, Injectable } from '../../@eyjs-di' import { EyJsConfig } from '../../@eyjs-env' import debug from 'debug' import pino, { type BaseLogger, type LoggerOptions } from 'pino' import pretty from 'pino-pretty' import { ColorManager } from './color-themes' import { getConfigFromEnv, mergeConfig } from './env-config' import { type LoggerConfig } from './logger-config.interface' import { type ExtensibleLogger } from './logger.interface' @Injectable() export class Logger implements ExtensibleLogger { private readonly logger: BaseLogger private readonly debugEnabled: boolean private readonly debugKeys: Set private readonly debugLoggers: Map = new Map() private readonly colorManager: ColorManager private readonly serviceName: string constructor( @Inject(EyJsConfig) private readonly eyConfig: EyJsConfig, config: LoggerConfig = {}, useEnv: boolean = false ) { // Si useEnv es true, cargar configuraciΓ³n desde .env const finalConfig = useEnv ? mergeConfig(getConfigFromEnv(this.eyConfig), config) : config const { level = 'info', enableDebug = true, debugKeys = [], serverUrl, serviceName = 'eyjs-logger', colors = {}, } = finalConfig this.serviceName = serviceName this.debugEnabled = enableDebug this.debugKeys = new Set(debugKeys) this.colorManager = new ColorManager(colors) // Configure transport for Pino - simplified without custom colors const transportOptions: Record = { colorize: false, // Disable Pino's colors colorizeObjects: false, // Simple format without custom prettifiers messageFormat: (log: any, messageKey: string) => { return log[messageKey] }, } // If there's a server URL, configure for remote sending if (serverUrl) { transportOptions['target'] = 'pino-http-send' transportOptions['options'] = { url: serverUrl, method: 'POST', headers: { 'Content-Type': 'application/json', }, } } else { transportOptions['target'] = 'pino-pretty' } const transport = pretty(transportOptions) const options: LoggerOptions = { level, base: { service: serviceName, timestamp: () => `,"timestamp":"${new Date().toISOString()}"`, }, } this.logger = pino(options, transport) // Configure debug colors this.setupDebugColors() } private setupDebugColors(): void { const originalLog = debug.log if (originalLog) { debug.log = (str: string) => { const key = str.split(' ')[0] const color = key ? this.colorManager.getDebugKeyColor(key) : '' const reset = this.colorManager.getResetColor() originalLog(`${color}${str}${reset}`) } } } setColorTheme(theme: string): void { this.colorManager.addCustomColors({ theme: theme as any }) } addCustomColors(customColors: any): void { this.colorManager.addCustomColors(customColors) } disableColors(): void { this.colorManager.disableColors() } enableColors(): void { this.colorManager.enableColors() } info(message: string, context?: Record): void { const color = this.colorManager.getLevelColor('info') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}${message}${reset}` : message this.logger.info(context, coloredMessage) } warn(message: string, context?: Record): void { const color = this.colorManager.getLevelColor('warn') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}${message}${reset}` : message this.logger.warn(context, coloredMessage) } error(message: string, context?: Record): void { const color = this.colorManager.getLevelColor('error') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}${message}${reset}` : message this.logger.error(context, coloredMessage) } debug(key: string, message: string, context?: Record): void { if (!this.debugEnabled) return // Auto-prefix with service name if not already prefixed const prefixedKey = key.includes(':') ? key : `${this.serviceName}:${key}` let debugLogger = this.debugLoggers.get(prefixedKey) if (!debugLogger) { debugLogger = debug(prefixedKey) this.debugLoggers.set(prefixedKey, debugLogger) } const contextStr = context ? ` ${JSON.stringify(context)}` : '' debugLogger(`${message}${contextStr}`) } trace(message: string, context?: Record): void { const color = this.colorManager.getLevelColor('trace') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}${message}${reset}` : message this.logger.trace(context, coloredMessage) } createDebugLogger(key: string): debug.Debugger { if (!this.debugEnabled) { return debug(key) } let debugLogger = this.debugLoggers.get(key) if (!debugLogger) { debugLogger = debug(key) this.debugLoggers.set(key, debugLogger) } return debugLogger } enableDebugKey(key: string): void { this.debugKeys.add(key) } disableDebugKey(key: string): void { this.debugKeys.delete(key) } getActiveDebugKeys(): string[] { return Array.from(this.debugKeys) } userAction( action: string, userId?: string, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('userAction') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}πŸ‘€ User action: ${action}${reset}` : `πŸ‘€ User action: ${action}` this.logger.info({ userId, ...context }, coloredMessage) } systemInfo(message: string, context?: Record): void { const color = this.colorManager.getLogTypeColor('systemInfo') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}βš™οΈ System: ${message}${reset}` : `βš™οΈ System: ${message}` this.logger.info(context, coloredMessage) } businessLogic(message: string, context?: Record): void { const color = this.colorManager.getLogTypeColor('businessLogic') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}πŸ’Ό Business: ${message}${reset}` : `πŸ’Ό Business: ${message}` this.logger.info(context, coloredMessage) } deprecatedFeature( feature: string, alternative?: string, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('deprecatedFeature') const reset = this.colorManager.getResetColor() const message = alternative ? `⚠️ Deprecated feature '${feature}' used. Consider using '${alternative}' instead.` : `⚠️ Deprecated feature '${feature}' used.` const coloredMessage = this.colorManager.config.enabled ? `${color}${message}${reset}` : message this.logger.warn(context, coloredMessage) } performanceWarning(message: string, metrics?: Record): void { const color = this.colorManager.getLogTypeColor('performanceWarning') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}🐌 Performance warning: ${message}${reset}` : `🐌 Performance warning: ${message}` this.logger.warn(metrics, coloredMessage) } securityWarning(message: string, context?: Record): void { const color = this.colorManager.getLogTypeColor('securityWarning') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}πŸ”’ Security warning: ${message}${reset}` : `πŸ”’ Security warning: ${message}` this.logger.warn(context, coloredMessage) } configurationWarning( message: string, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('configurationWarning') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}βš™οΈ Configuration warning: ${message}${reset}` : `βš™οΈ Configuration warning: ${message}` this.logger.warn(context, coloredMessage) } validationError( message: string, field?: string, value?: unknown, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('validationError') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}❌ Validation error: ${message}${reset}` : `❌ Validation error: ${message}` this.logger.error({ field, value, ...context }, coloredMessage) } databaseError( message: string, query?: string, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('databaseError') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}πŸ—„οΈ Database error: ${message}${reset}` : `πŸ—„οΈ Database error: ${message}` this.logger.error({ query, ...context }, coloredMessage) } apiError( message: string, endpoint?: string, statusCode?: number, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('apiError') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}🌐 API error: ${message}${reset}` : `🌐 API error: ${message}` this.logger.error({ endpoint, statusCode, ...context }, coloredMessage) } authenticationError( message: string, userId?: string, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('authenticationError') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}πŸ” Authentication error: ${message}${reset}` : `πŸ” Authentication error: ${message}` this.logger.error({ userId, ...context }, coloredMessage) } systemError( message: string, component?: string, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('systemError') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}πŸ’₯ System error: ${message}${reset}` : `πŸ’₯ System error: ${message}` this.logger.error({ component, ...context }, coloredMessage) } businessError( message: string, operation?: string, context?: Record, ): void { const color = this.colorManager.getLogTypeColor('businessError') const reset = this.colorManager.getResetColor() const coloredMessage = this.colorManager.config.enabled ? `${color}πŸ’Ό Business error: ${message}${reset}` : `πŸ’Ό Business error: ${message}` this.logger.error({ operation, ...context }, coloredMessage) } }