feat(observability): /health 不再产生 server span

容器 HEALTHCHECK 每 30 秒探一次、上游也在轮询,这些请求各自是一条孤立
trace,量大且没有信息量——和 worker 空转长轮询同一个问题,把观测后台刷满
的正是它们。

instrument_app 传 excluded_urls,由新增的 RAKUTEN_OTEL_EXCLUDED_URLS 控制
(默认 /health$)。按 search 匹配完整 URL,锚点保证不误伤 /api/health-*;
留空时传 None,回落到 OTel 自己的 OTEL_PYTHON_FASTAPI_EXCLUDED_URLS。

三个服务共用 instrument_app,因此抓取/交易/网关一并生效。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-28 15:24:52 +08:00
co-authored by Claude Opus 5
parent 3c7618a1d6
commit 4cc30bc058
5 changed files with 78 additions and 4 deletions
+5
View File
@@ -103,6 +103,11 @@ class Settings(BaseSettings):
otel_service_name: str = "rakuten" # 仅作兜底;实际值由两侧 main.py 显式覆盖
otel_headers: str | None = None # OTLP 鉴权头,形如 "k=v,k=v";当前 endpoint 裸跑,留空
otel_export_interval_ms: int = 5000
# 不产生 server span 的路径(逗号分隔正则,按 search 匹配完整 URL)。
# 默认排掉 /health:容器 HEALTHCHECK 每 30 秒探一次、上游也在轮询,这些请求
# 各自成为一条孤立 trace,量大且没有信息量——和 worker 空转长轮询同一个问题
# (见 telemetry.py::suppressed)。留空表示不排除任何路径。
otel_excluded_urls: str = "/health$"
# 解析失败时把页面 HTML 作为 span event 上报的上限字节;超出截断并标注。
# 单个搜索页 HTML 可达 200KB-2MB,调高时同步关注 OTLP 单次请求大小限制。
otel_snapshot_max_bytes: int = 2_000_000
+9 -2
View File
@@ -121,10 +121,17 @@ def instrument_app(app: "FastAPI") -> None:
照样产生 span),所以顺序不构成问题。
otel 关闭时跳过:省掉一层用不上的中间件。
`excluded_urls` 把健康检查挡在 server span 之外(默认 `/health$`):容器
HEALTHCHECK 每 30 秒探一次、上游也在轮询,这些请求各自是一条孤立 trace,
量大且没有信息量。配置留空时传 None,让 OTel 回落到它自己的
`OTEL_PYTHON_FASTAPI_EXCLUDED_URLS` 环境变量。
"""
if not get_settings().otel_enabled:
settings = get_settings()
if not settings.otel_enabled:
return
FastAPIInstrumentor.instrument_app(app)
excluded = settings.otel_excluded_urls.strip() or None
FastAPIInstrumentor.instrument_app(app, excluded_urls=excluded)
def shutdown_telemetry() -> None: