import { createWriteStream, existsSync, mkdirSync } from 'fs' import { join } from 'path' export class ProductionLogger { constructor(config = {}) { this.currentFileSize = 0 this.config = { level: 'info', enableConsole: true, enableFile: true, logDir: './logs', maxFileSize: 100, maxFiles: 10, enableRotation: true, enableAudit: true, ...config, } this.initializeLogDirectory() this.initializeLogStreams() } /** * 获取单例实例 */ static getInstance(config) { if (!ProductionLogger.instance) { ProductionLogger.instance = new ProductionLogger(config) } return ProductionLogger.instance } /** * 初始化日志目录 */ initializeLogDirectory() { if (this.config.enableFile && this.config.logDir) { if (!existsSync(this.config.logDir)) { mkdirSync(this.config.logDir, { recursive: true }) } } } /** * 初始化日志流 */ initializeLogStreams() { if (this.config.enableFile && this.config.logDir) { const timestamp = new Date().toISOString().split('T')[0] // 主日志文件 this.currentLogFile = join(this.config.logDir, `trading-system-${timestamp}.log`) this.logStream = createWriteStream(this.currentLogFile, { flags: 'a' }) // 审计日志文件 if (this.config.enableAudit) { const auditFile = join(this.config.logDir, `audit-${timestamp}.log`) this.auditStream = createWriteStream(auditFile, { flags: 'a' }) } } } /** * 记录 Debug 级别日志 */ debug(message, metadata, module = 'SYSTEM') { this.log('debug', module, message, metadata) } /** * 记录 Info 级别日志 */ info(message, metadata, module = 'SYSTEM') { this.log('info', module, message, metadata) } /** * 记录 Warning 级别日志 */ warn(message, metadata, module = 'SYSTEM') { this.log('warn', module, message, metadata) } /** * 记录 Error 级别日志 */ error(message, error, metadata, module = 'SYSTEM') { const errorMetadata = { ...metadata, ...(error && { error: { name: error.name, message: error.message, stack: error.stack, }, }), } this.log('error', module, message, errorMetadata) } /** * 记录 Critical 级别日志 */ critical(message, error, metadata, module = 'SYSTEM') { const errorMetadata = { ...metadata, ...(error && { error: { name: error.name, message: error.message, stack: error.stack, }, }), } this.log('critical', module, message, errorMetadata) } /** * 记录交易相关日志 */ trade(action, details) { this.log('info', 'TRADING', `交易操作: ${action}`, { ...details, category: 'trading', }) } /** * 记录对冲相关日志 */ hedge(action, details) { this.log('info', 'HEDGING', `对冲操作: ${action}`, { ...details, category: 'hedging', }) } /** * 记录账户相关日志 */ account(action, details) { this.log('info', 'ACCOUNT', `账户操作: ${action}`, { ...details, category: 'account', }) } /** * 记录 WebSocket 相关日志 */ websocket(action, details) { this.log('info', 'WEBSOCKET', `WebSocket事件: ${action}`, { ...details, category: 'websocket', }) } /** * 记录性能相关日志 */ performance(metric, value, details) { this.log('info', 'PERFORMANCE', `性能指标: ${metric}`, { metric, value, unit: 'ms', ...details, category: 'performance', }) } /** * 记录审计日志 */ audit(action, details) { const auditEntry = { timestamp: new Date().toISOString(), action, ...details, category: 'audit', } // 同时写入主日志和审计日志 this.log('info', 'AUDIT', `审计操作: ${action}`, auditEntry) if (this.auditStream) { this.auditStream.write(JSON.stringify(auditEntry) + '\n') } } /** * 核心日志记录方法 */ log(level, module, message, metadata) { // 检查日志级别 if (!this.shouldLog(level)) { return } const logEntry = { timestamp: new Date().toISOString(), level, module, message, metadata, } // 控制台输出 if (this.config.enableConsole) { this.outputToConsole(logEntry) } // 文件输出 if (this.config.enableFile && this.logStream) { this.outputToFile(logEntry) } // 检查文件轮转 if (this.config.enableRotation && this.shouldRotate()) { this.rotateLogFile() } } /** * 检查是否应该记录该级别的日志 */ shouldLog(level) { const levels = ['debug', 'info', 'warn', 'error', 'critical'] const currentLevelIndex = levels.indexOf(this.config.level) const messageLevelIndex = levels.indexOf(level) return messageLevelIndex >= currentLevelIndex } /** * 输出到控制台 */ outputToConsole(entry) { const coloredLevel = this.getColoredLevel(entry.level) const timestamp = entry.timestamp.split('T')[1].split('.')[0] // 只显示时间部分 let output = `${timestamp} ${coloredLevel} [${entry.module}] ${entry.message}` if (entry.metadata) { const metadataStr = JSON.stringify(entry.metadata, null, 2) .split('\n') .map(line => ` ${line}`) .join('\n') output += `\n${metadataStr}` } // 根据级别选择输出方式 switch (entry.level) { case 'error': case 'critical': console.error(output) break case 'warn': console.warn(output) break default: console.log(output) } } /** * 输出到文件 */ outputToFile(entry) { if (!this.logStream) return const logLine = JSON.stringify(entry) + '\n' this.logStream.write(logLine) this.currentFileSize += Buffer.byteLength(logLine) } /** * 获取带颜色的日志级别 */ getColoredLevel(level) { const colors = { debug: '\x1b[90m', info: '\x1b[32m', warn: '\x1b[33m', error: '\x1b[31m', critical: '\x1b[41m', // 红色背景 } const reset = '\x1b[0m' return `${colors[level]}${level.toUpperCase().padEnd(8)}${reset}` } /** * 检查是否需要轮转日志文件 */ shouldRotate() { const maxSizeBytes = (this.config.maxFileSize || 100) * 1024 * 1024 return this.currentFileSize > maxSizeBytes } /** * 轮转日志文件 */ rotateLogFile() { if (this.logStream) { this.logStream.end() } if (this.auditStream) { this.auditStream.end() } this.currentFileSize = 0 this.initializeLogStreams() this.info( '日志文件已轮转', { newLogFile: this.currentLogFile, }, 'LOGGER', ) } /** * 创建子日志器 */ child(module, baseMetadata) { return { debug: (message, metadata) => this.debug(message, { ...baseMetadata, ...metadata }, module), info: (message, metadata) => this.info(message, { ...baseMetadata, ...metadata }, module), warn: (message, metadata) => this.warn(message, { ...baseMetadata, ...metadata }, module), error: (message, error, metadata) => this.error(message, error, { ...baseMetadata, ...metadata }, module), critical: (message, error, metadata) => this.critical(message, error, { ...baseMetadata, ...metadata }, module), } } /** * 关闭日志器 */ close() { if (this.logStream) { this.logStream.end() } if (this.auditStream) { this.auditStream.end() } } /** * 获取统计信息 */ getStats() { return { config: this.config, currentLogFile: this.currentLogFile, currentFileSize: this.currentFileSize, streamActive: !!this.logStream && !this.logStream.destroyed, } } } // 创建默认的全局日志器实例 export const logger = ProductionLogger.getInstance({ level: process.env.LOG_LEVEL || 'info', enableConsole: process.env.NODE_ENV !== 'test', enableFile: process.env.NODE_ENV === 'production', logDir: process.env.LOG_DIR || './logs', })