Loki MCP Server -支持Claude Desktop/Claude Code/Cursor 等客户端通过自然语言查询日志
MCP定位技术栈架构项目结构基础框架搭建开发部署及常见问题# Loki MCP Server - CLAUDE.md Go 实现的 MCP Server集成 Grafana Loki 日志查询。支持 Claude Desktop / Claude Code / Cursor 等客户端通过自然语言查询日志。 --- ## 一、项目概述 ### 定位 将 Grafana Loki 的日志查询能力通过 MCP (Model Context Protocol) 暴露给 AI 助手运维人员可以用自然语言代替 LogQL 查询日志。 ### 技术栈 | 层 | 技术 | 版本 | |---|------|------| | 语言 | Go | 1.24 | | MCP SDK | github.com/mark3labs/mcp-go | v0.32.0 | | 日志存储 | Grafana Loki | 2.9.0 | | 日志采集 | Promtail | 2.9.0 | | 可视化 | Grafana | latest | | 容器化 | Docker Compose | - | ### 核心能力3 个 MCP Tool | Tool | 用途 | Loki API | |------|------|----------| | loki_query | 执行 LogQL 查询 | /loki/api/v1/query_range | | loki_label_names | 获取所有标签名 | /loki/api/v1/labels | | loki_label_values | 获取标签值列表 | /loki/api/v1/label/{name}/values | --- ## 二、架构设计 ### 整体架构 ┌─────────────────┐ ┌──────────────────┐ ┌──────────────┐ │ AI Client │ │ Loki MCP Server │ │ Grafana │ │ (Claude Code/ │─────→│ :8080 │─────→│ Loki │ │ Desktop/ │ MCP │ │ HTTP │ :3100 │ │ Cursor) │ │ 3 种传输协议: │ │ │ └─────────────────┘ │ - stdio │ └──────────────┘ │ - SSE (/sse) │ │ - HTTP (/stream)│ │ │ │ /healthz (K8s) │ └──────────────────┘ ### 三种传输协议同端口共存 | 协议 | 端点 | 场景 | |------|------|------| | stdio | 标准输入输出 | 本地 binary / Docker 集成Claude Desktop 直接启动进程 | | SSE | /sse /mcp | Server-Sent Events远程连接旧协议 | | Streamable HTTP | /stream | 新一代 MCP 远程协议推荐 | **设计要点**通过 http.ServeMux 将 SSE 和 Streamable HTTP 注册到同一端口stdio 作为后台 goroutine 并行运行。 ### 项目结构 loki-mcp/ ├── cmd/ │ ├── server/main.go # 入口注册 Tool 启动 3 种传输 │ └── client/main.go # JSON-RPC 测试客户端 ├── internal/ │ └── handlers/ │ ├── loki.go # 核心3 个 Tool 的完整实现 (993 行) │ └── loki_test.go # 单元测试 (261 行) ├── pkg/ │ └── utils/logger.go # 简易日志工具 ├── grafana/ │ └── provisioning/datasources/loki.yaml # Grafana 数据源预配置 ├── promtail/ │ └── config.yml # 日志采集配置 ├── examples/ │ ├── claude-desktop/ # 4 种 Claude Desktop 配置示例 │ ├── claude-code-commands/ # Slash Command 模板 │ ├── simple-sse-client.html # SSE 测试页面 │ └── sse-client.html # 完整 SSE 客户端 ├── docker-compose.yml # 本地 5 服务开发环境 ├── Dockerfile # 多阶段构建 ├── Makefile # 构建/测试/运行 ├── go.mod / go.sum ├── run-mcp-server.sh # 启动脚本 ├── test-loki-query.sh # 查询测试脚本 ├── insert-loki-logs.sh # 插入测试日志 └── README.md --- ## 三、从零实现指南 ### Phase 1基础框架搭建 #### 1.1 初始化 Go 项目 bash mkdir loki-mcp cd loki-mcp go mod init github.com/yourname/loki-mcp go get github.com/mark3labs/mcp-gov0.32.0 #### 1.2 理解 MCP Server 入口 (cmd/server/main.go) 核心模式**创建 Server → 注册 Tool → 启动传输层** go // 1. 创建 MCP Server 实例 s : server.NewMCPServer( Loki MCP Server, 0.1.0, server.WithResourceCapabilities(true, true), server.WithLogging(), ) // 2. 注册工具Tool 定义 Handler 函数 lokiQueryTool : handlers.NewLokiQueryTool() s.AddTool(lokiQueryTool, handlers.HandleLokiQuery) // 3. 创建传输层 sseServer : server.NewSSEServer(s, server.WithSSEEndpoint(/sse), server.WithMessageEndpoint(/mcp), ) streamableServer : server.NewStreamableHTTPServer(s) // 4. 统一路由 mux : http.NewServeMux() mux.Handle(/sse, sseServer) mux.Handle(/mcp, sseServer) mux.Handle(/stream, streamableServer) mux.HandleFunc(/healthz, healthHandler) // 5. 并行启动 HTTP stdio go http.ListenAndServe(:8080, mux) go server.ServeStdio(s) // 6. 优雅关闭 stop : make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) -stop **关键设计决策** - 三种传输协议同端口简化部署一个端口搞定所有客户端 - stdio 后台运行兼容 Claude Desktop 的进程模式 - /healthz 端点适配 K8s readiness/liveness probe #### 1.3 理解 Tool 定义模式 每个 Tool 由两部分组成 1. **Tool 定义函数** (NewXxxTool()) — 声明参数 schema 2. **Handler 函数** (HandleXxx()) — 处理请求逻辑 go // Tool 定义声明参数、类型、默认值、描述 func NewLokiQueryTool() mcp.Tool { return mcp.NewTool(loki_query, mcp.WithDescription(Run a query against Grafana Loki), mcp.WithString(query, mcp.Required(), mcp.Description(LogQL query string)), mcp.WithString(url, mcp.Description(Loki server URL), mcp.DefaultString(lokiURL)), mcp.WithString(start, mcp.Description(Start time (default: 1h ago))), mcp.WithNumber(limit, mcp.Description(Max entries (default: 100))), mcp.WithString(format, mcp.DefaultString(raw)), // ... 认证参数 ) } // Handler提取参数 → 构建请求 → 调用 Loki API → 格式化输出 func HandleLokiQuery(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { args : request.GetArguments() // ... 处理逻辑 return mcp.NewToolResultText(formattedResult), nil } ### Phase 2核心逻辑实现 #### 2.1 请求处理流程 (internal/handlers/loki.go) 参数提取 → 环境变量回退 → 时间解析 → URL构建 → HTTP请求 → 响应解析 → 格式化输出 #### 2.2 参数提取模式带环境变量回退 每个参数都遵循**请求参数 环境变量 默认值** 的优先级链 go // 统一模式先看请求参数再看环境变量 var lokiURL string if urlArg, ok : args[url].(string); ok urlArg ! { lokiURL urlArg } else { lokiURL os.Getenv(LOKI_URL) if lokiURL { lokiURL http://localhost:3100 } } 环境变量清单 | 变量 | 用途 | 默认值 | |------|------|--------| | LOKI_URL | Loki 地址 | http://localhost:3100 | | LOKI_ORG_ID | 租户 ID | 空 | | LOKI_USERNAME | Basic Auth 用户名 | 空 | | LOKI_PASSWORD | Basic Auth 密码 | 空 | | LOKI_TOKEN | Bearer Token | 空 | | PORT | 服务端口 | 8080 | #### 2.3 时间解析 (parseTime) 支持多种输入格式按顺序尝试 go func parseTime(timeStr string) (time.Time, error) { // 1. now 关键字 if timeStr now { return time.Now(), nil } // 2. 相对时间: -1h, -30m if timeStr[0] - { duration, err : time.ParseDuration(timeStr) if err nil { return time.Now().Add(duration), nil } } // 3. RFC3339: 2024-01-15T10:30:45Z // 4. ISO 变体: 2006-01-02T15:04:05, 2006-01-02 15:04:05 // 5. 纯日期: 2006-01-02 } #### 2.4 URL 构建 智能路径拼接处理各种 base URL 格式 go func buildLokiQueryURL(baseURL, query string, start, end int64, limit int) (string, error) { u, _ : url.Parse(baseURL) // 路径规范化避免重复拼接 if !strings.Contains(u.Path, loki/api/v1) { u.Path /loki/api/v1/query_range } // 查询参数 q : u.Query() q.Set(query, query) // LogQL q.Set(start, fmt.Sprintf(%d, start)) // Unix 秒 q.Set(end, fmt.Sprintf(%d, end)) q.Set(limit, fmt.Sprintf(%d, limit)) u.RawQuery q.Encode() return u.String(), nil } #### 2.5 认证机制 三级认证优先级Bearer Token Basic Auth 无认证 go if token ! { req.Header.Add(Authorization, Bearer token) } else if username ! || password ! { req.SetBasicAuth(username, password) } // 多租户隔离始终添加如果有值 if orgID ! { req.Header.Add(X-Scope-OrgID, orgID) } #### 2.6 响应数据结构 go type LokiResult struct { Status string json:status // success | error Data LokiData json:data Error string json:error,omitempty } type LokiData struct { ResultType string json:resultType // streams Result []LokiEntry json:result } type LokiEntry struct { Stream map[string]string json:stream // 标签: {job: xx, pod: xx} Values [][]string json:values // [[纳秒时间戳, 日志行], ...] } #### 2.7 三种输出格式 | 格式 | 用途 | 示例 | |------|------|------| | raw默认 | AI 解析友好最紧凑 | 2024-01-15T10:30:45Z {jobapi} Request received | | json | 程序化处理 | 完整 JSON 结构 | | text | 人类可读 | 编号 Stream 时间戳日志行 | #### 2.8 已知 Bug 及修复 **时间戳 2262 年 Bug**Loki 返回纳秒时间戳早期实现使用 time.Unix(ts, 0) 将纳秒当秒处理导致显示 2262 年。 修复time.Unix(0, int64(ts)) — 第一个参数为 0 秒第二个参数为纳秒。 ### Phase 3Docker 化与本地环境 #### 3.1 Dockerfile多阶段构建 dockerfile # Stage 1: 编译 FROM golang:1.24-alpine AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download # 利用缓存层 COPY . . RUN CGO_ENABLED0 GOOSlinux go build -o loki-mcp-server ./cmd/server # Stage 2: 运行最小镜像 FROM alpine:latest WORKDIR /app COPY --frombuilder /app/loki-mcp-server . EXPOSE 8080 ENTRYPOINT [./loki-mcp-server] **关键点** - CGO_ENABLED0静态链接不依赖 glibc - 先 COPY go.mod/go.sum → go mod download利用 Docker 层缓存加速构建 - 最终镜像基于 alpine:latest最小化攻击面 #### 3.2 Docker Compose5 服务完整环境 yaml services: loki-mcp-server: # MCP Server :8080 depends_on: loki: condition: service_healthy # 等 Loki 就绪 loki: # 日志存储 :3100 healthcheck: # /ready 端点检查 test: [CMD-SHELL, wget -q --spider http://localhost:3100/ready || exit 1] grafana: # 可视化 :3000 volumes: - ./grafana/provisioning:/etc/grafana/provisioning # 预配置数据源 promtail: # 日志采集 volumes: - /var/log:/var/log # 采集宿主机日志 - /var/run/docker.sock:/var/run/docker.sock # 采集容器日志 log-generator: # 测试日志生成器 command: | # 每 5 秒生成 INFO/ERROR 日志 while true; do echo INFO: ...; sleep 5; done **服务依赖链**log-generator → promtail → loki → loki-mcp-serverGrafana 独立。 ### Phase 4测试策略 #### 4.1 单元测试 重点覆盖时间戳解析bug 高发区 go func TestFormatLokiResults_NoYear2262Bug(t *testing.T) { testCases : []struct { name string timestampNs string expectedYear string }{ {Current, 1705312245000000000, 2024}, // 2024-01-15 {Recent, 1700000000000000000, 2023}, // 2023-11-14 {Future, 1800000000000000000, 2027}, // 2027-01-11 } for _, tc : range testCases { t.Run(tc.name, func(t *testing.T) { // 构建 LokiResult → formatLokiResults → 断言年份 }) } } **测试用例覆盖** - 正常时间戳解析 - 多条日志时间戳 - 非法时间戳回退 - 空结果处理 - 当前时间回归 - 2262 年 Bug 回归表驱动3 个时间点 #### 4.2 运行测试 bash make test # 全部测试 go test -coverprofilecoverage.out ./... # 带覆盖率 go tool cover -funccoverage.out # 查看覆盖率 go test -race ./... # 竞态检测 #### 4.3 集成测试脚本 bash # 插入测试日志 ./insert-loki-logs.sh --num 20 --job custom-job --app my-app # 查询验证 ./test-loki-query.sh {jobvarlogs} ./test-loki-query.sh {jobvarlogs} | ERROR -1h now 50 ### Phase 5部署与客户端集成 #### 5.1 部署方式选择 | 方式 | 场景 | 命令 | |------|------|------| | 本地二进制 | 开发调试 | make run | | Docker 单容器 | 已有 Loki 实例 | docker run -p 8080:8080 -e LOKI_URL... loki-mcp-server | | Docker Compose | 完整本地环境 | docker-compose up --build | | K8s Deployment | 生产环境 | 见下方 manifest | | 远程 URL | 团队共享 | https://loki-mcp.loki.com/stream | #### 5.2 K8s 部署建议 yaml apiVersion: apps/v1 kind: Deployment metadata: name: loki-mcp-server spec: replicas: 2 # 无状态可水平扩展 template: spec: containers: - name: loki-mcp-server image: loki-mcp-server:v0.1.0 # 固定版本 ports: - containerPort: 8080 env: - name: LOKI_URL value: http://loki-gateway.monitoring:3100 - name: LOKI_TOKEN valueFrom: secretKeyRef: name: loki-auth key: token readinessProbe: httpGet: path: /healthz port: 8080 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 8080 periodSeconds: 30 resources: requests: cpu: 50m memory: 64Mi limits: cpu: 200m memory: 128Mi #### 5.3 客户端接入 **Claude Code推荐 Streamable HTTP** bash claude mcp add --transport http --scope user loki https://loki-mcp.loki.com/stream **Claude Desktop本地 Docker** json { mcpServers: { loki: { command: docker, args: [run, --rm, -i, -e, LOKI_URLhttp://host.docker.internal:3100, loki-mcp-server:latest] } } } **Cursor** json { mcpServers: { loki: { command: docker, args: [run, --rm, -i, -e, LOKI_URLhttp://host.docker.internal:3100, loki-mcp-server:latest] } } } --- ## 四、开发命令速查 bash # 构建 make build # 编译本地二进制 make build-linux # 交叉编译 Linux (amd64) docker build -t loki-mcp-server . # 构建镜像 # 运行 make run # 本地运行 docker-compose up --build # 完整本地环境 # 测试 make test # 单元测试 go test -race ./... # 竞态检测 ./test-loki-query.sh # 集成测试 ./insert-loki-logs.sh # 插入测试数据 # 依赖 make deps # 下载依赖 make tidy # 整理 go.mod # 清理 make clean # 删除二进制 docker-compose down -v # 清理容器和数据卷 --- ## 五、代码规范 ### Go 规范 - gofmt / goimports 格式化 - 错误处理fmt.Errorf(context: %w, err) 包装 - 环境变量用 const 定义名称避免硬编码字符串 - HTTP 客户端必须设置 Timeout当前 30s - 使用 context.Context 传递请求上下文 ### MCP Tool 开发规范 - Tool 名称用 snake_case如 loki_query - 必填参数用 mcp.Required() 标记 - 每个参数都要有 mcp.Description()包含默认值说明 - 环境变量回退必须在 Handler 中实现而非仅 Tool 定义中 - 返回 mcp.NewToolResultText() 作为标准响应 ### 新增 Tool 模板 如果需要添加新工具如 loki_series遵循以下步骤 1. 在 internal/handlers/loki.go 中添加 - NewLokiSeriesTool() — 定义参数 - HandleLokiSeries() — 实现逻辑 - buildLokiSeriesURL() — URL 构建 - executeLokiSeriesQuery() — HTTP 请求 - formatLokiSeriesResults() — 格式化输出 2. 在 cmd/server/main.go 中注册 go seriesTool : handlers.NewLokiSeriesTool() s.AddTool(seriesTool, handlers.HandleLokiSeries) 3. 在 internal/handlers/loki_test.go 中添加测试 --- ## 六、运维关注点 ### 可观测性 - /healthz 端点返回 ok适配 K8s probe - 服务本身无状态无需持久化存储 - 启动日志输出所有端点地址 - 建议生产环境添加 Prometheus metrics 端点/metrics ### 安全 - 认证信息通过环境变量注入不写入镜像层 - Bearer Token 优先于 Basic Auth - 多租户通过 X-Scope-OrgID 头隔离 - HTTP 超时 30s 防止慢查询阻塞 ### 扩展方向 | 方向 | 说明 | |------|------| | 添加 /metrics | Prometheus 指标暴露 | | 添加 loki_series Tool | 查询 series 元数据 | | 添加 loki_stats Tool | 查询 ingester 统计 | | 提取认证参数公共函数 | 消除 3 个 Handler 中的重复代码 | | 添加请求日志中间件 | 记录每次 Tool 调用的查询和耗时 | | 支持 TLS | HTTPS 终止或证书配置 | | 添加 rate limiting | 防止 AI 频繁查询 | --- ## 七、学习路径建议 第 1 天跑起来 ├── docker-compose up --build ├── 访问 Grafana :3000 理解 Loki 数据结构 ├── ./insert-loki-logs.sh 插入测试数据 └── ./test-loki-query.sh 验证查询 第 2 天读懂入口 ├── cmd/server/main.go (100 行) — MCP 注册 传输层 ├── 理解 mcp-go SDK 的 AddTool 模式 └── 理解三协议同端口架构 第 3 天精读核心 ├── internal/handlers/loki.go (993 行) ├── 参数提取 → 环境变量回退模式 ├── parseTime 多格式时间解析 ├── buildLokiQueryURL 路径规范化 ├── executeLokiQuery HTTP 认证 └── formatLokiResults 三种输出格式 第 4 天测试与 Bug ├── internal/handlers/loki_test.go ├── 纳秒时间戳 2262 年 Bug 的前因后果 └── Go 表驱动测试风格 第 5 天Docker 部署 ├── Dockerfile 多阶段构建 ├── docker-compose.yml 服务编排 ├── K8s 部署清单设计 └── 客户端集成配置 第 6 天动手扩展 ├── 尝试添加 loki_series Tool ├── 提取认证参数公共函数消除重复 └── 添加 /metrics 端点 --- ## 八、常见问题 ### 连接失败 1. claude mcp get loki 检查配置 2. 确认网络连通性 3. 检查 HTTPS 证书 ### 查询无结果 1. 确认 Loki 有对应时间范围的数据 2. 多租户场景检查 org_id 3. 用 loki_label_names 先查可用标签 ### Docker 环境问题 1. Loki 启动需要时间等 healthcheck 通过 2. Mac 上 Docker 访问宿主机用 host.docker.internal 3. docker-compose down -v 可清理数据重来 ### 时间戳显示异常 - 确认使用 time.Unix(0, int64(ns)) 而非 time.Unix(ns, 0) - Loki 返回纳秒时间戳不是秒使用测试▗ ▗ ▖ ▖ Claude Code v2.1.96 Opus 4.6 (1M context) with medium effort · API Usage Billing ▘▘ ▝▝ ~/loki-mcp ⎿ SessionStart:startup hook error ❯ /mcp ⎿ MCP dialog dismissed ❯ loki查看所有可用的标签名 ⏺ loki - loki_label_names (MCP) ⏺ Loki 当前可用的标签名 ┌────────────────┬─────────────────────────┐ │ 标签 │ 常见用途 │ ├────────────────┼─────────────────────────┤ │ app │ 应用名称 │ ├────────────────┼─────────────────────────┤ │ broker │ 消息代理 │ ├────────────────┼─────────────────────────┤ │ component │ 组件标识 │ ├────────────────┼─────────────────────────┤ │ container │ 容器名 │ ├────────────────┼─────────────────────────┤ │ env │ 环境 (prod/staging/dev) │ ├────────────────┼─────────────────────────┤ │ filename │ 日志文件路径 │ ├────────────────┼─────────────────────────┤ │ host │ 主机名 │ ├────────────────┼─────────────────────────┤ │ instance │ 实例地址 │ ├────────────────┼─────────────────────────┤ │ job │ Prometheus job 名称 │ ├────────────────┼─────────────────────────┤ │ namespace │ K8s 命名空间 │ ├────────────────┼─────────────────────────┤ │ node_name │ K8s 节点名 │ ├────────────────┼─────────────────────────┤ │ pod │ Pod 名称 │ ├────────────────┼─────────────────────────┤ │ request_method │ HTTP 请求方法 │ ├────────────────┼─────────────────────────┤ │ service_name │ 服务名称 │ ├────────────────┼─────────────────────────┤ │ site │ 站点标识 │ ├────────────────┼─────────────────────────┤ │ status │ 状态码 │ ├────────────────┼─────────────────────────┤ │ stream │ stdout/stderr │ ├────────────────┼─────────────────────────┤ │ uri │ 请求路径 │ └────────────────┴─────────────────────────┘ 需要查看某个标签的具体值可以告诉我比如 namespace 有哪些值。 ❯ 查询apppb-trading-engine envprodjp 的近5分钟的错误日志帮我分析下 ⏺ loki - loki_query (MCP)(query: {app\pb-trading-engine\, env\prodjp\} |~ (?i)error|exception|fatal|panic, start: 5m, limit: 200) ⎿ Error: MCP error -32603: invalid start time: unsupported time format: 5m ⏺ loki - loki_query (MCP)(query: {app\pb-trading-engine\, env\prodjp\} |~ (?i)error|exception|fatal|panic, start: 2026-04-08T15:00:00Z, end: 2026-04-08T15:05:00Z, limit: 200) ⏺ 时间格式需要调整让我用 UTC 时间重试当前北京时间约 15:05即 UTC 07:05。 ⏺ loki - loki_query (MCP)(query: {app\pb-trading-engine\, env\prodjp\} |~ (?i)error|exception|fatal|panic, start: 2026-04-08T07:00:00Z, end: 2026-04-08T07:05:00Z, limit: 200) ⏺ 近 5 分钟没有匹配到错误日志。让我先确认标签值是否正确以及扩大时间范围看看。 ⏺ loki - loki_label_values (MCP)(label: app) ⏺ loki - loki_label_values (MCP)(label: env) ⏺ 标签值确认正确apppb-trading-engine 和 envprodjp 都存在。让我扩大时间范围查询。 ⏺ loki - loki_query (MCP)(query: {app\pb-trading-engine\, env\prodjp\} |~ (?i)error|exception|fatal|panic, start: 2026-04-08T06:00:00Z, end: 2026-04-08T07:10:00Z, limit: 200) ⎿ Error: result (73,209 characters) exceeds maximum allowed tokens. Output has been saved to /Users/example/.claude/projects/-Users-example-ltp-codes-ops-agent/0d50fb2b-2b85-4322-9f3a-72d75fc427 b3/tool-results/mcp-loki-loki_query-1111111.txt. Format: JSON array with schema: [{type: string, text: string}] … 7 lines (ctrlo to expand) ⏺ 查到大量错误日志让我逐段读取分析。 Searched for 1 pattern, read 1 file, searched memories (ctrlo to expand) ⏺ 文件是 JSON 格式用 jq 解析。 ⏺ Bash(cat /Users/example/.claude/projects/-Users-example-ltp-codes-ops-agent/0d50fb2b-2b85-4322-9f3a-72d75fc427b3/tool-results/mcp-loki-loki_query-111111.txt |…) ⎿ 2026-04-08T06:39:55Z {service_namepb-trading-engine,apppb-trading-engine,detected_levelinfo,envprodjp,filename/data/logs/pb-trading-engine/info.log,host10.19.5.136} 2026-04-08 06:39:54.9 62 [disruptor-worker9] [INFO ] [TraceId11111111] com.ltp.pb.engine.service.biz.PlacePerpOrderService - placePerpOrder TRADE_MAX_ORDERS_ERROR. oid:6666666,count:60,maxNum:6 0 … 24 lines (ctrlo to expand) ⏺ Bash(cat /Users/example/.claude/projects/-Users-example-ltp-codes-ops-agent/0d50fb2b-2b85-4322-9f3a-72d75fc427b3/tool-results/mcp-loki-loki_query-222222.txt |…) ⎿ 总日志条数: 172 错误类型分布 … 24 lines (ctrlo to expand) ⏺ Bash(cat /Users/example/.claude/projects/-Users-example-ltp-codes-ops-agent/0d50fb2b-2b85-4322-9f3a-72d75fc427b3/tool-results/mcp-loki-loki_query-333333.txt |…) ⎿ 非 TRADE_MAX_ORDERS_ERROR 的日志 2026-04-08T06:25:17Z {apppb-trading-engine,detected_levelerror,envprodjp,filename/data/logs/pb-trading-engine/warn.log,host10.19.5.136,service_namepb-trading-engine} 2026-04-08 06:25:12. … 34 lines (ctrlo to expand) ⏺ pb-trading-engine (prodjp) 错误日志分析 时间范围: 2026-04-08 06:03 ~ 06:43 UTC约 14:03 ~ 14:43 JST 总日志条数: 172 条 --- 1. 错误类型分布 ┌───────────────────────────────────────────────────────┬──────┬───────┬──────────┐ │ 错误类型 │ 数量 │ 占比 │ 严重程度 │ ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤ │ TRADE_MAX_ORDERS_ERROR │ 158 │ 91.9% │ MEDIUM │ ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤ │ LiqService - tryTriggerLiqHandle account status error │ 9 │ 5.2% │ HIGH │ ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤ │ LiqService - cancel all/open exceed limit │ 2 │ 1.2% │ HIGH │ ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤ │ PostWorkerHandler - lark alert (飞书告警) │ 2 │ 1.2% │ INFO │ ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤ │ OrderMonitor - ems not exist │ 1 │ 0.6% │ MEDIUM │ └───────────────────────────────────────────────────────┴──────┴───────┴──────────┘ --- 2. 逐类分析 (1) TRADE_MAX_ORDERS_ERROR — 158 条 (占 91.9%) PlacePerpOrderService - placePerpOrder TRADE_MAX_ORDERS_ERROR. oid:xxx, count:60, maxNum:60 - 原因: 某账户的永续合约挂单数 (count60) 达到最大限制 (maxNum60)新单被拒绝 - 主机: 集中在 10.19.5.136161 条 - 时间: 呈周期性爆发每隔约 5-6 分钟出现一波06:03, 06:09, 06:15, 06:22, 06:26, 06:39 - 推测: 某个做市/策略账户在高频下单每次触发限制后短暂停顿再重试形成周期性模式 - 影响: INFO 级别不影响系统稳定性但说明客户端策略未处理好限频 (2) LiqService 清算状态异常 — 9 条 (HIGH) tryTriggerLiqHandle account status error oldAccountStatusNORMAL/MARGIN_CALL/CANCEL_ALL/CANCEL_OPEN - 涉及用户: userId1234563 个 portfolioId - 11111 (BINANCE) — equity ≈ 19.6 USDT - 22222 (BINANCE) — equity ≈ 35.2 USDT - 33333 (BINANCE) — equity ≈ 1.0~1.5 USDT - 原因: 清算引擎尝试触发清算处理时账户状态不符合预期如想从 NORMAL 转到清算状态时发现状态不一致 - 关注点: portfolioId 33333 的 equity 极低仅 ~1 USDT经历了完整的清算流程 MARGIN_CALL → CANCEL_OPEN → CANCEL_ALL (3) cancel all/open exceed limit — 2 条 (HIGH) cancel all exceed limit, portfolioId 33333 cancel open exceed limit, portfolioId 33333 - 原因: 同一 portfolioId 33333 的清算撤单操作频率超过限制 - 关联: 与上面的 LiqService 状态异常是同一账户清算过程中多次尝试撤单被限频 (4) OrderMonitor - ems not exist — 1 条 orderStateMonitor ems not exist. pid:11111, oid:666666, engine:OPEN - 原因: 订单状态监控发现某订单的 EMS执行管理系统记录不存在 - 可能: 订单创建与 EMS 注册之间的时序问题或 EMS 记录已被清理 --- 3. 主机分布 ┌──────────────┬──────┬─────────────────────────────┐ │ Host │ 条数 │ 主要错误 │ ├──────────────┼──────┼─────────────────────────────┤ │ 10.19.5.136 │ 161 │ TRADE_MAX_ORDERS_ERROR 为主 │ ├──────────────┼──────┼─────────────────────────────┤ │ 10.19.11.174 │ 11 │ LiqService 清算相关 │ └──────────────┴──────┴─────────────────────────────┘ --- 4. 建议 ┌────────┬───────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 优先级 │ 行动 │ 说明 │ ├────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤ │ P1 │ 检查 userId123456 账户健康度 │ 多个 portfolio 同时触发清算equity 极低需确认是否需人工干预 │ ├────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤ │ P2 │ 排查高频下单源 │ 10.19.5.136 上每 5 分钟爆发 158 次 MAX_ORDERS_ERROR定位是哪个 API client / 策略在重复触碰限制 │ ├────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤ │ P3 │ 优化清算限频逻辑 │ cancel all/open exceed limit 说明清算过程中的撤单频率控制可能需要调整避免清算流程被自身限频阻断 │ ├────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤ │ P4 │ 确认 EMS 数据一致性 │ ems not exist 虽然只有 1 条但可能代表订单生命周期管理的隐患 │ └────────┴───────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────┘ 需要我针对某个具体问题深入排查吗比如查看 userId123456 的更多日志或者分析下单源的 TraceId 链路。

相关新闻