feat: 集成阿里云日志服务(SLS)并增强日志记录详情

- 添加 SLS 日志上报模块(sls-logger.js)
  - 支持批量上报(每10条或5秒间隔)
  - 环境变量缺失时静默降级
  - 自动重试失败的日志

- 新增日志信息提取器(log-extractor.js)
  - 提取 Token 使用统计(input_tokens, output_tokens)
  - 提取用户标识信息(user_id, session_id, ip)
  - 提取请求参数(temperature, max_tokens, stream)
  - 提取消息摘要(message_count, role_distribution, tool_names)

- 增强所有 API 端点的日志记录
  - /v1/chat/completions
  - /v1/responses
  - /v1/messages
  - /v1/messages/count_tokens

- 修复日志字段序列化问题
  - 扁平化嵌套对象字段,避免 [object Object]
  - 数组字段转换为逗号分隔字符串

- 添加阿里云环境变量配置到 docker-compose.yml
  - ALIYUN_ACCESS_KEY_ID
  - ALIYUN_ACCESS_KEY_SECRET
  - ALIYUN_SLS_ENDPOINT
  - ALIYUN_SLS_PROJECT
  - ALIYUN_SLS_LOGSTORE

- 修改认证配置为自动刷新 Token 机制
  - 使用 DROID_REFRESH_KEY 替代固定的 FACTORY_API_KEY
  - 实现每6小时自动刷新(Token有效期8小时)
  - Token 持久化到 auth.json
This commit is contained in:
Claude Code
2025-12-27 04:42:43 +00:00
parent eb1096ce54
commit 82a5a2cdfb
5 changed files with 336 additions and 37 deletions

117
routes.js
View File

@@ -10,6 +10,7 @@ import { OpenAIResponseTransformer } from './transformers/response-openai.js';
import { getApiKey } from './auth.js';
import { getNextProxyAgent } from './proxy-manager.js';
import { logRequest as slsLogRequest } from './sls-logger.js';
import { buildDetailedLog } from './log-extractor.js';
const router = express.Router();
@@ -189,7 +190,14 @@ async function handleChatCompletions(req, res) {
}
res.end();
logInfo('Stream forwarded (common type)');
slsLogRequest({ method: 'POST', endpoint: '/v1/chat/completions', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/chat/completions',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req
}));
} catch (streamError) {
logError('Stream error', streamError);
res.end();
@@ -209,7 +217,14 @@ async function handleChatCompletions(req, res) {
}
res.end();
logInfo('Stream completed');
slsLogRequest({ method: 'POST', endpoint: '/v1/chat/completions', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/chat/completions',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req
}));
} catch (streamError) {
logError('Stream error', streamError);
res.end();
@@ -232,12 +247,28 @@ async function handleChatCompletions(req, res) {
logResponse(200, null, data);
res.json(data);
}
slsLogRequest({ method: 'POST', endpoint: '/v1/chat/completions', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/chat/completions',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req,
responseData: data
}));
}
} catch (error) {
logError('Error in /v1/chat/completions', error);
slsLogRequest({ method: 'POST', endpoint: '/v1/chat/completions', model: req.body?.model, status: 500, duration_ms: Date.now() - startTime, error: error.message });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/chat/completions',
model: req.body?.model,
status: 500,
duration_ms: Date.now() - startTime,
req,
error: error.message
}));
res.status(500).json({
error: 'Internal server error',
message: error.message
@@ -394,7 +425,14 @@ async function handleDirectResponses(req, res) {
}
res.end();
logInfo('Stream forwarded successfully');
slsLogRequest({ method: 'POST', endpoint: '/v1/responses', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/responses',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req
}));
} catch (streamError) {
logError('Stream error', streamError);
res.end();
@@ -404,12 +442,28 @@ async function handleDirectResponses(req, res) {
const data = await response.json();
logResponse(200, null, data);
res.json(data);
slsLogRequest({ method: 'POST', endpoint: '/v1/responses', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/responses',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req,
responseData: data
}));
}
} catch (error) {
logError('Error in /v1/responses', error);
slsLogRequest({ method: 'POST', endpoint: '/v1/responses', model: req.body?.model, status: 500, duration_ms: Date.now() - startTime, error: error.message });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/responses',
model: req.body?.model,
status: 500,
duration_ms: Date.now() - startTime,
req,
error: error.message
}));
res.status(500).json({
error: 'Internal server error',
message: error.message
@@ -631,7 +685,14 @@ async function handleDirectMessages(req, res) {
}
res.end();
logInfo('Stream forwarded successfully');
slsLogRequest({ method: 'POST', endpoint: '/v1/messages', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/messages',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req
}));
} catch (streamError) {
logError('Stream error', streamError);
res.end();
@@ -641,12 +702,28 @@ async function handleDirectMessages(req, res) {
const data = await response.json();
logResponse(200, null, data);
res.json(data);
slsLogRequest({ method: 'POST', endpoint: '/v1/messages', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/messages',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req,
responseData: data
}));
}
} catch (error) {
logError('Error in /v1/messages', error);
slsLogRequest({ method: 'POST', endpoint: '/v1/messages', model: req.body?.model, status: 500, duration_ms: Date.now() - startTime, error: error.message });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/messages',
model: req.body?.model,
status: 500,
duration_ms: Date.now() - startTime,
req,
error: error.message
}));
res.status(500).json({
error: 'Internal server error',
message: error.message
@@ -743,11 +820,27 @@ async function handleCountTokens(req, res) {
const data = await response.json();
logResponse(200, null, data);
res.json(data);
slsLogRequest({ method: 'POST', endpoint: '/v1/messages/count_tokens', model: modelId, status: 200, duration_ms: Date.now() - startTime });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/messages/count_tokens',
model: modelId,
status: 200,
duration_ms: Date.now() - startTime,
req,
responseData: data
}));
} catch (error) {
logError('Error in /v1/messages/count_tokens', error);
slsLogRequest({ method: 'POST', endpoint: '/v1/messages/count_tokens', model: req.body?.model, status: 500, duration_ms: Date.now() - startTime, error: error.message });
slsLogRequest(buildDetailedLog({
method: 'POST',
endpoint: '/v1/messages/count_tokens',
model: req.body?.model,
status: 500,
duration_ms: Date.now() - startTime,
req,
error: error.message
}));
res.status(500).json({
error: 'Internal server error',
message: error.message