Skip to content

日志系统说明

本文档帮助开发者了解 MJ-Studio 日志系统的设计原理和运作流程。

概述

MJ-Studio 的日志系统分为两个独立部分:

  1. 控制台日志:使用 console.log 输出实时统计信息,用于开发调试
  2. HTTP 文件日志:使用 JSONL 格式记录完整的 HTTP 请求响应数据,用于问题追踪

两者相互独立,各司其职:

  • 控制台日志关注实时统计(请求大小、耗时、token 用量)
  • 文件日志关注完整数据(HTTP 请求响应的原始内容)

一、控制台日志

设计目标

实时监控系统运行状态,输出格式化的统计信息,方便开发者快速了解:

  • 哪个对话/任务正在执行
  • 使用了哪个上游和模型
  • 请求响应的大小和耗时
  • 是否发生错误

日志格式

对话模块

[Chat] {操作类型} | 对话#{id} "{标题}" | {详细信息}

操作类型:聊天请求/完成、重放请求/完成、压缩请求/完成、标题请求/完成、错误、中断

示例

[Chat] 聊天请求 | 对话#123 "Vue组件设计" | 上游:https://api.openai.com 模型:gpt-4 | 提示词:0.5KB 历史:10条/15.2KB 当前:0.3KB | 总计:16.0KB
[Chat] 聊天完成 | 对话#123 | 响应:2.1KB | 耗时:3250ms
[Chat] 聊天错误 | 对话#123 | HTTP 429: Rate limit exceeded

绘图/视频任务

[Task] {操作} | #{taskId} {modelType}/{apiFormat} | 上游:{name} 模型:{modelName} Key:{keyName} | {详细信息}

示例

[Task] 提交 | #57 image/mj-proxy | 上游:MJ官方 模型:midjourney Key:default | 一只可爱的猫咪...
[Task] #57 HTTP 请求 → POST https://api.midjourney.com/mj/submit/imagine
[Task] #57 HTTP 响应 ← 200 OK (1234ms)

数据统计

  • 大小计算:UTF-8 编码字节数,< 1KB 显示字节,≥ 1KB 显示 KB(保留1位小数)
  • 消息统计:格式为 {count}条/{size}KB
  • 耗时计算:请求开始到响应结束的毫秒数

实现位置

  • 对话日志:server/services/chat.tsserver/services/claude.ts
  • 任务日志:server/services/task.ts 及各上游服务文件

二、HTTP 文件日志

设计目标

记录完整的 API 请求响应数据,便于:

  • 追踪具体的 API 调用细节
  • 调试上游 API 返回的错误
  • 分析接口性能问题
  • 审计 API 调用历史

文件组织

logs/
├── conversation/
│   ├── 1.jsonl          # 对话 #1 的所有请求响应
│   ├── 2.jsonl
│   └── ...
└── task/
    ├── 1.jsonl          # 任务 #1 的所有请求响应
    ├── 2.jsonl
    └── ...

特点

  • 按对话 ID 或任务 ID 分文件
  • JSONL 格式(每行一个 JSON 对象)
  • 自动创建目录和文件
  • 支持追加写入

JSONL 格式

JSONL (JSON Lines) 每行包含一个独立的 JSON 对象。

优势

  • 易于追加:直接写入文件末尾
  • 易于读取:逐行解析,内存占用小
  • 易于工具处理:jqgrep 等可直接处理

示例

jsonl
{"type":"request","timestamp":"2025-01-06T10:30:00.000Z","messageId":123,"url":"https://api.openai.com/v1/chat/completions","method":"POST","headers":{"Authorization":"Bea...xyz"},"body":{"model":"gpt-4","messages":[...]}}
{"type":"response","timestamp":"2025-01-06T10:30:02.500Z","messageId":123,"status":200,"statusText":"OK","body":{"choices":[...]},"durationMs":2500}

记录内容

请求记录

  • type: "request"
  • timestamp: ISO 8601 格式时间戳
  • conversationId / taskId: 关联 ID
  • messageId: (仅对话)消息 ID
  • url: 请求 URL
  • method: HTTP 方法
  • headers: 请求头(已脱敏)
  • body: 请求体(已脱敏)

响应记录

  • type: "response"
  • timestamp: ISO 8601 格式时间戳
  • conversationId / taskId: 关联 ID
  • messageId: (仅对话)消息 ID
  • status: HTTP 状态码
  • statusText: HTTP 状态文本
  • body: 响应体(已脱敏)
  • durationMs: 请求耗时(毫秒)
  • error / errorType: (仅错误)错误信息

数据脱敏规则

1. Authorization 头

显示首尾各 4 个字符,中间用省略号代替。

原始:Bearer sk-abc123def456ghi789jkl012mno345
脱敏:Bea...345

2. Base64 图片数据

自动检测并截断为 [base64 N chars][dataUrl N chars]

检测规则

  • Data URL 格式:data:image/*;base64,...
  • 纯 Base64:连续 Base64 字符(长度 > 100)

3. 消息数组截断

对于大量消息的请求,保留前 2 条和最后 1 条,添加元数据。

json
{
  "messages": {
    "_truncated": true,
    "_summary": "[10 messages, 15234 bytes]",
    "_first": [
      {"role": "system", "content": "你是一个专业的AI助手...长的AI助手,擅长写作 (245字节)"},
      {"role": "user", "content": "你好,我想了解一下...下一下人工智能的发展 (189字节)"}
    ],
    "_last": {"role": "user", "content": "帮我写一篇关于人...篇关于人工智能的文章 (156字节)"}
  }
}

4. 消息内容截断

单条消息 content 保留首尾各 20 字符。

规则

  • 长度 ≤ 50:保持原样
  • 长度 > 50:首 20 + "..." + 尾 20 + " (N字节)"
原始:你是一个专业的AI助手,能够帮助用户解答各种问题...(省略)...你擅长写作、编程、分析数据等多种技能。

截断:你是一个专业的AI助手,能够帮助用...同时保持友好和专业的态度。你擅长写作、编程、分析数据等多种技能。 (245字节)

5. 流式响应内容

记录首尾预览和总长度。

你好!我是 AI 助手...助和支持。有什么可以帮助您的吗? (总长度: 1234字节)

记录时机

对话模块

  1. 请求发起时:记录完整 HTTP 请求
  2. 响应完成时:
    • 成功:记录响应体、状态码、耗时
    • 流式输出:记录内容预览
    • HTTP 错误:记录完整错误响应体
    • 网络错误:记录错误消息和类型

任务模块

  1. 提交任务时:记录请求
  2. 收到响应时:记录响应
  3. 轮询状态时:(可选)记录轮询请求响应
  4. 任务失败时:记录错误响应

实现位置

日志工具server/utils/httpLogger.ts

日志函数

  • logConversationRequest(conversationId, messageId, data)
  • logConversationResponse(conversationId, messageId, data)
  • logTaskRequest(taskId, data)
  • logTaskResponse(taskId, data)

调用位置

  • 对话服务:chat.tsclaude.ts
  • 任务服务:mj.tsgemini.tsdalle.tsopenaiChat.tskoukoutu.tsvideoUnified.ts

日志查看

bash
# 查看对话日志
cat logs/conversation/1.jsonl | jq .

# 筛选请求记录
cat logs/conversation/1.jsonl | jq 'select(.type == "request")'

# 筛选错误响应
cat logs/conversation/1.jsonl | jq 'select(.status >= 400)'

# 查看任务日志
cat logs/task/57.jsonl | jq .

# 查看最后一条记录
tail -n 1 logs/task/57.jsonl | jq .

# 搜索特定错误
grep "Rate limit" logs/**/*.jsonl

三、运作流程

对话流程

用户发送消息

API 端点接收 (server/api/conversations/[id]/messages.post.ts)

准备日志上下文(conversationId、messageId、操作类型等)

调用对话服务 (chat.ts 或 claude.ts)

【控制台】输出"聊天请求"统计
【文件】记录 HTTP 请求到 logs/conversation/{id}.jsonl

发起上游 API 请求

接收响应

【控制台】输出"聊天完成"统计
【文件】记录响应到 logs/conversation/{id}.jsonl

返回结果给前端

绘图任务流程

用户提交绘图任务

API 端点接收 (server/api/tasks.post.ts)

创建任务记录(保存到数据库)

调用任务服务 submitTask(taskId)

【控制台】输出"提交"统计

根据 apiFormat 选择上游服务

【文件】记录 HTTP 请求到 logs/task/{taskId}.jsonl
【控制台】输出"HTTP 请求"

发起上游 API 请求

【文件】记录响应到 logs/task/{taskId}.jsonl
【控制台】输出"HTTP 响应"

更新任务状态

异步轮询流程(MJ、抠抠图、视频)

提交任务(同上)

收到上游任务 ID

更新任务状态为"processing"

前端定时轮询 (GET /api/tasks/{id})

后端调用 syncTaskStatus(taskId)

查询上游任务状态

【文件】记录轮询请求响应(可选)

更新本地任务状态

返回最新状态给前端

四、设计原则

1. 职责分离

  • 控制台日志:实时监控和开发调试
  • 文件日志:完整数据记录和事后分析

2. 数据隐私

  • 自动脱敏敏感信息
  • 保留必要的调试信息
  • 可追溯但不泄露完整数据

3. 性能考虑

  • 追加写入,避免锁定文件
  • JSONL 流式处理,内存占用小
  • 自动截断大数据

4. 可维护性

  • 日志格式统一
  • 按对话/任务分文件
  • 元数据标记提示数据处理

五、常见场景

调试对话请求失败

  1. 查看控制台日志确认错误
  2. 打开 logs/conversation/{id}.jsonl
  3. 找到最近的请求响应记录
  4. 检查请求体和响应体

分析任务耗时

  1. 查看控制台"HTTP 响应"耗时
  2. 打开 logs/task/{taskId}.jsonl
  3. 查看 durationMs 字段
  4. 对比不同模型平均耗时

追踪 API Key 使用

  1. 搜索日志中的 Authorization 头
  2. 根据首尾字符识别不同 Key
  3. 统计使用次数和错误率

六、注意事项

开发时

  • 修改服务时传递正确的日志参数
  • 新增上游服务时记录日志
  • 测试时检查日志文件生成

部署时

  • 确保 logs/ 目录可写
  • 定期清理旧日志
  • 监控日志文件大小

调试时

  • 优先查看控制台日志
  • 需要详细数据时查看文件日志
  • 注意脱敏数据

七、扩展方向

  • 日志聚合:集成 Elasticsearch、Loki 等
  • 日志分析:统计调用频率、成功率、平均耗时
  • 告警机制:错误率、余额、耗时异常告警
  • 日志压缩:自动压缩旧日志节省空间

通过理解日志系统的设计和运作流程,开发者可以更高效地调试问题、优化性能和监控系统健康状况。

MJ-Studio - 多模型 AI 工作台