UNPKG

yijing-bazi-mcp

Version:

易经八字分析 MCP 服务器 - 提供传统命理分析和易经卦象解读的AI工具

459 lines (404 loc) 11.5 kB
/** * 日志工具类 * 用于系统日志记录和错误追踪 */ const winston = require('winston'); const path = require('path'); const fs = require('fs'); class Logger { constructor(options = {}) { // 单例模式,避免重复创建winston实例 if (Logger.instance) { return Logger.instance; } this.logDir = options.logDir || path.join(process.cwd(), 'logs'); this.level = options.level || 'info'; this.maxFiles = options.maxFiles || 5; this.maxSize = options.maxSize || '10m'; this.ensureLogDirectory(); this.logger = this.createLogger(); Logger.instance = this; } /** * 确保日志目录存在 */ ensureLogDirectory() { if (!fs.existsSync(this.logDir)) { fs.mkdirSync(this.logDir, { recursive: true }); } } /** * 创建Winston日志器 */ createLogger() { const logFormat = winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.json(), winston.format.printf(({ timestamp, level, message, stack, ...meta }) => { let log = `${timestamp} [${level.toUpperCase()}]: ${message}`; if (Object.keys(meta).length > 0) { log += ` | Meta: ${JSON.stringify(meta)}`; } if (stack) { log += `\nStack: ${stack}`; } return log; }) ); const transports = [ // 控制台输出 new winston.transports.Console({ level: this.level, format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) }), // 错误日志文件 new winston.transports.File({ filename: path.join(this.logDir, 'error.log'), level: 'error', format: logFormat, maxsize: this.maxSize, maxFiles: this.maxFiles }), // 综合日志文件 new winston.transports.File({ filename: path.join(this.logDir, 'combined.log'), format: logFormat, maxsize: this.maxSize, maxFiles: this.maxFiles }), // 应用日志文件 new winston.transports.File({ filename: path.join(this.logDir, 'app.log'), level: 'info', format: logFormat, maxsize: this.maxSize, maxFiles: this.maxFiles }) ]; return winston.createLogger({ level: this.level, format: logFormat, transports, exitOnError: false }); } /** * 记录信息日志 * @param {string} message - 日志消息 * @param {Object} meta - 元数据 */ info(message, meta = {}) { this.logger.info(message, meta); } /** * 记录警告日志 * @param {string} message - 日志消息 * @param {Object} meta - 元数据 */ warn(message, meta = {}) { this.logger.warn(message, meta); } /** * 记录错误日志 * @param {string} message - 日志消息 * @param {Error|Object} error - 错误对象或元数据 */ error(message, error = {}) { if (error instanceof Error) { this.logger.error(message, { error: error.message, stack: error.stack, name: error.name }); } else { this.logger.error(message, error); } } /** * 记录调试日志 * @param {string} message - 日志消息 * @param {Object} meta - 元数据 */ debug(message, meta = {}) { this.logger.debug(message, meta); } /** * 记录详细日志 * @param {string} message - 日志消息 * @param {Object} meta - 元数据 */ verbose(message, meta = {}) { this.logger.verbose(message, meta); } /** * 记录工具调用日志 * @param {string} toolName - 工具名称 * @param {Object} params - 参数 * @param {string} status - 状态 (start|success|error) * @param {*} result - 结果或错误 */ logToolCall(toolName, params, status, result = null) { const logData = { tool: toolName, params: this.sanitizeParams(params), status, timestamp: new Date().toISOString() }; if (result) { if (status === 'error') { logData.error = result instanceof Error ? result.message : result; } else { logData.result = this.sanitizeResult(result); } } switch (status) { case 'start': this.info(`工具调用开始: ${toolName}`, logData); break; case 'success': this.info(`工具调用成功: ${toolName}`, logData); break; case 'error': this.error(`工具调用失败: ${toolName}`, logData); break; default: this.info(`工具调用: ${toolName}`, logData); } } /** * 记录MCP服务器日志 * @param {string} event - 事件类型 * @param {Object} data - 事件数据 */ logMCPEvent(event, data = {}) { this.info(`MCP事件: ${event}`, { event, data: this.sanitizeData(data), timestamp: new Date().toISOString() }); } /** * 记录性能指标 * @param {string} operation - 操作名称 * @param {number} duration - 持续时间(毫秒) * @param {Object} meta - 额外元数据 */ logPerformance(operation, duration, meta = {}) { this.info(`性能指标: ${operation}`, { operation, duration: `${duration}ms`, performance: true, ...meta, timestamp: new Date().toISOString() }); } /** * 记录用户操作 * @param {string} action - 操作类型 * @param {Object} details - 操作详情 */ logUserAction(action, details = {}) { this.info(`用户操作: ${action}`, { action, details: this.sanitizeData(details), userAction: true, timestamp: new Date().toISOString() }); } /** * 记录系统状态 * @param {string} component - 组件名称 * @param {string} status - 状态 * @param {Object} details - 详情 */ logSystemStatus(component, status, details = {}) { this.info(`系统状态: ${component} - ${status}`, { component, status, details: this.sanitizeData(details), systemStatus: true, timestamp: new Date().toISOString() }); } /** * 记录安全事件 * @param {string} event - 安全事件类型 * @param {Object} details - 事件详情 * @param {string} severity - 严重程度 (low|medium|high|critical) */ logSecurityEvent(event, details = {}, severity = 'medium') { const logMethod = severity === 'critical' || severity === 'high' ? 'error' : 'warn'; this[logMethod](`安全事件: ${event}`, { securityEvent: event, severity, details: this.sanitizeData(details), timestamp: new Date().toISOString() }); } /** * 清理敏感参数 * @param {Object} params - 参数对象 * @returns {Object} 清理后的参数 */ sanitizeParams(params) { const sensitiveKeys = ['password', 'token', 'secret', 'key', 'auth']; const sanitized = { ...params }; for (const key of Object.keys(sanitized)) { if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) { sanitized[key] = '[REDACTED]'; } } return sanitized; } /** * 清理结果数据 * @param {*} result - 结果数据 * @returns {*} 清理后的结果 */ sanitizeResult(result) { if (typeof result === 'string' && result.length > 1000) { return result.substring(0, 1000) + '... [TRUNCATED]'; } if (typeof result === 'object' && result !== null) { return this.sanitizeData(result); } return result; } /** * 清理数据对象 * @param {Object} data - 数据对象 * @returns {Object} 清理后的数据 */ sanitizeData(data) { if (typeof data !== 'object' || data === null) { return data; } const sanitized = {}; const maxDepth = 3; const sanitizeRecursive = (obj, depth = 0) => { if (depth > maxDepth) { return '[MAX_DEPTH_REACHED]'; } const result = {}; for (const [key, value] of Object.entries(obj)) { if (typeof value === 'object' && value !== null) { if (Array.isArray(value)) { result[key] = value.slice(0, 10).map(item => typeof item === 'object' ? sanitizeRecursive(item, depth + 1) : item ); if (value.length > 10) { result[key].push(`... [${value.length - 10} more items]`); } } else { result[key] = sanitizeRecursive(value, depth + 1); } } else if (typeof value === 'string' && value.length > 500) { result[key] = value.substring(0, 500) + '... [TRUNCATED]'; } else { result[key] = value; } } return result; }; return sanitizeRecursive(data); } /** * 创建子日志器 * @param {string} module - 模块名称 * @returns {Object} 子日志器 */ child(module) { return { info: (message, meta = {}) => this.info(`[${module}] ${message}`, meta), warn: (message, meta = {}) => this.warn(`[${module}] ${message}`, meta), error: (message, error = {}) => this.error(`[${module}] ${message}`, error), debug: (message, meta = {}) => this.debug(`[${module}] ${message}`, meta), verbose: (message, meta = {}) => this.verbose(`[${module}] ${message}`, meta) }; } /** * 获取日志统计信息 * @returns {Object} 统计信息 */ getStats() { const logFiles = ['error.log', 'combined.log', 'app.log']; const stats = { logDirectory: this.logDir, files: {}, totalSize: 0 }; for (const file of logFiles) { const filePath = path.join(this.logDir, file); try { if (fs.existsSync(filePath)) { const stat = fs.statSync(filePath); stats.files[file] = { size: stat.size, modified: stat.mtime, exists: true }; stats.totalSize += stat.size; } else { stats.files[file] = { exists: false }; } } catch (error) { stats.files[file] = { error: error.message, exists: false }; } } return stats; } /** * 清理旧日志文件 * @param {number} daysToKeep - 保留天数 */ cleanupLogs(daysToKeep = 30) { try { const files = fs.readdirSync(this.logDir); const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - daysToKeep); let deletedCount = 0; for (const file of files) { const filePath = path.join(this.logDir, file); const stat = fs.statSync(filePath); if (stat.mtime < cutoffDate) { fs.unlinkSync(filePath); deletedCount++; this.info(`删除旧日志文件: ${file}`); } } this.info(`日志清理完成,删除了 ${deletedCount} 个文件`); } catch (error) { this.error('日志清理失败', error); } } /** * 设置日志级别 * @param {string} level - 日志级别 */ setLevel(level) { this.level = level; this.logger.level = level; this.info(`日志级别已设置为: ${level}`); } /** * 关闭日志器 */ close() { this.logger.close(); } } // 创建默认日志器实例 const defaultLogger = new Logger(); module.exports = Logger; module.exports.default = defaultLogger;