Files
teaching-feedback-assistant/docs/observability-runbook.md
shay7sev a875fe9f63 feat(observability): add structured application logging
Emit correlated JSON logs from the API and FunASR services, propagate request IDs to the client, and configure bounded opt-in Docker logging.

Document the host-wide Grafana/Loki query workflow and keep observability deployment ownership outside the application repository.
2026-07-22 13:52:40 +08:00

175 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 中央日志接入与查询手册
教学反馈服务通过容器标签接入宿主机中央观测栈。本项目不包含也不启停 Loki、Alloy 或
Grafana中央栈位于 `/home/shay/data/docker-service/observability`Grafana 地址为
`http://192.168.193.237:39300`
## 登录 Grafana
1. 在可信局域网或已接入该主机网络的浏览器中打开 `http://192.168.193.237:39300`
2. 用户名使用 `admin`
3. 初始密码读取中央栈的
`/home/shay/data/docker-service/observability/.env` 中的 `GRAFANA_ADMIN_PASSWORD`
需要在目标机终端确认初始密码时使用:
```bash
sed -n 's/^GRAFANA_ADMIN_PASSWORD=//p' /home/shay/data/docker-service/observability/.env
```
不要把密码粘贴到 issue、提交信息、聊天记录或截图中。如果已经在 Grafana 页面修改过密码,
应使用修改后的密码;`.env` 只保证记录首次创建管理员时使用的值。
## 发布与接入检查
业务镜像构建时注入版本信息:
```bash
export APP_VERSION="$(git describe --tags --always)"
export GIT_SHA="$(git rev-parse --short HEAD)"
docker compose -f compose.deploy.yml config -q
docker compose -f compose.deploy.yml up -d --build --force-recreate
docker compose -f compose.deploy.yml ps
```
仅修改采集标签或日志轮转时不需要构建镜像:
```bash
docker compose -f compose.deploy.yml up -d --force-recreate --no-build api funasr
```
确认运行容器配置:
```bash
docker inspect teaching-feedback-api-1 --format '{{json .HostConfig.LogConfig}}'
docker inspect teaching-feedback-api-1 --format '{{json .Config.Labels}}'
docker inspect teaching-feedback-funasr-1 --format '{{json .HostConfig.LogConfig}}'
docker inspect teaching-feedback-funasr-1 --format '{{json .Config.Labels}}'
curl -fsS http://127.0.0.1:39180/health
```
预期日志驱动为 `local``max-size=20m``max-file=5`;两个服务都应包含
`observability.logs=true``observability.application=teaching-feedback`
`observability.environment=lan`
## 使用预置仪表盘
登录后打开 `Dashboards`,进入 `Host Observability / Host Service Logs`,然后设置:
- `Application``teaching-feedback`
- `Environment``lan`
- `Service`:按需选择 `api``funasr``All`
- 右上角时间范围:复现问题时优先选择最近 5 至 30 分钟
仪表盘适合浏览和筛选;需要精确关联请求或比较版本时,打开 `Explore`,数据源选择 `Loki`
## 常用 LogQL
查看教学反馈的全部日志:
```logql
{application="teaching-feedback"}
```
按服务、级别和文本筛选:
```logql
{application="teaching-feedback", service="api"} | json
{application="teaching-feedback", service="funasr"} | json
{application="teaching-feedback", level=~"WARN|ERROR"}
{application="teaching-feedback"} |= "database"
```
按 HTTP 状态和耗时寻找优化目标:
```logql
{application="teaching-feedback", service="api"} | json | status >= 500
{application="teaching-feedback", service="api"} | json | latency_ms >= 1000
{application="teaching-feedback", service="api"} | json | event="http_request_finished"
```
按关联 ID 追踪一次请求或转录任务:
```logql
{application="teaching-feedback", service="api"} | json | request_id="<request-id>"
{application="teaching-feedback", service="api"} | json | feedback_session_id="<session-id>"
{application="teaching-feedback", service="api"} | json | lesson_id="<lesson-id>"
{application="teaching-feedback", service="api"} | json | segment_id="<segment-id>"
{application="teaching-feedback", service="funasr"} | json | segment_id="<segment-id>"
```
按部署版本确认问题发生在哪份代码:
```logql
{application="teaching-feedback"} | json | app_version="0.2.0"
{application="teaching-feedback"} | json | git_sha="<git-sha>"
```
`request_id``segment_id``git_sha` 等字段位于 JSON 正文,不能直接放进流选择器的 `{...}`
也不能新增为 Loki 标签。查询无结果时先扩大时间范围,再从只包含 `application` 的查询逐步增加条件。
## 用日志定位代码
优化或修复代码时采用以下流程:
1. 复现问题,记录发生时间和小程序错误界面显示的 `request_id`
2.`request_id` 查询 API 的完整事件序列,确认状态码、`event``latency_ms`
`error_class`
3. 涉及录音时,从 API 日志取得 `segment_id`,分别查询 API worker 和 FunASR判断耗时发生在
排队、上传、推理还是结果写回阶段。
4.`app_version``git_sha` 确认运行代码版本,避免根据旧镜像日志修改新代码。
5. 修改后使用相同路径复现并比较事件数量、失败分类和耗时,不以“没有 ERROR”代替性能验收。
日志事件主要对应以下代码:
- HTTP 请求开始、结束、请求 ID`server/src/main.rs`
- API 错误分类和响应:`server/src/error.rs``server/src/recording_routes.rs`
- 转录队列、租约、重试和状态写回:`server/src/transcription_worker.rs`
- API 到本地 ASR 的请求关联:`server/src/speech.rs`
- FunASR 请求和推理耗时:`asr-service/app.py`
- 小程序请求 ID 展示与复制:`utils/api.ts` 及各页面的错误处理代码
## 历史范围
中央栈首次接入时回采了当时仍存在的 Docker 容器日志。2026-07-22 验收时可查询到的最早记录为:
- API2026-07-22 11:48:42北京时间
- FunASR2026-07-22 11:53:42北京时间
已删除容器中更早的日志无法恢复。Loki 当前保留期为 14 天Docker 本地日志还受到
`20m x 5` 轮转限制,因此长期比较应记录 `git_sha`、问题时间和关键查询结果,而不能假设日志永久存在。
## 故障定位
应用健康但 Grafana 无日志:
1. 检查业务容器是否具有采集标签。
2. 检查业务容器是否持续向 stdout/stderr 输出日志。
3. 检查中央栈运行状态;命令见中央目录 `README.md`
4. 使用 `{application="teaching-feedback"}` 查询,避免一开始叠加过多过滤条件。
Grafana/Loki 不可用时,不要重启或回滚业务容器来尝试修复中央栈。先确认:
```bash
curl -fsS http://127.0.0.1:39180/health
docker compose -f compose.deploy.yml ps
docker compose -f compose.deploy.yml logs --since 10m api funasr
```
如果本地 Docker 日志仍正常,则问题属于中央观测栈。中央栈的启停、数据卷、备份、恢复和升级
统一由 `/home/shay/data/docker-service/observability/README.md` 管理。
Grafana 不可用时,可以先用 Docker 本地日志继续定位:
```bash
docker compose -f compose.deploy.yml logs --since 30m api funasr
```
不要为了临时查询而把 Loki `3100` 或 Alloy `12345` 发布到宿主机。
## 应用侧回滚
结构化日志代码出现回归时,回滚对应代码并重建业务镜像。仅日志驱动不兼容时,修改
`compose.deploy.yml` 后必须 `--force-recreate`;普通 restart 不会更新容器日志驱动。任何应用侧
回滚都不得删除中央 Loki 或 Grafana 数据卷。