# All files / modules/tts tts-logger.ts

0% Statements 0/101
0% Branches 0/1
0% Functions 0/1
0% Lines 0/101

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143

     /**
     * TTS 模块专用日志
     *
     * 解决分散日志问题：
     * 1. console.log / logToFile 混用 → 统一到 logger
     * 2. tts-debug.log 只在本地开发有效 → 统一到 logs/tts.log
     * 3. 没有按 provider 分类 → 按 name 字段区分
     */

    // 简易 console logger（logger.service 暂未创建）
    const logger = {
      info: (msg: string) => console.log(msg),
      warn: (msg: string) => console.warn(msg),
      error: (msg: string) => console.error(msg),
      debug: (msg: string) => console.debug(msg),
    };
    import path from 'path';
    import fs from 'fs';

    const LOG_FILE = path.join(process.cwd(), 'logs', 'tts.log');

    function ensureLogDir() {
      const dir = path.dirname(LOG_FILE);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
    }

    function writeToFile(msg: string) {
      const timestamp = new Date().toISOString();
      const line = `[${timestamp}] ${msg}\n`;
      fs.appendFileSync(LOG_FILE, line, 'utf-8');
    }

    export const ttsLogger = {
      /** Provider 注册/注销 */
      registry(action: 'register' | 'unregister' | 'skip', providerName: string, detail?: string) {
        const msg = `[Registry] ${action === 'skip' ? '跳过' : action === 'register' ? '注册' : '注销'} ${providerName}${detail ? `: ${detail}` : ''}`;
        if (action === 'skip') {
          logger.warn(msg);
        } else {
          logger.info(msg);
        }
        writeToFile(msg);
      },

      /** 熔断器状态变化 */
      circuitBreaker(providerName: string, state: 'open' | 'half_open' | 'closed', detail?: string) {
        const icons = { open: '🔴', half_open: '🟡', closed: '🟢' };
        const msg = `[CircuitBreaker] ${icons[state]} ${providerName} 熔断器: ${state}${detail ? ` (${detail})` : ''}`;
        logger.warn(msg);
        writeToFile(msg);
      },

      /** 额度耗尽/恢复 */
      quota(providerName: string, action: 'exhausted' | 'recovered', reason?: string) {
        const msg = `[Quota] ${action === 'exhausted' ? '⚠️ 额度耗尽' : '✅ 额度恢复'} ${providerName}${reason ? `: ${reason}` : ''}`;
        if (action === 'exhausted') {
          logger.warn(msg);
        } else {
          logger.info(msg);
        }
        writeToFile(msg);
      },

      /** 音频生成开始 */
      synthesisStart(audioId: string, providerName: string, textLength: number, segmentCount: number) {
        const msg = `[Synth] 📝 开始合成 audioId=${audioId}, provider=${providerName}, 文本=${textLength}字, 分段=${segmentCount}`;
        logger.info(msg);
        writeToFile(msg);
      },

      /** 单段合成完成 */
      segmentDone(audioId: string, providerName: string, segmentIndex: number, filePath: string, ms: number) {
        const msg = `[Synth]   段落[${segmentIndex}] ✅ ${providerName} ${ms}ms → ${path.basename(filePath)}`;
        logger.debug(msg);
        writeToFile(msg);
      },

      /** 音频生成完成 */
      synthesisDone(audioId: string, providerName: string, duration: number, audioUrl: string, ms: number) {
        const msg = `[Synth] ✅ 完成 audioId=${audioId}, provider=${providerName}, 时长=${duration}s, 耗时=${(ms / 1000).toFixed(1)}s, url=${audioUrl.substring(0, 60)}...`;
        logger.info(msg);
        writeToFile(msg);
      },

      /** 音频生成失败 */
      synthesisFailed(audioId: string, providerName: string, error: string, willRetry: boolean) {
        const msg = `[Synth] ❌ 失败 audioId=${audioId}, provider=${providerName}, error=${error.substring(0, 100)}${willRetry ? ' (将重试)' : ' (不重试)'}`;
        logger.error(msg);
        writeToFile(msg);
      },

      /** Provider 降级切换 */
      fallback(fromProvider: string, toProvider: string, reason: string) {
        const msg = `[Fallback] 🔄 ${fromProvider} → ${toProvider}: ${reason}`;
        logger.warn(msg);
        writeToFile(msg);
      },

      /** 健康检查 */
      healthCheck(providerName: string, state: 'recovered' | 'still_down', detail?: string) {
        const msg = `[HealthCheck] ${state === 'recovered' ? '✅' : '❌'} ${providerName}${detail ? `: ${detail}` : ''}`;
        if (state === 'recovered') {
          logger.info(msg);
        } else {
          logger.warn(msg);
        }
        writeToFile(msg);
      },

      /** 错误日志 */
      error(label: string, error: string | Error, context?: Record<string, any>) {
        const errorMsg = error instanceof Error ? error.message : error;
        const msg = `[Error] ${label}: ${errorMsg}${context ? ` | ${JSON.stringify(context)}` : ''}`;
        logger.error(msg);
        writeToFile(msg);
      },

      /** 通用信息 */
      info(message: string) {
        logger.info(`[TTS] ${message}`);
        writeToFile(`[Info] ${message}`);
      },

      /** 通用警告 */
      warn(message: string) {
        logger.warn(`[TTS] ${message}`);
        writeToFile(`[Warn] ${message}`);
      },

      /** LRC 歌词生成（调试级） */
      lrc(message: string) {
        logger.debug(`[LRC] ${message}`);
        writeToFile(`[LRC] ${message}`);
      },

      /** Provider 信息（调试级） */
      debug(message: string) {
        logger.debug(`[TTS] ${message}`);
        writeToFile(`[Debug] ${message}`);
      },
    };