import type { ILoggingConfig } from '../index.js'; /** * Log levels in order of severity */ const LOG_LEVELS = { error: 0, warn: 1, info: 2, debug: 3, } as const; type LogLevel = keyof typeof LOG_LEVELS; /** * Structured logger with configurable levels and formats */ export class Logger { private config: Required; private minLevel: number; constructor(config: ILoggingConfig) { // Apply defaults for any missing config this.config = { level: config.level ?? 'info', format: config.format ?? 'text', enabled: config.enabled ?? true, }; this.minLevel = LOG_LEVELS[this.config.level]; } /** * Check if a log level should be output */ private shouldLog(level: LogLevel): boolean { if (!this.config.enabled) { return false; } return LOG_LEVELS[level] <= this.minLevel; } /** * Format a log message */ private format(level: LogLevel, message: string, meta?: Record): string { const timestamp = new Date().toISOString(); if (this.config.format === 'json') { return JSON.stringify({ timestamp, level, message, ...(meta || {}), }); } // Text format const metaStr = meta ? ` ${JSON.stringify(meta)}` : ''; return `[${timestamp}] ${level.toUpperCase()}: ${message}${metaStr}`; } /** * Log at error level */ public error(message: string, meta?: Record): void { if (this.shouldLog('error')) { console.error(this.format('error', message, meta)); } } /** * Log at warn level */ public warn(message: string, meta?: Record): void { if (this.shouldLog('warn')) { console.warn(this.format('warn', message, meta)); } } /** * Log at info level */ public info(message: string, meta?: Record): void { if (this.shouldLog('info')) { console.log(this.format('info', message, meta)); } } /** * Log at debug level */ public debug(message: string, meta?: Record): void { if (this.shouldLog('debug')) { console.log(this.format('debug', message, meta)); } } /** * Log HTTP request */ public request(method: string, url: string, meta?: Record): void { this.info(`→ ${method} ${url}`, meta); } /** * Log HTTP response */ public response(method: string, url: string, statusCode: number, duration: number): void { const level: LogLevel = statusCode >= 500 ? 'error' : statusCode >= 400 ? 'warn' : 'info'; if (this.shouldLog(level)) { const message = `← ${method} ${url} - ${statusCode} (${duration}ms)`; if (level === 'error') { this.error(message, { statusCode, duration }); } else if (level === 'warn') { this.warn(message, { statusCode, duration }); } else { this.info(message, { statusCode, duration }); } } } /** * Log S3 error */ public s3Error(code: string, message: string, status: number): void { this.error(`[S3Error] ${code}: ${message}`, { code, status }); } }