首页 > 基础资料 博客日记

Loki MCP Server -支持Claude Desktop/Claude Code/Cursor 等客户端通过自然语言查询日志

2026-04-08 17:00:03基础资料围观1

这篇文章介绍了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-go@v0.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 {job=api} Request received` |
    | `json` | 程序化处理 | 完整 JSON 结构 |
    | `text` | 人类可读 | 编号 Stream + 时间戳日志行 |
    
    #### 2.8 已知 Bug 及修复
    
    **时间戳 2262 年 Bug**:Loki 返回纳秒时间戳,早期实现使用 `time.Unix(ts, 0)` 将纳秒当秒处理,导致显示 2262 年。
    
    修复:`time.Unix(0, int64(ts))` — 第一个参数为 0 秒,第二个参数为纳秒。
    
    ### Phase 3:Docker 化与本地环境
    
    #### 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_ENABLED=0 GOOS=linux go build -o loki-mcp-server ./cmd/server
    
    # Stage 2: 运行(最小镜像)
    FROM alpine:latest
    WORKDIR /app
    COPY --from=builder /app/loki-mcp-server .
    EXPOSE 8080
    ENTRYPOINT ["./loki-mcp-server"]
    ```
    
    **关键点**- `CGO_ENABLED=0`:静态链接,不依赖 glibc
    - 先 COPY go.mod/go.sum → `go mod download`:利用 Docker 层缓存加速构建
    - 最终镜像基于 `alpine:latest`:最小化攻击面
    
    #### 3.2 Docker Compose(5 服务完整环境)
    
    ```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-server`,Grafana 独立。
    
    ### 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 -coverprofile=coverage.out ./...    # 带覆盖率
    go tool cover -func=coverage.out            # 查看覆盖率
    go test -race ./...                         # 竞态检测
    ```
    
    #### 4.3 集成测试脚本
    
    ```bash
    # 插入测试日志
    ./insert-loki-logs.sh --num 20 --job "custom-job" --app "my-app"
    
    # 查询验证
    ./test-loki-query.sh '{job="varlogs"}'
    ./test-loki-query.sh '{job="varlogs"} |= "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_URL=http://host.docker.internal:3100", "loki-mcp-server:latest"]
        }
      }
    }
    ```
    
    **Cursor**:
    ```json
    {
      "mcpServers": {
        "loki": {
          "command": "docker",
          "args": ["run", "--rm", "-i", "-e", "LOKI_URL=http://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 有哪些值。
    
    ❯ 查询app=pb-trading-engine env=prodjp 的近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")
    
    ⏺ 标签值确认正确:app=pb-trading-engine 和 env=prodjp 都存在。让我扩大时间范围查询。
    
    ⏺ 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 (ctrl+o to expand)
    
    ⏺ 查到大量错误日志,让我逐段读取分析。
    
      Searched for 1 pattern, read 1 file, searched memories (ctrl+o 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_name=pb-trading-engine,app=pb-trading-engine,detected_level=info,env=prodjp,filename=/data/logs/pb-trading-engine/info.log,host=10.19.5.136} 2026-04-08 06:39:54.9
         62 [disruptor-worker9] [INFO ] [TraceId=11111111] com.ltp.pb.engine.service.biz.PlacePerpOrderService - placePerpOrder TRADE_MAX_ORDERS_ERROR. oid:6666666,count:60,maxNum:6
         0+24 lines (ctrl+o 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 (ctrl+o 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 {app=pb-trading-engine,detected_level=error,env=prodjp,filename=/data/logs/pb-trading-engine/warn.log,host=10.19.5.136,service_name=pb-trading-engine} 2026-04-08 06:25:12.     
         … +34 lines (ctrl+o 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                                │ 15891.9% │ MEDIUM   │                                                                                                                     
      ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤                                                                                                                     
      │ LiqService - tryTriggerLiqHandle account status error │ 95.2%  │ HIGH     │                                                                                                                     
      ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤                                                                                                                     
      │ LiqService - cancel all/open exceed limit             │ 21.2%  │ HIGH     │                                                                                                                     
      ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤                                                                                                                     
      │ PostWorkerHandler - lark alert (飞书告警)               │ 21.2%  │ INFO      │                                                                                                                     
      ├───────────────────────────────────────────────────────┼──────┼───────┼──────────┤                                                                                                                     
      │ OrderMonitor - ems not exist                          │ 10.6%  │ MEDIUM   │                                                                                                                     
      └───────────────────────────────────────────────────────┴──────┴───────┴──────────┘                                                                                                                     
                                                                                                                                                                                                              
      ---                                                                                                                                                                                                     
      2. 逐类分析                                                                                                                                                                                             
                                                                                                                                                                                                              
      (1) TRADE_MAX_ORDERS_ERROR — 158 条 (占 91.9%)                                                                                                                                                          
                                                                                                                                                                                                              
      PlacePerpOrderService - placePerpOrder TRADE_MAX_ORDERS_ERROR. oid:xxx, count:60, maxNum:60                                                                                                             
                                                                                                                                                                                                              
      - 原因: 某账户的永续合约挂单数 (count=60) 达到最大限制 (maxNum=60),新单被拒绝                                                                                                                          
      - 主机: 集中在 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 oldAccountStatus=NORMAL/MARGIN_CALL/CANCEL_ALL/CANCEL_OPEN                                                                                                     
                                                                                                                                                                                                              
      - 涉及用户: userId=1234563 个 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.136161  │ TRADE_MAX_ORDERS_ERROR 为主 │                                                                                                                                                   
      ├──────────────┼──────┼─────────────────────────────┤                                                                                                                                                 
      │ 10.19.11.17411   │ LiqService 清算相关         │                                                                                                                                                   
      └──────────────┴──────┴─────────────────────────────┘                                                                                                                                                   
                                                                                                                                                                                                              
      ---                                                                                                                                                                                                     
      4. 建议                                                                                                                                                                                               
                                                                                                                                                                                                              
      ┌────────┬───────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────┐
      │ 优先级 │             行动              │                                               说明                                                │                                                          
      ├────────┼───────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤                                                        
      │ P1     │ 检查 userId=123456 账户健康度 │ 多个 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 条,但可能代表订单生命周期管理的隐患                                     │
      └────────┴───────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────┘                                                          
                      
      需要我针对某个具体问题深入排查吗?比如查看 userId=123456 的更多日志,或者分析下单源的 TraceId 链路。                                                                                                    
                      

     


文章来源:https://www.cnblogs.com/Jame-mei/p/19836377
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云