diff --git a/src/server/handlers/claude.js b/src/server/handlers/claude.js index ea21036e..0fea3147 100644 --- a/src/server/handlers/claude.js +++ b/src/server/handlers/claude.js @@ -302,6 +302,11 @@ export const handleClaudeRequest = async (req, res, isStream) => { type: "message_stop" })); + // 存储 token 数量到 res.locals 供日志使用 + if (usageData) { + res.locals.tokens = usageData.total_tokens || usageData.completion_tokens; + } + clearInterval(heartbeatTimer); res.end(); } catch (error) { @@ -346,6 +351,12 @@ export const handleClaudeRequest = async (req, res, isStream) => { ); const stopReason = toolCalls.length > 0 ? 'tool_use' : 'end_turn'; + + // 存储 token 数量到 res.locals 供日志使用 + if (usageData) { + res.locals.tokens = usageData.total_tokens || usageData.completion_tokens; + } + const response = createClaudeResponse( msgId, model, @@ -377,6 +388,12 @@ export const handleClaudeRequest = async (req, res, isStream) => { ); const stopReason = toolCalls.length > 0 ? 'tool_use' : 'end_turn'; + + // 存储 token 数量到 res.locals 供日志使用 + if (usage) { + res.locals.tokens = usage.total_tokens || usage.completion_tokens; + } + const response = createClaudeResponse( msgId, model, diff --git a/src/server/handlers/gemini.js b/src/server/handlers/gemini.js index 9f48d818..e22d4316 100644 --- a/src/server/handlers/gemini.js +++ b/src/server/handlers/gemini.js @@ -190,6 +190,11 @@ export const handleGeminiRequest = async (req, res, modelName, isStream) => { const finalChunk = createGeminiResponse(null, null, null, null, finishReason, usageData, { passSignatureToClient: config.passSignatureToClient }); writeStreamData(res, finalChunk); + // 存储 token 数量到 res.locals 供日志使用 + if (usageData) { + res.locals.tokens = usageData.total_tokens || usageData.completion_tokens; + } + clearInterval(heartbeatTimer); endStream(res, false); } catch (error) { @@ -234,6 +239,12 @@ export const handleGeminiRequest = async (req, res, modelName, isStream) => { ); const finishReason = "STOP"; + + // 存储 token 数量到 res.locals 供日志使用 + if (usageData) { + res.locals.tokens = usageData.total_tokens || usageData.completion_tokens; + } + const response = createGeminiResponse(content, reasoningContent || null, reasoningSignature, toolCalls, finishReason, usageData, { passSignatureToClient: config.passSignatureToClient }); res.json(response); } catch (error) { @@ -254,6 +265,12 @@ export const handleGeminiRequest = async (req, res, modelName, isStream) => { ); const finishReason = toolCalls.length > 0 ? "STOP" : "STOP"; + + // 存储 token 数量到 res.locals 供日志使用 + if (usage) { + res.locals.tokens = usage.total_tokens || usage.completion_tokens; + } + const response = createGeminiResponse(content, reasoningContent, reasoningSignature, toolCalls, finishReason, usage, { passSignatureToClient: config.passSignatureToClient }); res.json(response); } diff --git a/src/server/handlers/geminicli.js b/src/server/handlers/geminicli.js index aee8e2eb..92c72fc4 100644 --- a/src/server/handlers/geminicli.js +++ b/src/server/handlers/geminicli.js @@ -91,6 +91,12 @@ export const handleGeminiCliRequest = async (req, res, forceFormat = null) => { writer.finalize(); + // 存储 token 数量到 res.locals 供日志使用 + const usageData = writer.getUsageData(); + if (usageData) { + res.locals.tokens = usageData.total_tokens || usageData.completion_tokens; + } + clearInterval(heartbeatTimer); endStream(res, false); } catch (error) { @@ -138,6 +144,11 @@ export const handleGeminiCliRequest = async (req, res, forceFormat = null) => { usage }); + // 存储 token 数量到 res.locals 供日志使用 + if (usage) { + res.locals.tokens = usage.total_tokens || usage.completion_tokens; + } + clearInterval(heartbeatTimer); endStream(res, false); } catch (error) { @@ -186,6 +197,11 @@ export const handleGeminiCliRequest = async (req, res, forceFormat = null) => { } } + // 存储 token 数量到 res.locals 供日志使用 + if (usage) { + res.locals.tokens = usage.total_tokens || usage.completion_tokens; + } + // 根据请求格式返回相应格式的响应 if (format === 'gemini') { res.json(createGeminiResponse( diff --git a/src/server/handlers/openai.js b/src/server/handlers/openai.js index 8534fa8f..fa5df270 100644 --- a/src/server/handlers/openai.js +++ b/src/server/handlers/openai.js @@ -133,6 +133,10 @@ export const handleOpenAIRequest = async (req, res) => { ); writeStreamData(res, { ...createStreamChunk(id, created, model, {}, hasToolCall ? 'tool_calls' : 'stop'), usage: usageData }); + // 存储 token 数量到 res.locals 供日志使用 + if (usageData) { + res.locals.tokens = usageData.total_tokens || usageData.completion_tokens; + } } clearInterval(heartbeatTimer); @@ -192,6 +196,11 @@ export const handleOpenAIRequest = async (req, res) => { } } + // 存储 token 数量到 res.locals 供日志使用 + if (usageData) { + res.locals.tokens = usageData.total_tokens || usageData.completion_tokens; + } + res.json(createOpenAIChatCompletionResponse({ id, created, @@ -236,6 +245,11 @@ export const handleOpenAIRequest = async (req, res) => { } } + // 存储 token 数量到 res.locals 供日志使用 + if (usage) { + res.locals.tokens = usage.total_tokens || usage.completion_tokens; + } + // 使用预构建的响应对象,减少内存分配 res.json(createOpenAIChatCompletionResponse({ id, diff --git a/src/server/index.js b/src/server/index.js index 6a86e7c3..c3353282 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -83,7 +83,11 @@ app.use((req, res, next) => { if (!ignorePaths.some(p => fullPath.startsWith(p))) { const start = Date.now(); res.on('finish', () => { - logger.request(req.method, fullPath, res.statusCode, Date.now() - start); + // 从请求体或 URL 参数中获取模型名称 + const model = req.body?.model || req.params?.model || null; + // 从响应对象中获取 token 数量 + const tokens = res.locals?.tokens || null; + logger.request(req.method, fullPath, res.statusCode, Date.now() - start, model, tokens); }); } next(); diff --git a/src/utils/logger.js b/src/utils/logger.js index 9da6cda9..847c0d18 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -42,12 +42,14 @@ function logMessage(level, ...args) { logWsServer.storeLog(level, message); } -function logRequest(method, path, status, duration) { +function logRequest(method, path, status, duration, model, tokens) { const statusColor = status >= 500 ? colors.red : status >= 400 ? colors.yellow : colors.green; - const message = `[${method}] - ${path} ${status} ${duration}ms`; + const modelInfo = model ? ` [${model}]` : ''; + const tokensInfo = tokens ? ` tokens:${tokens}` : ''; + const message = `[${method}] - ${path}${modelInfo} ${status} ${duration}ms${tokensInfo}`; // 输出到控制台 - console.log(`${colors.cyan}[${method}]${colors.reset} - ${path} ${statusColor}${status}${colors.reset} ${colors.gray}${duration}ms${colors.reset}`); + console.log(`${colors.cyan}[${method}]${colors.reset} - ${path}${colors.cyan}${modelInfo}${colors.reset} ${statusColor}${status}${colors.reset} ${colors.gray}${duration}ms${colors.reset}${colors.yellow}${tokensInfo}${colors.reset}`); // 存储日志(根据状态码决定级别) const level = status >= 500 ? 'error' : status >= 400 ? 'warn' : 'request';