Files
teaching-feedback-assistant/docs/observability-implementation-plan.md

29 KiB
Raw Blame History

局域网服务日志与可观测性实施计划

  • 状态:待实施
  • 编写日期2026-07-22
  • 代码基线:v0.2.0 / 04e80b1
  • 目标部署机:192.168.193.237(实施时必须重新确认)
  • 当前 APIhttp://192.168.193.237:39180

新会话启动方式

新开 Codex 会话后,先发送下面的提示词:

请完整阅读 docs/observability-implementation-plan.md、DEPLOY_5700U.md、
compose.deploy.yml、server/src/main.rs、server/src/transcription_worker.rs、
asr-service/app.py 和 utils/api.ts。

把 observability-implementation-plan.md 作为本次任务的执行约束。
先检查 git、目标部署机、Docker 日志驱动、磁盘和现有容器状态,再从文档中
第一个未完成的阶段开始实施。每完成一个阶段就运行该阶段验收,不要跳过日志
脱敏、请求 ID、持久化、回滚验证或文档更新。除非我明确要求不要自行提交、
推送或修改目标机防火墙。

新会话开始后应先更新本文档顶部的代码基线、目标机 IP 和实际组件版本。本文中的 镜像版本、端口和资源阈值是实施起点,不得替代实施时的现场检查。

1. 目标

本计划要建立一条可从小程序故障追溯到具体代码版本的日志链路:

小程序请求
  -> Rust API 结构化请求日志
     -> 转录队列事件
        -> FunASR 结构化推理日志
           -> Docker 本地日志与轮转
              -> Grafana Alloy 采集
                 -> Loki 持久化与检索
                    -> Grafana 查询、面板和告警

完成后应支持:

  1. 任意可信局域网电脑通过浏览器访问 Grafana。
  2. 按环境、Compose 项目、服务、容器、日志级别、时间和代码版本筛选日志。
  3. request_idfeedback_session_idlesson_idsegment_id 关联 API 与 FunASR。
  4. 判断故障发生在哪个镜像版本和 Git 提交,避免按错误版本修改代码。
  5. 保存至少 14 天日志,并限制 Docker 原始容器日志占用。
  6. 在 Loki/Grafana 不可用时API 与 FunASR 仍可正常提供业务服务。
  7. 后续可在同一 Grafana 中逐步加入日志告警和 Prometheus 指标。

2. 非目标

第一轮不做以下工作:

  • 不部署 Kubernetes也不把现有单机 Compose 改成集群。
  • 不部署 Promtail它已从当前 Loki 版本线移除,新部署使用 Alloy。
  • 不在应用代码中直接调用 Loki API应用只写标准输出和标准错误。
  • 不在第一轮部署 Tempo、全链路追踪、Mimir 或 Elasticsearch。
  • 不采集请求体、反馈正文、转录文本、音频内容、Bearer Token 或数据库连接串。
  • 不把 Grafana、Loki、Alloy 或 Docker API 直接暴露到公网。
  • 不用日志替代业务数据库、审计表或音频持久化卷。

3. 当前状态与缺口

3.1 已确认状态

  • compose.deploy.yml 只包含 funasrmigrateapi
  • API 暴露宿主机 39180FunASR 只在 Compose 内部网络提供 10095
  • 规划时 GET /health 返回健康,数据库、语音和微信鉴权均已配置。
  • Rust API 已使用 tracingtracing-subscriber,转录 worker 已记录部分 worker_idsegment_id 和错误事件。
  • Rust 使用 TraceLayer::new_for_http(),但默认请求/响应事件为 DEBUG;镜像当前 RUST_LOG=info,不能依赖它提供完整访问日志。
  • Rust 日志当前是文本格式,没有稳定 JSON 字段、请求 ID 或代码版本字段。
  • FunASR 使用 Python logging,但 Uvicorn 通过 --no-access-log 关闭访问日志;当前主要 记录模型启动和推理异常,没有成功请求耗时。
  • Compose 没有服务级 logging 配置。若目标机未修改 Docker daemon通常会使用默认 json-file 且没有自动轮转,存在磁盘耗尽风险。
  • compose.deploy.yml 中 API 镜像仍标记为 teaching-feedback-api:0.1.0,与当前 v0.2.0 不一致。
  • 仓库没有 Loki、Alloy、Grafana、日志持久化卷、数据源自动配置或日志运行手册。

3.2 必须解决的问题

问题 影响 解决阶段
Docker 日志无显式轮转 宿主机磁盘可能被占满 阶段 1
API 请求日志不完整 无法定位具体失败请求 阶段 2
API 与 ASR 缺少统一关联 ID 跨服务排障依赖时间猜测 阶段 2
日志没有版本元数据 无法映射到正确代码 阶段 1、2
日志仅保存在单机 Docker 中 其他电脑无法方便检索历史 阶段 3
没有保留期和持久化验证 重启或磁盘增长不可控 阶段 3
没有基于实际基线的告警 故障只能人工发现 阶段 4
没有时序指标 无法可靠发现“没有日志”的故障 阶段 5

4. 目标架构

应用栈和可观测性栈使用两份 Compose 文件独立管理:

compose.deploy.yml                    compose.observability.yml

api -------- stdout/stderr ----+      alloy ----> loki ----> grafana:3000
funasr ----- stdout/stderr -----+        |          |             |
migrate ---- stdout/stderr -----+--------+       loki-data     grafana-data
                                         |
                                 Docker Engine API
                                 /var/run/docker.sock

约束:

  • apifunasrmigrate 不依赖 Loki、Alloy 或 Grafana 启动。
  • Alloy 通过 Docker Engine API 发现带指定标签的容器并读取日志。
  • Loki 与 Alloy 不发布宿主机端口。
  • 只有 Grafana 发布到可信局域网,建议默认使用 ${GRAFANA_PORT:-39300}:3000
  • 局域网外访问使用 WireGuard、Tailscale 或 SSH 隧道,不做公网裸端口。
  • Grafana 必须启用登录;禁止匿名访问。
  • Loki 初始使用单体模式、TSDB 索引和本地文件系统卷,接受单机无高可用的现实。
  • Loki 默认保留 14 天;稳定运行并确认磁盘余量后再考虑 30 天。

5. 日志字段规范

5.1 公共字段

Rust API 和 FunASR 每条应用日志尽量使用一致字段:

字段 示例 说明
timestamp 2026-07-22T02:00:00.123Z RFC 3339 UTC 时间
level INFO TRACE/DEBUG/INFO/WARN/ERROR
service api apifunasrmigrate
environment lan 固定低基数字段
app_version 0.2.0 发行版本
git_sha 04e80b1 构建或部署提交
event http_request_finished 稳定、可查询的事件名
request_id UUID 单次 HTTP 请求关联 ID不作为 Loki 标签
message 简短英文或中文 人类可读说明

容器、Compose 项目、Compose 服务、镜像等字段由 Alloy 从 Docker 元数据补充。

5.2 API 请求字段

  • method
  • route:优先路由模板,例如 /api/v1/profiles/{profile_id}
  • path:必要时保留原始路径,但不得包含敏感 query 参数
  • status
  • latency_ms
  • request_id
  • error_class:只在失败时记录稳定分类

不要记录完整请求头。尤其禁止记录 Authorization、Cookie、微信 code、AppSecret 和 上传内容。

5.3 语音链路字段

  • feedback_session_id
  • lesson_id
  • segment_id
  • asr_request_id
  • worker_id
  • audio_format
  • audio_size_bytes
  • audio_duration_ms
  • queue_wait_ms
  • inference_latency_ms
  • provider
  • attempt
  • status

禁止记录本地音频绝对路径、热词原文、转录文字或反馈正文。

5.4 Loki 标签规范

仅将低基数字段设为 Loki 标签:

  • environment
  • compose_project
  • compose_service
  • container
  • service
  • level
  • app_version

以下字段只能保留在 JSON 日志正文中,通过 | json 查询,不能成为标签:

  • request_id
  • 用户 ID、学生 ID、会话 ID、课节 ID、片段 ID
  • URL 原始路径
  • 错误消息

6. 计划新增或修改的文件

文件 动作 目的
compose.deploy.yml 修改 日志轮转、容器筛选标签、版本环境变量、镜像版本
server/Cargo.toml 修改 开启 tracing-subscriber/jsontower-http/request-id
server/src/main.rs 修改 JSON 日志、请求 ID、INFO 请求完成日志、启动版本日志
server/src/config.rs 修改 读取 LOG_FORMATAPP_VERSIONGIT_SHAAPP_ENV
server/src/transcription_worker.rs 修改 增加队列等待、尝试次数、ASR 耗时和关联字段
server/src/speech.rs 修改 segment_id/请求 ID 传给 FunASR
server/.env.example 修改 记录非敏感日志配置项
utils/api.ts 修改 保存响应 X-Request-IDApiRequestError
相关小程序错误状态 修改 在可复制位置显示请求编号,不塞入短 Toast
asr-service/app.py 修改 请求 ID 中间件、成功/失败耗时和结构化字段
asr-service/logging_config.py 新增 标准库 JSON formatter 与日志初始化
asr-service/Dockerfile 修改 传入版本环境,继续避免重复 Uvicorn access log
compose.observability.yml 新增 Alloy、Loki、Grafana 三个服务
observability/alloy/config.alloy 新增 Docker 发现、筛选、标签、转发配置
observability/loki/loki.yml 新增 单体 TSDB、文件系统、保留期配置
observability/grafana/provisioning/datasources/loki.yml 新增 自动创建 Loki 数据源
observability/grafana/provisioning/dashboards/ 后续新增 阶段 4 面板和告警
observability/.env.example 新增 端口、版本、管理员账号变量说明,不含密码
.gitignore 修改 忽略 observability/.env、本地数据和导出文件
docs/observability-runbook.md 新增 启停、查询、备份、升级、故障处理
DEPLOY_5700U.md 修改 把日志栈加入部署和验收流程

7. 分阶段实施

阶段 0现场基线与风险检查

目标:在修改配置前记录目标机真实状态。

在目标部署机执行:

cd /path/to/teaching-feedback-assistant
git status --short --branch
git rev-parse HEAD
git tag --points-at HEAD

docker version
docker compose version
docker info --format '{{.LoggingDriver}}'
docker compose -f compose.deploy.yml ps
docker compose -f compose.deploy.yml images
docker compose -f compose.deploy.yml logs --since 1h api funasr
docker inspect teaching-feedback-api-1 --format '{{json .HostConfig.LogConfig}}' || true

df -h
docker system df
timedatectl status
ss -lntp | grep -E ':(39180|39300|3100|12345)\b' || true

实施要求:

  1. 不输出 server/.env 内容。
  2. 记录 Docker 当前日志驱动、容器名、镜像 ID、重启次数和日志占用。
  3. 确认 39300 是否可用;冲突时选择其他端口并更新本文档。
  4. 确认至少有 10 GB 可用空间给日志卷;不足时先清理或降低保留期。
  5. 确认目标机时钟同步。日志统一为 UTCGrafana 按浏览器时区显示。
  6. 确认 API、FunASR 和真实转录链路在改造前正常。

验收:把基线命令和非敏感结果写入实施记录,任何失败先解释,不进入阶段 1。

阶段 1Docker 日志轮转与版本元数据

目标:即使集中日志尚未部署,容器日志也不能无限增长。

compose.deploy.yml 增加可复用配置:

x-default-logging: &default-logging
  driver: local
  options:
    max-size: "20m"
    max-file: "5"

funasrmigrateapi 使用该配置。优先使用服务级配置,不修改 Docker daemon 全局默认,避免影响目标机的其他项目。

同时增加:

labels:
  observability.logs: "true"
  observability.environment: "lan"
environment:
  APP_ENV: lan
  APP_VERSION: ${APP_VERSION:-0.2.0}
  GIT_SHA: ${GIT_SHA:-unknown}
  LOG_FORMAT: ${LOG_FORMAT:-json}

要求:

  1. API 镜像标签不得继续硬编码为 0.1.0
  2. 实施时从 Git tag/commit 注入 APP_VERSIONGIT_SHA
  3. 镜像版本必须固定,不使用 latest
  4. local 日志驱动的配置值必须是字符串。
  5. 修改日志驱动后必须重建容器;仅 restart 不会更新已有容器的日志驱动。

验证:

docker compose -f compose.deploy.yml config
docker compose -f compose.deploy.yml up -d --build --force-recreate
docker compose -f compose.deploy.yml ps
docker compose -f compose.deploy.yml logs --since 10m api funasr
docker inspect <api-container> --format '{{json .HostConfig.LogConfig}}'
docker inspect <funasr-container> --format '{{json .Config.Labels}}'
curl -fsS http://127.0.0.1:39180/health

阶段 1 完成标准三个服务使用受控日志驱动API 健康且真实转录仍成功。

阶段 2结构化请求日志与跨服务关联

2.1 Rust API

依赖调整:

tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tower-http = { version = "0.6", features = ["cors", "trace", "request-id"] }

实现要求:

  1. LOG_FORMAT=json 时使用 tracing_subscriber::fmt().json(),关闭 ANSI并保留当前 span。
  2. 本地开发允许 LOG_FORMAT=pretty,但生产 Compose 固定 json
  3. 使用 tower_http::request_id
    • 接受合法的客户端 X-Request-ID
    • 缺失时用 MakeRequestUuid 生成。
    • 在响应头中传播同一个 X-Request-ID
  4. 中间件顺序按 tower-http 要求设置:先 Set Request ID再 Trace最后 Propagate。
  5. TraceLayer 的请求开始、请求结束和失败级别显式设置为 INFO/ERROR,不依赖默认 DEBUG
  6. make_span_with 不记录 headers记录 methodroute/pathrequest_id
  7. on_response 记录 statuslatency_ms
  8. 5xx/超时记录 ERROR,可预期 4xx 不得全部当成系统错误。
  9. 启动时记录 serviceenvironmentapp_versiongit_sha、监听地址和语音提供方。
  10. 不在日志中输出 DATABASE_URL、微信密钥、Summary API Key 或任何 Token。

补充单元/集成测试:

  • 无请求 ID 时响应包含合法 UUID。
  • 带合法请求 ID 时响应原样传播。
  • 日志中不包含 Authorization 值。
  • 200、400、401、500 都产生一次完成事件。
  • JSON 日志每行可被 jq 解析。

2.2 转录 worker 与 Rust -> FunASR

  1. 在 claim job 时取得 processing_started_attranscription_attempts 等必要字段。
  2. segment_id 作为跨 API/ASR 的稳定业务关联字段。
  3. Rust 请求 FunASR 时设置 X-Request-ID;可直接使用 segment_id,或生成新的 asr_request_id 并同时记录两者。
  4. 记录排队等待、音频读取、HTTP 调用、推理完成和数据库更新的分段耗时。
  5. 成功日志不得包含 transcript失败日志只记录经过清理的错误分类和消息。

2.3 FunASR

  1. 新增标准 JSON formatter不因日志系统引入另一套应用框架。
  2. 中间件读取/生成 X-Request-ID,并写回响应头。
  3. 记录 asr_request_startedasr_request_finishedasr_request_failed
  4. 成功和失败均记录 statuslatency_msaudio_size_bytesaudio_duration_msaudio_formatrequest_id
  5. 保留 --no-access-log,避免 Uvicorn 默认访问日志和自定义日志重复。
  6. 不记录临时路径、文件名、hotword、模型输出文本或上传内容。

2.4 小程序错误编号

  1. ApiRequestError 增加可选 requestId
  2. wx.requestwx.uploadFile 都从响应头读取 X-Request-ID,兼容响应头大小写。
  3. 短 Toast 不塞入长 UUID需要持久显示错误的状态区增加“请求编号”与复制入口。
  4. 开发者控制台可以记录请求编号,但不得打印 Token、上传路径或请求正文。

阶段 2 验收:

cd server
cargo fmt --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings

cd ..
npm run typecheck
docker compose -f compose.deploy.yml config

再用一个固定 X-Request-ID 调用 /health 和一个授权业务接口,确认 API 日志可按该 ID 检索。使用真实音频验证 segment_id 能关联 Rust 与 FunASR且日志中没有转录正文。

阶段 3部署 Alloy、Loki、Grafana

3.1 版本选择

实施时查阅官方 release notes选择互相兼容的稳定版本并固定完整版本号

  • grafana/alloy:<version>
  • grafana/loki:<version>
  • grafana/grafana:<version>

禁止使用 latest。将选定版本写入 observability/.env.example 和实施记录。

3.2 Compose 约束

新增 compose.observability.yml,项目名建议为 teaching-feedback-observability

loki

  • 单体模式。
  • 配置文件只读挂载。
  • /var/loki 使用 loki-data 持久化卷。
  • 不发布 3100 到宿主机。
  • 提供 readiness 健康检查。
  • restart: unless-stopped

alloy

  • 配置文件只读挂载。
  • /var/lib/alloy/data 使用 alloy-data,保存 Docker 读取位置。
  • 初始部署读取 /var/run/docker.sock
  • Docker Socket 挂载即使标记只读,也不是完整的 API 权限隔离;容器必须使用固定镜像、 最小权限且不对外开放 Alloy UI。安全要求提高时改为受限 Docker Socket Proxy。
  • 不发布 Alloy 12345 到宿主机。
  • restart: unless-stopped

grafana

  • /var/lib/grafana 使用 grafana-data
  • 默认发布 ${GRAFANA_PORT:-39300}:3000
  • GF_AUTH_ANONYMOUS_ENABLED=false
  • 管理员密码仅放在未提交的 observability/.env 或 Docker Secret。
  • 自动挂载 Loki 数据源 provisioning。
  • restart: unless-stopped

3.3 Loki 配置

observability/loki/loki.yml 至少满足:

  • auth_enabled: false,但 Loki 只存在于内部 Docker 网络。
  • 单体 target=all
  • TSDB schema不使用已弃用 BoltDB index。
  • chunks、rules、WAL 和 compactor 目录全部落在 /var/loki
  • compactor retention 开启。
  • 初始 retention_period: 336h14 天)。
  • 禁用匿名 usage analytics若当前版本支持对应配置
  • 设定合理 ingestion/query 限制,防止一次错误查询拖垮单机。

文件系统存储没有副本,也不会按磁盘剩余空间自动删除;保留期之外仍要监控磁盘。

3.4 Alloy 配置

observability/alloy/config.alloy 应包含:

  1. discovery.docker 连接 unix:///var/run/docker.sock
  2. discovery.relabel 只保留 observability.logs=true 的容器。
  3. 从 Docker 元数据映射 compose_projectcompose_servicecontainerimage
  4. loki.source.docker 读取容器日志并保存 positions。
  5. loki.process 添加 environment=lan,解析 JSON 日志级别;解析失败时保留原始行。
  6. 不把 request_id、用户或业务 ID 提升为标签。
  7. loki.write 发送到 http://loki:3100/loki/api/v1/push

3.5 Grafana 自动配置

Loki 数据源通过 provisioning 创建:

apiVersion: 1
datasources:
  - name: Loki
    type: loki
    access: proxy
    url: http://loki:3100
    isDefault: true
    editable: false

首次只创建数据源和一个最小“日志浏览”面板,不提前加入未经验证的告警阈值。

3.6 启动与验证

docker compose -f compose.observability.yml config
docker compose -f compose.observability.yml up -d
docker compose -f compose.observability.yml ps
docker compose -f compose.observability.yml logs --since 10m alloy loki grafana
curl -fsS http://127.0.0.1:39300/api/health

验收:

  1. 浏览器可从另一台局域网电脑打开 http://192.168.193.237:39300
  2. 未登录不能查看日志。
  3. Grafana Explore 能看到 apifunasr
  4. 以下查询可用,实际标签名以 Alloy 配置为准:
{compose_service="api"} | json
{compose_service="api"} | json | request_id="<request-id>"
{compose_service="funasr"} | json | segment_id="<segment-id>"
{compose_service="api"} | json | status >= 500
  1. 重启 Alloy 后不大量重复采集旧日志。
  2. 重启整套可观测性 Compose 后Grafana 数据源、账号配置和 Loki 历史仍存在。
  3. 停止 Loki/Alloy/Grafana 后API 和 FunASR 继续正常工作。
  4. Loki、Alloy 的宿主机端口不可访问。

阶段 4基于实际日志建立面板与告警

先运行至少 37 天,观察正常流量、错误数量和转录耗时,再设置阈值。

首批面板:

  • API 请求总量、4xx、5xx。
  • API latency_ms 的 p50/p95/p99。
  • 各路由错误日志列表。
  • 转录成功、失败和重试数量。
  • ASR 推理耗时和音频时长比值。
  • ASR service is unavailable 与恢复事件。
  • app_version 对比错误率。

首批日志告警候选:

  • 5 分钟内 API 5xx 达到 3 次。
  • 10 分钟内出现转录失败。
  • 5 分钟内持续出现数据库查询失败。
  • ASR 不可用持续超过 5 分钟。
  • Loki ingestion 或 Alloy target unhealthy。

告警要求:

  1. 每条告警必须提供可直接打开的 Grafana 查询。
  2. 配置 for 持续时间,避免瞬时启动日志触发。
  3. 通知内容包含服务、环境、版本和时间范围,不包含用户数据。
  4. 先发送到低风险通知渠道进行一周观察,再升级为正式告警。
  5. 告警规则和 dashboard JSON 纳入 Git禁止只在网页里手工保存。

Grafana 可直接基于 Loki 日志创建告警但“服务完全无日志”“CPU/内存异常”“磁盘即将 耗尽”更适合指标,留到阶段 5。

阶段 5按实际故障增加指标采集

日志稳定后再引入 Prometheus 指标。Alloy 可以抓取指标,但它不是长期指标存储;自托管 方案需要增加 Prometheus或把指标 remote write 到已有兼容后端。

建议指标:

Rust API

  • http_requests_total{route,method,status_class}
  • http_request_duration_seconds{route,method}
  • transcription_queue_jobs{status}
  • transcription_job_duration_seconds
  • transcription_job_retries_total
  • database_pool_connections{state}
  • asr_available

FunASR

  • asr_requests_total{status}
  • asr_inference_duration_seconds
  • asr_audio_duration_seconds
  • asr_inflight_requests
  • asr_failures_total{reason}

宿主机与容器:

  • node-exporterCPU、内存、磁盘、负载。
  • cAdvisor 或 Alloy Docker integration容器 CPU、内存、重启和网络。
  • upAPI、FunASR、Loki、Alloy、Grafana 抓取状态。

指标标签禁止包含请求 ID、学生 ID、会话 ID 或原始 URL。路由必须使用模板路径避免 高基数。

阶段 5 告警优先级:

  1. 磁盘剩余小于 15%。
  2. API/FunASR up == 0 持续 2 分钟。
  3. 转录队列持续增长或最老任务等待超阈值。
  4. API p95 延迟持续异常。
  5. 容器反复重启或内存逼近限制。

只有在日志和指标仍无法解释跨服务延迟时,才评估 OpenTelemetry + Tempo tracing。

8. 安全与隐私要求

禁止进入日志

  • Authorization、Cookie、微信登录 code、session key。
  • WECHAT_APP_SECRET、数据库密码、完整 DATABASE_URL
  • Summary/LLM API Key。
  • 学生姓名、家长称呼、反馈正文、转录全文、hotword。
  • 音频内容、完整本地路径、上传临时文件名。
  • 完整请求/响应 body。

允许但不作为标签

  • 内部 UUIDprofile/session/lesson/segment。
  • request ID。
  • 清理后的错误消息。

网络与权限

  • Grafana 只允许可信局域网 CIDR 或 VPN 访问。
  • Loki、Alloy 和 Docker API 不发布到宿主机。
  • Alloy 的 Docker Socket 权限视为高权限,固定镜像版本并限制可访问人员。
  • Grafana 管理员密码不得提交;首次登录后更换默认密码。
  • Scalar 和 Grafana 都属于运维入口,未来若局域网信任边界扩大,应统一加反向代理、 HTTPS 和认证。

9. 容量、保留与备份

初始值:

  • Docker 原始日志:每容器 20 MB x 5
  • Loki14 天保留。
  • 每日检查:df -hdocker system df、Loki 卷大小。
  • 预警:磁盘剩余 20% 开始观察15% 告警10% 紧急处理。

备份:

  • grafana-data:保存数据源、用户和本地状态;虽然 provisioning 在 Git 中,仍需备份。
  • loki-data:若历史日志不是关键资产,可不做强一致备份,但必须明确接受丢失风险。
  • 配置文件和 dashboard/alert provisioning 必须进入 Git。
  • 不备份 Alloy positions 时会导致重采或漏采风险,因此 alloy-data 应持久化。

备份和恢复命令在实施时写入 docs/observability-runbook.md,并实际演练一次 Grafana 配置 恢复和 Loki 重启持久性。

10. 回滚方案

可观测性栈必须可独立回滚:

docker compose -f compose.observability.yml down

该操作不能停止 compose.deploy.yml 的业务服务。默认不加 -v,避免误删日志和 Grafana 数据。

应用日志回滚:

  1. LOG_FORMAT=pretty 可暂时恢复文本输出,不需要改业务代码。
  2. 若请求中间件引发问题,回退对应提交并重建 API 镜像。
  3. local 日志驱动与采集不兼容,回退 Compose 日志块后 --force-recreate 容器; restart 不足以恢复日志驱动。
  4. 每阶段使用独立提交,禁止把业务功能改动混进日志基础设施提交。

删除数据前必须先确认:

docker volume ls | grep teaching-feedback
docker compose -f compose.observability.yml down
# 只有用户明确确认后才允许删除 loki-data/grafana-data/alloy-data

11. 建议提交拆分

  1. ops(logging): configure container log rotation and version labels
  2. feat(logging): add structured request and transcription logs
  3. ops(observability): add alloy loki and grafana stack
  4. docs(observability): add operations and troubleshooting runbook
  5. feat(observability): add validated dashboards and alerts
  6. feat(metrics): expose service and transcription metrics(阶段 5单独实施

每个提交前运行与其影响范围相匹配的测试。部署修改未经目标机验收不得直接打 release tag。

12. 最终验收清单

  • 目标机 Docker 日志驱动和轮转参数已记录。
  • API、FunASR、migrate 均使用受控日志轮转。
  • Rust 与 FunASR 每行输出有效 JSON。
  • API 响应返回 X-Request-ID
  • 小程序能保留并展示失败请求编号。
  • 真实录音可通过 segment_id/request_id 关联 Rust 与 FunASR。
  • 日志中未发现 Token、密钥、学生信息、反馈正文或转录全文。
  • Alloy 只采集明确标记的容器。
  • Loki 与 Alloy 没有宿主机端口。
  • Grafana 需要登录且只在可信网络可达。
  • Loki 数据源由 provisioning 自动创建。
  • 14 天 retention 生效,磁盘增长已观察。
  • 重启可观测性栈后历史和配置仍存在。
  • 停止可观测性栈不影响 API 和 FunASR。
  • cargo fmt --checkcargo test、严格 Clippy 和 npm run typecheck 通过。
  • docs/observability-runbook.md 包含启停、查询、备份、升级和回滚命令。
  • 至少 37 天基线后再启用正式告警。
  • 指标阶段明确使用 Prometheus 或兼容存储,不误把 Alloy 当成指标数据库。

13. 官方参考资料

实施时重新核对最新稳定版本和配置语法:

14. 实施完成后的交付报告

新会话完成每个阶段后应报告:

  • 实际提交和部署版本。
  • 新增/修改文件与 git diff --stat
  • 目标机日志驱动、容器、端口、网络和卷状态。
  • API、FunASR、Alloy、Loki、Grafana 健康检查结果。
  • 一条真实请求和一条真实转录的关联查询结果。
  • 日志脱敏检查结果。
  • 数据保留、磁盘使用和备份/恢复验证。
  • 已配置告警及其基线依据。
  • 未完成项、已知风险和明确回滚命令。

报告不得输出密码、Token、完整数据库地址、学生信息、反馈正文或转录内容。