Skip to content

fix(python): 补中间件服务端指标;中间件指标统一去掉 obs_ 前缀 - #12

Merged
TangJia025 merged 2 commits into
mainfrom
fix/2061-python-server-metrics
Sep 17, 2026
Merged

TangJia025 merged 2 commits into
mainfrom
fix/2061-python-server-metrics

Conversation

@TangJia025

@TangJia025 TangJia025 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

背景

spec/metrics-format.mddocs/微服务可观测性建设技术设计.md 都声明 SDK 中间件默认暴露
obs_http_server_* 两个指标,但 python/grep http_server 零命中 —— 文档、Go、Node 都有,
Python 独缺,能力矩阵里的「同上」是空头承诺。

补实现时顺带发现:obs_ 前缀」这套约定四语言里只有本 PR 的 Python 真的照做,Go / Node 注册的是
无前缀名、注释却写着 obs_,Java 走 Actuator 更无从带。因此本 PR 的后半段把命名口径反了过来,
以「不加任何前缀」为准(详见下一节)。

改动一:补中间件 HTTP 服务端指标

指标 label
http_server_requests_total method path status_code
http_server_request_duration_seconds 同上(直方图)
  • 新增 Metrics.http_server() → 懒注册的 _HttpServer 句柄,三个适配器共用。缓存是必需的
    prometheus_client 对同名指标重复注册会直接抛 ValueError,不缓存中间件多请求下必挂。
  • path路由模板:Starlette scope["route"].path / Flask url_rule.rule /
    Django resolver_match.route,取不到(404)归 unmatched。实测 FastAPI 在
    BaseHTTPMiddlewarecall_next 之后 scope["route"] 可见,不需要自行归一化。
  • 桶边界显式钉住 [0.005 … 10]prometheus_client 自带默认多出 .075/.75/7.5
    不钉就会与 Go / Node 的桶对不上。
  • collect_server_metrics=False 可整体关闭(服务已有等价 instrumentation 时)。
  • 视图抛异常时补记 500 再原样抛出,避免漏计。

改动二:命名口径反转 —— 中间件指标不加任何前缀

spec 原先要求中间件公共指标带 SDK 保留前缀 obs_。实际上:

位置 注册的名字 obs_
Go middleware / ginmw http_server_requests_total 否(但 3 处注释写着要用 obs_,与代码矛盾)
Node lib/middleware 同上 否(文件头注释同样写着 obs_
Python 本 PR 上一 commit 按 spec 加了 obs_
Java SDK 不注册,走 Actuator

即「带前缀」既没有实现基础,收益也不成立:同一条 series 已带 service label,名字里再编一遍是重复信息;
带前缀会让「同一指标名下的跨服务查询」失效;官方 instrumentation 的默认名(Micrometer 的
http_server_requests_seconds)本就不带这类前缀,自造前缀要多做一层名字映射,与「薄封装、不自研
instrumentation」冲突。故 spec 改为不加任何前缀,四语言实现与全部文档同步:

  • spec/metrics-format.md:删掉 obs_ 保留前缀规则并写明三条理由;指标名改为
    http_server_requests_total / http_server_request_duration_seconds / log_entries_total
  • duration 的 label 补上 status_code:Go / Node / Python 三个实现本来就带,契约是唯一掉队的一方
    (按状态码看时延,如只看 5xx 的 P99,是常见诉求)。
  • 「服务要求」补一句同构边界service/env/instance/community 是强约束,服务端指标的名字与其余
    label
    由各语言官方库的既定形状决定,SDK 只装配不改名 —— 所以 Java 的
    http_server_requests_seconds / status / uri 与另三语言的
    http_server_requests_total / status_code / path 并不逐字相同,跨语言告警规则请对齐通用 label。
  • Go / Node:修正 3 处「用 obs_ 前缀」的注释;Python:两个常量去前缀。
  • Java:namespace 的 javadoc 不再建议填 "obs_",改为「用来拼业务指标的 <service>_ 前缀」,无代码改动。
  • 文档:README.mdAGENTS.mdgo/README.mdnode/README.mdpython/README.md
    docs/微服务可观测性建设技术设计.mdobs_ 表述全部同步。

改动三:与 Java 对齐的五处

  • 部署级字段兜底(同 PR fix(java): 部署级字段兜底、community 跨请求串染 + ObsFilter 测试覆盖 #9 的 Java 修复):Metrics.__init__ 构造时即完成三级解析
    (显式参数 > OBS_* > 内置默认)。此前空值会被写进 series,与其它语言对不上、按 label 过滤静默漏数。
  • 统一 init 语义metrics.init() 改为「可重复调用、最后一次生效」(重建实例与注册表),
    log.init() 一致;此前是首次调用生效、后续参数被静默丢弃。
  • 不再改 root logger 级别log.init() 移除 root.setLevel(DEBUG) —— root 是宿主应用的全局开关,
    压到 DEBUG 会连带让应用自己挂在 root 上的 handler 开始收 DEBUG 记录。级别改设在
    get_logger() 交出的 logger 上;log.get_logger() 不带名字时返回 SDK 自己的 logger(obs_sdk)而不是 root。
  • [test] extra 补 fastapi/flask/django:中间件单测用 pytest.importorskip,不装会静默跳过
    —— 本地/CI 看着全绿而中间件实际零覆盖。CI 随之并成一条 pip install -e 'python[test]'

测试

  • 新增 tests/test_env.py(6 例)覆盖 _env 三级解析 —— 这层此前零覆盖,Java 的同类缺陷正是从这里漏过去的。
  • tests/test_middleware.py 重写:每个框架断言真实 text 输出里的 http_server_requests_total
    含路由模板(/items/{item_id} 而非 /items/12345)、404 → unmatched、异常 → 500、直方图桶边界。
  • test_metrics / test_loginit 重建、构造兜底、不动 root 级别三处。

验证

git stashpython/obs_sdk 旧实现跑同一套测试:

10 failed, 18 passed     # 失败项即上述新断言

恢复后(四语言全跑):

Python  28 passed, 0 skipped     # 收集数 28 说明三个框架都在真实运行,没有 importorskip 静默跳过
Go      go vet + go test ./... 全 ok
Node    10 pass / 0 fail
Java    38 tests, 0 failures (BUILD SUCCESS)

遗留(未处理)

  1. Go / Node 的 path label 用原始 URLmiddleware.gor.URL.Path、ginmw 的
    c.Request.URL.Path(gin 明明有 c.FullPath() 可用)、Node 的 req.originalUrl
    Python 用的是路由模板,三语言 path 语义不一致,且 Go / Node 有基数风险。属另一个改动,未动。
  2. docs/进度追踪.md:19(「存在无兜底缺陷」)与设计文档 §4.2.4 / R5 在 PR fix(java): 部署级字段兜底、community 跨请求串染 + ObsFilter 测试覆盖 #9 合入后已过时。
  3. 设计文档第 386 行的 UT 数量表已过时:现为 Go 31(对)、Python 28(表里 15)、Java 38(表里 19)、Node 10(对)。
    Java 的偏差来自 PR fix(java): 部署级字段兜底、community 跨请求串染 + ObsFilter 测试覆盖 #9,早于本 PR。

顺带修掉的文档错误(非本次主题,单独说明)

  • python/README.mdflask_middleware(app, resolver=lambda: "openeuler") 少一个参数,
    resolver 签名是 Callable[[request], Optional[str]],照抄会 TypeError
    同处 ObsMiddleware 类名写错(实为 DjangoMiddleware)。
  • obs_sdk/metrics.py 模块 docstring:metrics.counter("meeting_created_total", "…", "kind")
    把字符串当 labelnames 传,会被 list("kind") 拆成 ['k','i','n','d'] 四个 label,改为 ["kind"]

🤖 Generated with Claude Code

AI 使用声明

当前 PR 是否有 AI 参与:

python/ 此前缺 HTTP 服务端指标:docs/微服务可观测性建设技术设计.md 与
spec/metrics-format.md 都声明中间件默认暴露 obs_http_server_*,而 python/ 下
grep http_server 零命中,能力矩阵里的「同上」是空头承诺。

指标(spec/metrics-format.md「默认暴露的中间件指标」)
- 新增 Metrics.http_server() → 懒注册的 _HttpServer 句柄,FastAPI / Flask / Django
  三个适配器共用。缓存是必需的:prometheus_client 对同名指标重复注册直接抛 ValueError。
- path label 取**路由模板**:Starlette scope["route"].path / Flask url_rule.rule /
  Django resolver_match.route,取不到(404)归 unmatched。实测 FastAPI 在
  BaseHTTPMiddleware 的 call_next 之后 scope["route"] 可见,无需自行归一。
- 桶边界显式钉住 [0.005 … 10],与 Go / Node 对齐 —— prometheus_client 自带默认
  多出 .075/.75/7.5,不钉就与另两个语言对不上。
- collect_server_metrics=False 可关闭(服务已有等价 instrumentation)。
- 视图抛异常时补记 500 再原样抛出,避免漏计。

其余四处
- Metrics.__init__ 构造时即做三级解析(显式 > OBS_* > 内置默认),同 Java 修复:
  此前空值会被写进 series,与其它语言对不上、按 label 过滤静默漏数。
- metrics.init() 与 log.init() 统一为「可重复调用、最后一次生效」(重建实例与
  注册表),此前是首次调用生效、后续参数被静默丢弃。
- log.init() 不再 root.setLevel(DEBUG):root 是宿主应用的全局开关,会连带让应用
  自己挂在 root 上的 handler 开始收 DEBUG。级别改设在 get_logger() 交出的 logger
  上;log.get_logger() 不带名字时返回 SDK 自己的 logger(obs_sdk)而不是 root。
- pyproject [test] extra 补 fastapi/flask/django:中间件单测用 importorskip,不装
  会静默跳过 —— 本地/CI 全绿而中间件零覆盖。CI 随之并成 pip install -e 'python[test]'。

测试
- 新增 tests/test_env.py(6 例)覆盖 _env 三级解析;这层此前零覆盖,Java 的同类
  缺陷正是从这里漏过去的。
- tests/test_middleware.py 重写:每个框架断言真实 text 输出里的
  obs_http_server_requests_total,含路由模板(/items/{item_id} 而非 /items/12345)、
  404 → unmatched、异常 → 500、直方图桶边界。
- test_metrics / test_log 补 init 重建、构造兜底、不动 root 级别三处。

回归验证:只 stash python/obs_sdk 回旧实现跑同一套测试 → 10 failed / 18 passed,
失败项即上述新断言;恢复后 28 passed、0 skipped(收集数 28 说明三个框架都在跑)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
@opensourceways-bot

Copy link
Copy Markdown

Welcome To opensourceways Community

Hey @TangJia025 , thanks for your contribution to the community.

Bot Usage Manual

I'm the Bot here serving you. You can find the instructions on how to interact with me at Here . That means you can comment below every pull request or issue to trigger Bot Commands.

Contact Guide

If you have any questions, please contact the SIG: infratructure ,
and any of the maintainers: @GeorgeCao-hw, @TangJia025, @pkking, @zhongjun2 ,
and any of the committers: @GeorgeCao-hw, @TangJia025, @pkking, @zkhzkhz .

@opensourceways-bot

Copy link
Copy Markdown

CLA Signature Pass

TangJia025, thanks for your pull request. All authors of the commits have signed the CLA. 👍

@opensourceways-bot

Copy link
Copy Markdown

Linking Issue Notice

@TangJia025 , the pull request must be linked to at least one issue.
If an issue has already been linked, but the needs-issue label remains, you can remove the label by commenting /check-issue .

命名口径统一。spec 原先要求中间件公共指标带 SDK 保留前缀 obs_,但四语言里**只有
Python 真的带**(本 PR 上一个 commit 刚加),Go / Node 注册的是无前缀名、注释却写着
obs_,Java 走 Actuator 更无从带。前缀的收益也不成立:

- 同一条 series 已带 service label,名字里再编一遍前缀是重复信息;
- 带前缀会让「同一指标名下的跨服务查询」失效,每个服务得各写一套名字;
- 官方 instrumentation 的默认名不带这类前缀(Micrometer 的
  http_server_requests_seconds),自造前缀要多做一层名字映射,与「薄封装、
  不自研 instrumentation」冲突。

故 spec 改为**不加任何前缀**,四个实现与全部文档同步。

spec/metrics-format.md
- 删掉 obs_ 保留前缀规则,补上「为什么中间件指标不加前缀」的三条理由。
- 指标名改 http_server_requests_total / http_server_request_duration_seconds /
  log_entries_total(末者四语言都还没实现,改名只为口径一致)。
- duration 的 label 补上 status_code:Go / Node / Python 三个实现本来就带,
  契约是唯一掉队的一方(按状态码看时延是常见诉求)。
- 「服务要求」补一句同构边界:service/env/instance/community 是强约束,服务端指标的
  名字与其余 label 由各语言官方库的既定形状决定,SDK 不改名。

实现
- Python:两个常量去前缀(本 PR 引入,尚未合入,一并纠正)。
- Go:middleware.go 文件头与行内注释、ginmw.go 文件头从「用 obs_ 前缀」改为无前缀
  —— 此前这三处注释与代码注册的名字是矛盾的(代码一直是无前缀)。
- Node:middleware.js 文件头注释同上。
- Java:ObsSdkConfig / ObsMetrics 的 namespace javadoc 不再建议填 "obs_",改为
  「用来拼业务指标的 <service>_ 前缀」;Java 无代码改动。

文档:README.md、AGENTS.md、go/README.md、node/README.md、python/README.md、
docs/微服务可观测性建设技术设计.md 的 obs_ 表述同步。

四语言验证:Python 28 passed / Go go vet + test ok / Node 10 pass / Java 38 passed。

Co-Authored-By: Claude Code <noreply@anthropic.com>
@opensourceways-bot

Copy link
Copy Markdown

CLA Signature Pass

TangJia025, thanks for your pull request. All authors of the commits have signed the CLA. 👍

@TangJia025 TangJia025 changed the title fix(python): 补中间件服务端指标 obs_http_server_*,统一 init 语义与部署级字段兜底 fix(python): 补中间件服务端指标;中间件指标统一去掉 obs_ 前缀 Sep 17, 2026
@TangJia025
TangJia025 merged commit 0098a21 into main Sep 17, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants