ZMUKE DevOps / runbooks/loki-issue-watcher-auto-500.md

导入说明(2026-08-27) 本文原件位于 zmuke-n-00001~adminb84002/devops/docs/ops/loki-issue-watcher-auto-500.md, 只读取回、内容未改。已逐行扫描,无凭据真值:文中 GITEA_TOKEN=replace-with-token 是占位符,其余命中均为键名或说明文字。 归属提示:本文描述的 loki-issue-watcher 属于 logging 观测栈,该栈是 SlaunchX 观测栈的副本,当前在 zmuke-n-00001未运行,归属待 owner 决定 (见 deploy/zmuke-n-00001/logging/README.md)。若归 SlaunchX,本文应随栈迁往 slaunchx-devops

Loki 自动异常工单操作文档

本文说明 docker/logging/loki_issue_watcher 如何从 test-ai Loki 自动捕获 test 环境 HTTP 5xx 和可行动 400,并生成开发可直接处理的 Gitea issue。

目标

架构

Nginx / Java logs -> Promtail -> Loki
                         |
                         v
              loki-issue-watcher
                         |
                         v
          Gitea issue create/update

loki-issue-watcher 是旁路服务,不改 Promtail、Loki 或 Grafana 告警链路。 通知发送能力暂时不在 Compose 和默认文档配置中暴露,后续需要时再启用。

默认范围

默认只处理:

env=test
log_type=access
HTTP status=5xx / actionable 400
Java service=slaunchx-app-prometheus-test

不处理 alpha / product,避免初期误报和敏感日志扩散。

可行动 400 只覆盖后端需要处理的异常,例如枚举编码错配、JSON/DTO 反序列化、后端读库后转换失败。普通登录失败、token/session 缺失、常规业务校验和用户输入错误不会自动建 issue。

配置

在启动 docker/logging 的环境文件或 shell 中设置:

GITEA_BASE_URL=https://gitea.slaunchx.cc
GITEA_TOKEN=replace-with-token
LOKI_ISSUE_GITEA_REPO=slaunchx/slaunchx-backend-platform
LOKI_ISSUE_DRY_RUN=0

常用配置项:

变量默认值说明
LOKI_ISSUE_WATCH_ENVtest观察的环境
LOKI_ISSUE_JAVA_SERVICE_NAMEslaunchx-app-prometheus-testJava 服务名
LOKI_ISSUE_POLL_INTERVAL_SECONDS60轮询间隔
LOKI_ISSUE_LOOKBACK_SECONDS300每次回看窗口
LOKI_ISSUE_OVERLAP_SECONDS120与上次查询重叠,避免漏掉延迟日志
LOKI_ISSUE_JAVA_WINDOW_SECONDS10access 前后查 Java 日志的秒数
LOKI_ISSUE_DRY_RUN01 时只输出 payload,不创建 issue
GITEA_TOKEN需要目标仓库 issue 读写权限

没有 GITEA_TOKEN 时,watcher 会启动,但不会创建 issue。

启动

cd /root/www/devops/docker/logging
mkdir -p data/loki-issue-watcher
docker compose --env-file .env.shared up -d --build loki-issue-watcher

建议首次以 dry-run 验证:

cd /root/www/devops/docker/logging
LOKI_ISSUE_DRY_RUN=1 docker compose --env-file .env.shared up -d --build loki-issue-watcher
docker logs --tail 200 -f slaunchx-loki-issue-watcher

确认指纹和标题符合预期后,再设置 LOKI_ISSUE_DRY_RUN=0

去重规则

同类异常使用同一个 open issue。5xx 指纹字段保持历史兼容:

env + host + method + normalized_path + exception_type + exception_location

可行动 400 会额外加入归一化后的异常 message;message 中纯数字归一成 <int>,例如 57030401 / 57030402 会归为同一类枚举编码错配。

normalized_path 会去掉 query,并把明显业务 ID 段替换成 {id}。例如:

/web/v1/recharges/RCO7rnKJSHSQfVhWG5k/confirm
-> /web/v1/recharges/{id}/confirm

如果对应 issue 已关闭,再次命中时会创建新 issue,视为回归。

Issue 标题

格式:

[AUTO-500][test][system] GET /web/v1/... -> IllegalStateException at Xxx.java:123

示例:

[AUTO-500][test][system] GET /web/v1/exchange-channels/templates/page -> IllegalStateException at ExchangeChannelService.java:553
[AUTO-400][test][tenant] GET /web/v1/recharge/channel/page -> IllegalArgumentException at RechargeChannelVisibility.java:52

Issue 正文

正文必须能让开发者不查 Loki 也能定位问题,包含:

正文结构:

<!-- loki-500-fingerprint:<sha> -->
## Auto 500 Report

**Environment:** test
**Host:** system-test.slaunchx.cc
**External request:** `GET /web/v1/exchange-channels/templates/page`
**HTTP status:** `500`
**Access time:** 2026-06-04T15:41:45+08:00
**Internal path:** `/prometheus/web/v1/system/exchange-channels/templates/page`
**requestId:** `1780558905.614-63966-58`
**traceId:** `6a212c39229ab18164be2bef08e18779`

## Failure

**Exception:** `java.lang.IllegalStateException`
**Message:** Invalid exchange template rate_config JSON
**Location:** `com.slaunchx.core.business.exchange.channel.ExchangeChannelService.lambda$parseRateConfig$9(ExchangeChannelService.java:553)`
**Suspected layer:** DB JSON format / Java deserialization compatibility

## Application Stack Frames

at com.slaunchx.core.business.exchange.channel.ExchangeChannelService.lambda$parseRateConfig$9(ExchangeChannelService.java:553) at com.slaunchx.core.infrastructure.json.JsonPayloadCodec.readValueOrThrowNullable(JsonPayloadCodec.java:99) at com.slaunchx.core.business.exchange.channel.ExchangeChannelService.parseRateConfig(ExchangeChannelService.java:550)

重复命中评论

同类异常再次发生时,不重复创建 issue。为避免刷屏,只在命中次数达到 235102050 时追加评论:

Repeated 500 detected.

**Seen count:** 3
**Latest time:** 2026-06-04T15:46:15+08:00
**External request:** `GET /web/v1/recharge/template/page?page=0&size=10`
**requestId:** `1780559174.906-64037-3`
**traceId:** `6a212d462b41792b126db1be0bc4c816`
**Exception:** `java.lang.IllegalStateException: Invalid recharge template process_time_config JSON`
**Location:** `com.slaunchx.core.business.recharge.template.RechargeChannelTemplateService.lambda$parseProcessTimeConfig$6(RechargeChannelTemplateService.java:282)`

69.63.201.120 - - [04/Jun/2026:15:46:15 +0800] "GET /web/v1/recharge/template/page?page=0&size=10 HTTP/1.1" 500 ...

重复评论只放最新证据;完整堆栈保留在首次 issue 正文里。其他重复次数只更新 watcher 本地计数,不写 Gitea 评论。

标签

watcher 会尝试复用目标仓库已有标签:

kind:* 是根因类型标签:

标签不存在时会跳过,不影响 issue 创建。

状态文件

状态持久化在:

docker/logging/data/loki-issue-watcher/state.json

包含:

如果需要重新回放历史窗口,可以先停服务并备份/删除该状态文件。

验证命令

本地静态验证:

python3 -m py_compile \
  docker/logging/loki_issue_watcher/watcher.py \
  docker/logging/loki_issue_watcher/tests/test_watcher.py

python3 docker/logging/loki_issue_watcher/tests/test_watcher.py

Compose 验证:

cd docker/logging
docker compose --env-file .env.shared config
docker compose --env-file .env.shared build loki-issue-watcher
docker compose --env-file .env.shared run --rm --no-deps \
  loki-issue-watcher python -m py_compile watcher.py

运行验证:

docker logs --tail 200 slaunchx-loki-issue-watcher

期望看到:

常见问题

服务启动但不创建 issue

检查:

docker logs --tail 200 slaunchx-loki-issue-watcher

常见原因:

issue 找不到 Java 位置

可能原因:

处理方式:

重复 issue 太多

检查 normalized path 和 exception location:

issue 正文太长

watcher 默认限制:

超过会追加:

...[truncated by loki-issue-watcher]

关键字段不会因为堆栈截断而消失。