/** * Logger Utility * 统一日志工具 */ import { appConfig } from '../config/app' /** * 日志级别 */ export enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 } /** * 日志级别映射 */ const LOG_LEVEL_MAP: Record = { debug: LogLevel.DEBUG, info: LogLevel.INFO, warn: LogLevel.WARN, error: LogLevel.ERROR } function parseBooleanEnv(value: string | undefined): boolean | undefined { if (typeof value !== 'string') { return undefined } const normalized = value.trim().toLowerCase() if (['1', 'true', 'yes', 'on'].includes(normalized)) { return true } if (['0', 'false', 'no', 'off'].includes(normalized)) { return false } return undefined } /** * 当前日志级别 */ const currentLogLevel = LOG_LEVEL_MAP[appConfig.logging.level] || LogLevel.INFO const prodSummaryLogOnly = appConfig.nodeEnv === 'production' && (parseBooleanEnv(process.env.PROD_SUMMARY_LOG_ONLY) ?? true) /** * 格式化时间戳 */ function formatTimestamp(): string { return new Date().toISOString() } /** * 安全的 JSON 序列化,处理大对象和循环引用 */ function safeStringify(obj: any, maxLength?: number): string { try { const seen = new WeakSet() const json = JSON.stringify(obj, (key, value) => { // 处理循环引用 if (typeof value === 'object' && value !== null) { if (seen.has(value)) { return '[Circular]' } seen.add(value) } return value }, 2) // 如果指定了最大长度且超出,则截断 if (maxLength && json.length > maxLength) { return json.slice(0, maxLength) + `...(truncated, total ${json.length} chars)` } return json } catch (error) { return '[Serialization Error]' } } /** * 格式化日志消息 */ function formatMessage(level: string, message: string, meta?: any): string { const timestamp = formatTimestamp() // 智能处理 meta 数据 let metaStr = '' if (meta) { // 对于大文本字段,不进行截断(如 AI 响应、代码等) const largeTextFields = ['content', 'code', 'response', 'aiResponse', 'stdout', 'stderr', 'output'] const hasLargeText = largeTextFields.some(field => field in meta) if (hasLargeText) { // 包含大文本,使用更大的限制或不限制 metaStr = ` ${safeStringify(meta)}` } else { // 普通元数据,可以适当限制 metaStr = ` ${safeStringify(meta, 5000)}` } } if (appConfig.logging.pretty) { // 开发环境:带颜色的格式 const colors: Record = { DEBUG: '\x1b[36m', // Cyan INFO: '\x1b[32m', // Green WARN: '\x1b[33m', // Yellow ERROR: '\x1b[31m' // Red } const reset = '\x1b[0m' const color = colors[level] || '' return `${color}[${timestamp}] ${level}${reset}: ${message}${metaStr}` } else { // 生产环境:JSON 格式 return JSON.stringify({ timestamp, level, message, ...meta }) } } /** * Logger 类 */ class Logger { private context?: string constructor(context?: string) { this.context = context } /** * 添加上下文信息 */ private addContext(meta?: any): any { if (!this.context) return meta return { ...meta, context: this.context } } private shouldEmit(level: LogLevel, meta?: any): boolean { if (!prodSummaryLogOnly) { return true } // In production summary-only mode, always keep warnings/errors visible. if (level >= LogLevel.WARN) { return true } return meta && typeof meta === 'object' && meta._logType === 'job_summary' } /** * 调试日志 */ debug(message: string, meta?: any): void { const contextMeta = this.addContext(meta) if (currentLogLevel <= LogLevel.DEBUG && this.shouldEmit(LogLevel.DEBUG, contextMeta)) { console.debug(formatMessage('DEBUG', message, contextMeta)) } } /** * 信息日志 */ info(message: string, meta?: any): void { const contextMeta = this.addContext(meta) if (currentLogLevel <= LogLevel.INFO && this.shouldEmit(LogLevel.INFO, contextMeta)) { console.log(formatMessage('INFO', message, contextMeta)) } } /** * 警告日志 */ warn(message: string, meta?: any): void { const contextMeta = this.addContext(meta) if (currentLogLevel <= LogLevel.WARN && this.shouldEmit(LogLevel.WARN, contextMeta)) { console.warn(formatMessage('WARN', message, contextMeta)) } } /** * 错误日志 */ error(message: string, meta?: any): void { const contextMeta = this.addContext(meta) if (currentLogLevel <= LogLevel.ERROR && this.shouldEmit(LogLevel.ERROR, contextMeta)) { console.error(formatMessage('ERROR', message, contextMeta)) } } /** * 创建子 logger 带上下文 */ child(context: string): Logger { const childContext = this.context ? `${this.context}:${context}` : context return new Logger(childContext) } } /** * 默认 logger 实例 */ export const logger = new Logger() /** * 创建带上下文的 logger */ export function createLogger(context: string): Logger { return new Logger(context) }