/** * Claude Stream 日志记录器 * * 支持全量消息写入文件,方便调试和问题排查 */ const fs = require('fs'); const path = require('path'); // 统一日志目录:项目根目录下的 .autodev-logs/ const DEFAULT_LOG_DIR = path.join(__dirname, '../../../../../.autodev-logs'); class StreamLogger { constructor(options = {}) { this.logDir = options.logDir || DEFAULT_LOG_DIR; this.prefix = options.prefix || 'claude-stream'; this.stream = null; this.filePath = null; this.messageCount = 0; } /** * 初始化日志文件 */ init(workDir, agentName = 'unknown') { try { // 始终使用统一的日志目录 const absLogDir = path.isAbsolute(this.logDir) ? this.logDir : DEFAULT_LOG_DIR; if (!fs.existsSync(absLogDir)) { fs.mkdirSync(absLogDir, { recursive: true }); } const timestamp = Date.now(); this.filePath = path.join(absLogDir, `${this.prefix}-${agentName}-${timestamp}.jsonl`); this.stream = fs.createWriteStream(this.filePath, { flags: 'a' }); console.log(`[StreamLogger] 日志文件: ${this.filePath}`); return this.filePath; } catch (e) { console.error('[StreamLogger] 初始化失败:', e.message); return null; } } /** * 记��原始消息(全量) */ logRaw(direction, data) { if (!this.stream) return; this.messageCount++; const record = { seq: this.messageCount, direction, // 'in' | 'out' | 'internal' timestamp: Date.now(), data }; this.stream.write(JSON.stringify(record) + '\n'); } /** * 记录入站消息 */ logIncoming(msg) { this.logRaw('in', msg); } /** * 记录出站消息 */ logOutgoing(msg) { this.logRaw('out', msg); } /** * 记录内部事件 */ logInternal(event, data) { this.logRaw('internal', { event, ...data }); } /** * 记录错误 */ logError(error) { this.logRaw('internal', { event: 'error', message: error.message, stack: error.stack }); } /** * 关闭日志 */ close() { if (this.stream) { this.logInternal('close', { messageCount: this.messageCount }); this.stream.end(); this.stream = null; } } /** * 获取统计信息 */ getStats() { return { filePath: this.filePath, messageCount: this.messageCount }; } } module.exports = { StreamLogger };