import winston from 'winston'; export type Logger = winston.Logger; function safeStringify(value: unknown): string { const seen = new WeakSet(); try { return JSON.stringify(value, (_key, val) => { if (typeof val === 'object' && val !== null) { if (seen.has(val)) return '[Circular]'; seen.add(val); } if (typeof val === 'bigint') return val.toString(); return val; }); } catch (err) { return `[Unserializable: ${(err as Error).message}]`; } } export function createLogger(): Logger { return winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.printf(({ level, message, timestamp, ...meta }) => { const metaStr = Object.keys(meta).length ? ` ${safeStringify(meta)}` : ''; return `${timestamp} [${level}]: ${message}${metaStr}`; }) ), }), new winston.transports.File({ filename: 'logs/error.log', level: 'error', }), new winston.transports.File({ filename: 'logs/combined.log', }), ], }); }