/** * 阿里云日志服务(SLS)日志模块 * * 功能: * - 将 API 请求/响应日志上报到阿里云 SLS * - 批量上报,减少 API 调用 * - 环境变量缺失时静默降级 */ import ALSClient from 'aliyun-log'; // SLS 配置 const SLS_CONFIG = { accessKeyId: process.env.ALIYUN_ACCESS_KEY_ID, accessKeySecret: process.env.ALIYUN_ACCESS_KEY_SECRET, endpoint: process.env.ALIYUN_SLS_ENDPOINT, project: process.env.ALIYUN_SLS_PROJECT, logstore: process.env.ALIYUN_SLS_LOGSTORE }; // 检查配置是否完整 const isConfigured = Object.values(SLS_CONFIG).every(v => v); let client = null; let logQueue = []; const BATCH_SIZE = 10; const FLUSH_INTERVAL_MS = 5000; // 初始化 SLS Client function initClient() { if (!isConfigured) { console.warn('[SLS] 阿里云日志服务未配置,日志将仅输出到控制台'); return null; } try { client = new ALSClient({ accessKeyId: SLS_CONFIG.accessKeyId, accessKeySecret: SLS_CONFIG.accessKeySecret, endpoint: SLS_CONFIG.endpoint }); console.log('[SLS] 阿里云日志服务客户端初始化成功'); return client; } catch (error) { console.error('[SLS] 初始化失败:', error.message); return null; } } // 刷新日志队列 async function flushLogs() { if (!client || logQueue.length === 0) return; const logsToSend = logQueue.splice(0, BATCH_SIZE); try { const logs = logsToSend.map(log => ({ timestamp: Math.floor(Date.now() / 1000), content: Object.fromEntries( Object.entries(log).map(([key, value]) => [key, String(value ?? '')]) ) })); await client.postLogStoreLogs(SLS_CONFIG.project, SLS_CONFIG.logstore, { logs }); console.log(`[SLS] 成功上报 ${logsToSend.length} 条日志`); } catch (error) { console.error('[SLS] 日志上报失败:', error.message); // 失败的日志重新入队(可选:限制重试次数) logQueue.unshift(...logsToSend); } } // 定时刷新 let flushTimer = null; function startFlushTimer() { if (flushTimer || !isConfigured) return; flushTimer = setInterval(flushLogs, FLUSH_INTERVAL_MS); } /** * 记录 API 请求日志 * @param {Object} logData - 日志数据 * @param {string} logData.method - HTTP 方法 * @param {string} logData.endpoint - 请求路径 * @param {string} logData.model - 模型 ID * @param {number} logData.status - 响应状态码 * @param {number} logData.duration_ms - 请求耗时 * @param {number} [logData.input_tokens] - 输入 Token 数 * @param {number} [logData.output_tokens] - 输出 Token 数 * @param {string} [logData.error] - 错误信息 */ export function logRequest(logData) { const enrichedLog = { timestamp: new Date().toISOString(), ...logData }; // 始终输出到控制台 console.log('[SLS]', JSON.stringify(enrichedLog)); if (!isConfigured) return; logQueue.push(enrichedLog); // 队列满时立即刷新 if (logQueue.length >= BATCH_SIZE) { flushLogs(); } } /** * 优雅关闭,刷新剩余日志 */ export async function shutdown() { if (flushTimer) { clearInterval(flushTimer); flushTimer = null; } await flushLogs(); console.log('[SLS] 已关闭'); } // 初始化 initClient(); startFlushTimer(); // 进程退出时优雅关闭 process.on('SIGTERM', shutdown); process.on('SIGINT', shutdown); export default { logRequest, shutdown };