// Filename: frontend/js/pages/logs/logList.js // --- [扩展] 静态错误码与样式的映射表 (源自Gemini官方文档) --- const STATIC_ERROR_MAP = { 'API_KEY_INVALID': { type: '密钥无效', style: 'red' }, 'INVALID_ARGUMENT': { type: '参数无效', style: 'red' }, 'PERMISSION_DENIED': { type: '权限不足', style: 'red' }, 'NOT_FOUND': { type: '资源未找到', style: 'gray' }, 'RESOURCE_EXHAUSTED': { type: '资源耗尽', style: 'orange' }, 'QUOTA_EXCEEDED': { type: '配额耗尽', style: 'orange' }, 'DEADLINE_EXCEEDED': { type: '请求超时', style: 'yellow' }, 'CANCELLED': { type: '请求已取消', style: 'gray' }, 'INTERNAL': { type: 'Google内部错误', style: 'yellow' }, 'UNAVAILABLE': { type: '服务不可用', style: 'yellow' }, }; // --- [更新] HTTP状态码到类型和样式的动态映射表 --- const STATUS_CODE_MAP = { 400: { type: '错误请求', style: 'red' }, 401: { type: '认证失败', style: 'red' }, 403: { type: '禁止访问', style: 'red' }, 404: { type: '资源未找到', style: 'gray' }, 413: { type: '请求体过大', style: 'orange' }, 429: { type: '请求频率过高', style: 'orange' }, 500: { type: '内部服务错误', style: 'yellow' }, 503: { type: '服务不可用', style: 'yellow' } }; // --- [新增] 特殊场景判断规则 (高优先级) --- const SPECIAL_CASE_MAP = [ { code: 400, keyword: 'api key not found', type: '无效密钥', style: 'red' }, // 之前实现的模型配置错误规则也可以移到这里,更加规范 { code: 404, keyword: 'call listmodels', type: '模型配置错误', style: 'orange' } ]; // --- 样式名称到 Tailwind 类的转换器 --- const styleToClass = (style) => { switch (style) { case 'red': return 'bg-red-500/10 text-red-600'; case 'orange': return 'bg-orange-500/10 text-orange-600'; case 'yellow': return 'bg-yellow-500/10 text-yellow-600'; case 'gray': return 'bg-zinc-500/10 text-zinc-600'; default: return 'bg-destructive/10 text-destructive'; } }; // [修正] 修正了正则表达式的名称,使其语义清晰 const errorCodeRegex = /(\d+)$/; // [修正] 移除了 MODEL_STYLE_MAP 的声明,因为它未在 _formatModelName 中使用 // 如果未来需要,可以重新添加 // const MODEL_STYLE_MAP = { ... }; class LogList { constructor(container) { this.container = container; if (!this.container) console.error("LogList: container element (tbody) not found."); } renderLoading() { if (!this.container) return; this.container.innerHTML = ` 加载日志中...`; } render(logs, pagination) { if (!this.container) return; if (!logs || logs.length === 0) { this.container.innerHTML = `没有找到相关的日志记录。`; return; } const { page, page_size } = pagination; const startIndex = (page - 1) * page_size; const logsHtml = logs.map((log, index) => this.createLogRowHtml(log, startIndex + index + 1)).join(''); this.container.innerHTML = logsHtml; } _interpretError(log) { // 1. 成功状态 if (log.IsSuccess) { return { type: 'N/A', statusCodeHtml: `成功` }; } // 2. [新增] 特殊场景优先判断 (结合ErrorCode和ErrorMessage) const codeMatch = log.ErrorCode ? log.ErrorCode.match(errorCodeRegex) : null; if (codeMatch && codeMatch[1] && log.ErrorMessage) { const code = parseInt(codeMatch[1], 10); const lowerCaseMsg = log.ErrorMessage.toLowerCase(); for (const rule of SPECIAL_CASE_MAP) { if (code === rule.code && lowerCaseMsg.includes(rule.keyword)) { return { type: rule.type, statusCodeHtml: `${code}` }; } } } // 3. 静态错误码匹配 (例如 "INVALID_ARGUMENT") if (log.ErrorCode && STATIC_ERROR_MAP[log.ErrorCode]) { const mapping = STATIC_ERROR_MAP[log.ErrorCode]; return { type: mapping.type, statusCodeHtml: `${log.ErrorCode}` }; } // 4. 动态解析HTTP状态码 (例如 "UPSTREAM_429") if (codeMatch && codeMatch[1]) { const code = parseInt(codeMatch[1], 10); let mapping = STATUS_CODE_MAP[code]; // 为所有 5xx 错误提供降级 if (!mapping && code >= 500 && code < 600) { mapping = STATUS_CODE_MAP[500]; } if (mapping) { return { type: mapping.type, statusCodeHtml: `${code}` }; } } // 5. 边界情况: ErrorCode 和 ErrorMessage 都为空 if (!log.ErrorCode && !log.ErrorMessage) { return { type: '未知', statusCodeHtml: `N/A` }; } // 6. 最终的降级处理 return { type: '未知错误', statusCodeHtml: `失败` }; } _formatModelName(modelName) { // [修正] 移除了对 MODEL_STYLE_MAP 的依赖,简化为统一的样式 // 这样可以避免因 MODEL_STYLE_MAP 未定义而产生的潜在错误 const styleClass = ''; // 可根据需要添加回样式逻辑 return `
${modelName}
`; } createLogRowHtml(log, index) { const groupName = log.GroupDisplayName || (log.GroupID ? `Group #${log.GroupID}` : 'N/A'); const apiKeyName = log.APIKeyName || (log.KeyID ? `Key #${log.KeyID}` : 'N/A'); const errorInfo = this._interpretError(log); const modelNameFormatted = this._formatModelName(log.ModelName); const errorMessageAttr = log.ErrorMessage ? `data-error-message="${escape(log.ErrorMessage)}"` : ''; const requestTime = new Date(log.RequestTime).toLocaleString(); return ` ${index} ${apiKeyName} ${groupName} ${errorInfo.type} ${errorInfo.statusCodeHtml} ${modelNameFormatted} ${requestTime} `; } } // [核心修正] 移除了文件末尾所有多余的代码,只保留最核心的默认导出 export default LogList;