|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Logger { |
|
|
constructor() { |
|
|
this.requestCounter = 0 |
|
|
this.activeRequests = new Map() |
|
|
|
|
|
|
|
|
this.LOG_LEVELS = { |
|
|
ERROR: 0, |
|
|
WARN: 1, |
|
|
INFO: 2, |
|
|
DEBUG: 3 |
|
|
} |
|
|
|
|
|
|
|
|
this.currentLevel = this.LOG_LEVELS[process.env.LOG_LEVEL?.toUpperCase()] || this.LOG_LEVELS.INFO |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
generateRequestId() { |
|
|
this.requestCounter++ |
|
|
return `req_${Date.now()}_${this.requestCounter.toString().padStart(4, '0')}` |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getTimestamp() { |
|
|
const now = new Date() |
|
|
|
|
|
const beijingTime = new Date(now.getTime() + (8 * 60 * 60 * 1000)) |
|
|
return beijingTime.toISOString().replace('T', ' ').replace('Z', '').substring(0, 19) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log(level, category, event, message, data = {}) { |
|
|
const levelValue = this.LOG_LEVELS[level.toUpperCase()] |
|
|
if (levelValue > this.currentLevel) { |
|
|
return |
|
|
} |
|
|
|
|
|
|
|
|
const logEntry = { |
|
|
时间: this.getTimestamp(), |
|
|
级别: this.translateLevel(level.toUpperCase()), |
|
|
分类: this.translateCategory(category), |
|
|
事件: event, |
|
|
消息: message, |
|
|
...data |
|
|
} |
|
|
|
|
|
const logString = JSON.stringify(logEntry) |
|
|
|
|
|
switch (level.toUpperCase()) { |
|
|
case 'ERROR': |
|
|
console.error(logString) |
|
|
break |
|
|
case 'WARN': |
|
|
console.warn(logString) |
|
|
break |
|
|
case 'DEBUG': |
|
|
console.debug(logString) |
|
|
break |
|
|
default: |
|
|
console.log(logString) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
translateLevel(level) { |
|
|
const levelMap = { |
|
|
'ERROR': '错误', |
|
|
'WARN': '警告', |
|
|
'INFO': '信息', |
|
|
'DEBUG': '调试' |
|
|
} |
|
|
return levelMap[level] || level |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
translateCategory(category) { |
|
|
const categoryMap = { |
|
|
'APPLICATION': '应用', |
|
|
'REQUEST': '请求', |
|
|
'SYSTEM': '系统', |
|
|
'MODEL': '模型', |
|
|
'IMAGE': '图片' |
|
|
} |
|
|
return categoryMap[category] || category |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logInfo(event, message, data = {}) { |
|
|
this.log('INFO', 'APPLICATION', event, message, data) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logWarn(event, message, data = {}) { |
|
|
this.log('WARN', 'APPLICATION', event, message, data) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logDebug(event, message, data = {}) { |
|
|
this.log('DEBUG', 'APPLICATION', event, message, data) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logRequestStart(method, url, headers = {}, body = {}) { |
|
|
const requestId = this.generateRequestId() |
|
|
|
|
|
|
|
|
this.activeRequests.set(requestId, { |
|
|
startTime: Date.now(), |
|
|
requestId, |
|
|
method, |
|
|
url, |
|
|
userAgent: headers['user-agent'] || 'unknown', |
|
|
contentType: headers['content-type'] || 'unknown', |
|
|
contentLength: headers['content-length'] || 'unknown', |
|
|
model: body.model || 'unknown', |
|
|
messageCount: Array.isArray(body.messages) ? body.messages.length : 0, |
|
|
hasImages: this.detectImages(body.messages), |
|
|
isStream: body.stream === true |
|
|
}) |
|
|
|
|
|
|
|
|
this.log('INFO', 'REQUEST', '请求开始', '收到新的API请求', { |
|
|
请求ID: requestId, |
|
|
方法: method, |
|
|
路径: url, |
|
|
用户代理: headers['user-agent'] || '未知', |
|
|
内容类型: headers['content-type'] || '未知', |
|
|
内容长度: headers['content-length'] || '未知', |
|
|
模型: body.model || '未知', |
|
|
消息数量: Array.isArray(body.messages) ? body.messages.length : 0, |
|
|
包含图片: this.detectImages(body.messages), |
|
|
流式请求: body.stream === true |
|
|
}) |
|
|
|
|
|
return requestId |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logRequestEnd(requestId, statusCode, responseInfo = {}) { |
|
|
const activeRequest = this.activeRequests.get(requestId) |
|
|
|
|
|
if (!activeRequest) { |
|
|
this.logWarn('请求结束', `未找到请求ID: ${requestId}`) |
|
|
return |
|
|
} |
|
|
|
|
|
const duration = Date.now() - activeRequest.startTime |
|
|
const isSuccess = statusCode >= 200 && statusCode < 300 |
|
|
|
|
|
|
|
|
this.log('INFO', 'REQUEST', '请求结束', '请求处理完成', { |
|
|
请求ID: requestId, |
|
|
耗时毫秒: duration, |
|
|
状态码: statusCode, |
|
|
处理成功: isSuccess, |
|
|
模型: activeRequest.model, |
|
|
流式请求: activeRequest.isStream, |
|
|
...responseInfo |
|
|
}) |
|
|
|
|
|
|
|
|
this.activeRequests.delete(requestId) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logImageProcessingStart(requestId, imageCount) { |
|
|
this.log('INFO', 'IMAGE', '图片处理开始', '开始处理上传的图片', { |
|
|
请求ID: requestId, |
|
|
图片数量: imageCount |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logLongImageDetection(requestId, imageIndex, detection) { |
|
|
this.log('INFO', 'IMAGE', '长图检测', '完成图片长度检测', { |
|
|
请求ID: requestId, |
|
|
图片索引: imageIndex, |
|
|
是否长图: detection.isLongImage ? '是' : '否', |
|
|
图片尺寸: `${detection.width}x${detection.height}`, |
|
|
高宽比: detection.ratio?.toFixed(2), |
|
|
检测阈值: detection.threshold |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logImageCropping(requestId, imageIndex, cropResult) { |
|
|
const timestamp = this.getTimestamp() |
|
|
const stats = cropResult.stats || {} |
|
|
|
|
|
const logData = { |
|
|
请求ID: requestId, |
|
|
时间: timestamp, |
|
|
事件: '图片切割', |
|
|
图片索引: imageIndex, |
|
|
切割片段数: stats.totalSegments || 0, |
|
|
原始尺寸: stats.originalDimensions, |
|
|
实际片段数: cropResult.segments?.length || 0 |
|
|
} |
|
|
|
|
|
console.log(`[图片处理] ${JSON.stringify(logData)}`) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logMessageSegmentation(requestId, imageIndex, segmentCount) { |
|
|
const timestamp = this.getTimestamp() |
|
|
const logData = { |
|
|
请求ID: requestId, |
|
|
时间: timestamp, |
|
|
事件: '消息分割', |
|
|
图片索引: imageIndex, |
|
|
分割消息数: segmentCount |
|
|
} |
|
|
|
|
|
console.log(`[消息处理] ${JSON.stringify(logData)}`) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logImageUpload(requestId, imageIndex, segmentIndex, success, url = null, error = null) { |
|
|
const timestamp = this.getTimestamp() |
|
|
const logData = { |
|
|
请求ID: requestId, |
|
|
时间: timestamp, |
|
|
事件: '图片上传', |
|
|
图片索引: imageIndex, |
|
|
片段索引: segmentIndex, |
|
|
成功: success ? '是' : '否', |
|
|
上传地址: success ? url : null, |
|
|
错误信息: success ? null : error |
|
|
} |
|
|
|
|
|
console.log(`[图片处理] ${JSON.stringify(logData)}`) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logModelCallStart(requestId, model, mammouthModel) { |
|
|
this.log('INFO', 'MODEL', '模型调用开始', '开始调用AI模型', { |
|
|
请求ID: requestId, |
|
|
请求模型: model, |
|
|
实际模型: mammouthModel |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logModelCallEnd(requestId, success, error = null, duration = null) { |
|
|
this.log(success ? 'INFO' : 'ERROR', 'MODEL', '模型调用结束', |
|
|
success ? '模型调用成功' : '模型调用失败', { |
|
|
请求ID: requestId, |
|
|
调用成功: success ? '是' : '否', |
|
|
错误信息: success ? null : error, |
|
|
耗时毫秒: duration |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logError(requestId, errorType, errorMessage, errorDetails = {}) { |
|
|
this.log('ERROR', 'APPLICATION', errorType, errorMessage, { |
|
|
requestId, |
|
|
...errorDetails |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logGlobalError(errorType, errorMessage, errorDetails = {}) { |
|
|
this.log('ERROR', 'SYSTEM', errorType, errorMessage, errorDetails) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logPerformanceMetrics(requestId, metrics) { |
|
|
const timestamp = this.getTimestamp() |
|
|
const logData = { |
|
|
requestId, |
|
|
timestamp, |
|
|
event: 'PERFORMANCE_METRICS', |
|
|
...metrics |
|
|
} |
|
|
|
|
|
console.log(`[PERFORMANCE] ${JSON.stringify(logData)}`) |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
detectImages(messages) { |
|
|
if (!Array.isArray(messages)) { |
|
|
console.log(`[图片检测] 消息不是数组: ${typeof messages}`) |
|
|
return false |
|
|
} |
|
|
|
|
|
let hasImages = false |
|
|
let imageCount = 0 |
|
|
|
|
|
messages.forEach((message, index) => { |
|
|
if (Array.isArray(message.content)) { |
|
|
const imagePartsCount = message.content.filter(part => part.type === 'image_url').length |
|
|
if (imagePartsCount > 0) { |
|
|
hasImages = true |
|
|
imageCount += imagePartsCount |
|
|
console.log(`[图片检测] 消息${index}包含${imagePartsCount}张图片`) |
|
|
} |
|
|
} else if (typeof message.content === 'string') { |
|
|
console.log(`[图片检测] 消息${index}是纯文本`) |
|
|
} else { |
|
|
console.log(`[图片检测] 消息${index}内容类型: ${typeof message.content}`) |
|
|
} |
|
|
}) |
|
|
|
|
|
console.log(`[图片检测] 总计检测到${imageCount}张图片`) |
|
|
return hasImages |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getActiveRequestsStats() { |
|
|
return { |
|
|
count: this.activeRequests.size, |
|
|
requests: Array.from(this.activeRequests.entries()).map(([id, req]) => ({ |
|
|
requestId: id, |
|
|
duration: Date.now() - req.startTime, |
|
|
model: req.model, |
|
|
isStream: req.isStream |
|
|
})) |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
module.exports = new Logger() |
|
|
|