From 77d8a4fdc3b2b1a2f7a74e07ea98c98b4f85ec2a Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 29 Jan 2026 22:01:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(logger):=20=E5=9C=A8=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=B8=AD=E6=B7=BB=E5=8A=A0=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/index.js | 4 +++- src/utils/logger.js | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/server/index.js b/src/server/index.js index 6a86e7c3..ce10aabd 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -83,7 +83,9 @@ 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; + logger.request(req.method, fullPath, res.statusCode, Date.now() - start, model); }); } next(); diff --git a/src/utils/logger.js b/src/utils/logger.js index 9da6cda9..b1a2a752 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -42,12 +42,13 @@ function logMessage(level, ...args) { logWsServer.storeLog(level, message); } -function logRequest(method, path, status, duration) { +function logRequest(method, path, status, duration, model) { const statusColor = status >= 500 ? colors.red : status >= 400 ? colors.yellow : colors.green; - const message = `[${method}] - ${path} ${status} ${duration}ms`; + const modelInfo = model ? ` [${model}]` : ''; + const message = `[${method}] - ${path}${modelInfo} ${status} ${duration}ms`; // 输出到控制台 - 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}`); // 存储日志(根据状态码决定级别) const level = status >= 500 ? 'error' : status >= 400 ? 'warn' : 'request'; From 039f8fc901f95538599ad4efe9abeeb4cddfa140 Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 29 Jan 2026 22:30:38 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(logging):=20=E5=9C=A8=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E6=97=A5=E5=BF=97=E4=B8=AD=E6=B7=BB=E5=8A=A0=20token?= =?UTF-8?q?=20=E6=95=B0=E9=87=8F=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/handlers/claude.js | 17 +++++++++++++++++ src/server/handlers/gemini.js | 17 +++++++++++++++++ src/server/handlers/geminicli.js | 16 ++++++++++++++++ src/server/handlers/openai.js | 14 ++++++++++++++ src/server/index.js | 4 +++- src/utils/logger.js | 7 ++++--- 6 files changed, 71 insertions(+), 4 deletions(-) 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 ce10aabd..c3353282 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -85,7 +85,9 @@ app.use((req, res, next) => { res.on('finish', () => { // 从请求体或 URL 参数中获取模型名称 const model = req.body?.model || req.params?.model || null; - logger.request(req.method, fullPath, res.statusCode, Date.now() - start, model); + // 从响应对象中获取 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 b1a2a752..847c0d18 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -42,13 +42,14 @@ function logMessage(level, ...args) { logWsServer.storeLog(level, message); } -function logRequest(method, path, status, duration, model) { +function logRequest(method, path, status, duration, model, tokens) { const statusColor = status >= 500 ? colors.red : status >= 400 ? colors.yellow : colors.green; const modelInfo = model ? ` [${model}]` : ''; - const message = `[${method}] - ${path}${modelInfo} ${status} ${duration}ms`; + const tokensInfo = tokens ? ` tokens:${tokens}` : ''; + const message = `[${method}] - ${path}${modelInfo} ${status} ${duration}ms${tokensInfo}`; // 输出到控制台 - console.log(`${colors.cyan}[${method}]${colors.reset} - ${path}${colors.cyan}${modelInfo}${colors.reset} ${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';