logger.js 2.43 KB
/**
 * 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 };