导入说明(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。
目标
- 自动发现 test 环境 Nginx access 里的 HTTP 5xx 和可行动 400。
- 自动关联 Java
FAILED/Exception details日志。 - 自动创建或更新
slaunchx/slaunchx-backend-platformissue。 - issue 正文必须自包含,开发者不需要 Loki 权限也能开始排查。
架构
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_ENV | test | 观察的环境 |
LOKI_ISSUE_JAVA_SERVICE_NAME | slaunchx-app-prometheus-test | Java 服务名 |
LOKI_ISSUE_POLL_INTERVAL_SECONDS | 60 | 轮询间隔 |
LOKI_ISSUE_LOOKBACK_SECONDS | 300 | 每次回看窗口 |
LOKI_ISSUE_OVERLAP_SECONDS | 120 | 与上次查询重叠,避免漏掉延迟日志 |
LOKI_ISSUE_JAVA_WINDOW_SECONDS | 10 | access 前后查 Java 日志的秒数 |
LOKI_ISSUE_DRY_RUN | 0 | 1 时只输出 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 也能定位问题,包含:
- 环境、host、外部请求、HTTP status、access 时间。
- Java 内部路径、requestId、traceId。
- 异常类型、异常 message、源码位置。
- 疑似问题层,例如
DB JSON format / Java deserialization compatibility。 - 前 15 条
com.slaunchx.*应用栈帧。 - access 原始行。
- Java failed 行和 exception details 行。
- 完整 stack trace 折叠块;超长时标记
[truncated by loki-issue-watcher]。 - 隐藏指纹 marker:
<!-- loki-500-fingerprint:<sha> -->或<!-- loki-400-fingerprint:<sha> -->。
正文结构:
<!-- 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。为避免刷屏,只在命中次数达到 2、3、5、10、20、50 时追加评论:
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 会尝试复用目标仓库已有标签:
bugauto-detectedportal:systemportal:tenantportal:consumerportal:partnerdomain:authdomain:paymentdomain:userdomain:walletkind:data-contractkind:jsonkind:db-schemakind:request-shape
kind:* 是根因类型标签:
kind:data-contract:枚举值、编码域、后端数据与代码模型不匹配。kind:json:JSON 格式或 JSON 反序列化问题。kind:db-schema:数据库 schema 与代码不匹配。kind:request-shape:请求结构、DTO 或参数反序列化问题。
标签不存在时会跳过,不影响 issue 创建。
状态文件
状态持久化在:
docker/logging/data/loki-issue-watcher/state.json
包含:
- 最近一次查询游标。
- 已处理 access event key。
- fingerprint 到 issue number 的映射。
- 同类命中计数。
如果需要重新回放历史窗口,可以先停服务并备份/删除该状态文件。
验证命令
本地静态验证:
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
期望看到:
- dry-run 模式:输出将创建的 issue title / fingerprint / labels。
- 正常模式:
created issue #...或updated issue #...。
常见问题
服务启动但不创建 issue
检查:
docker logs --tail 200 slaunchx-loki-issue-watcher
常见原因:
- 未配置
GITEA_TOKEN。 LOKI_ISSUE_DRY_RUN=1。- 查询窗口内没有新的 5xx access 或可行动 400 access。
- 状态文件已记录该 event,避免重复处理。
issue 找不到 Java 位置
可能原因:
- access 没有打到 Java,例如 Nginx upstream/network 失败。
- Java 日志晚于
LOKI_ISSUE_JAVA_WINDOW_SECONDS。 - host 到 portal 的推断不匹配。
处理方式:
- 临时调大
LOKI_ISSUE_JAVA_WINDOW_SECONDS。 - 检查 issue 正文里的 access 原始行和内部路径。
- 必要时按
requestId/traceId人工回查 Loki。
重复 issue 太多
检查 normalized path 和 exception location:
- 如果业务 ID 没被归一化,需要扩展
is_probable_id。 - 如果同一根因在不同 location 抛出,默认会拆成不同 issue,这是预期行为。
issue 正文太长
watcher 默认限制:
- issue body:约 60KB。
- full stack trace:约 42KB。
超过会追加:
...[truncated by loki-issue-watcher]
关键字段不会因为堆栈截断而消失。