跳到主要内容

从 YAML 到产物 — Weaver 编译、Rego 策略、schema 迁移

本章讲机器:同一份 YAML 怎么变成人读文档、各语言代码,以及构建期有哪些闸门保证「改约定不闯祸」。三件事:① Weaver 生成,② Rego 策略,③ schema 版本迁移。

3.1 Weaver:唯一的编译器

YAML 本身不会自己变成文档。干这活的是 Weaver——一个外部工具,以固定版本的 Docker 容器引入(otel/weaver:v0.24.2,dependencies.Dockerfile:6)。本仓只调用它,不含它的源码。

Weaver 在本仓有两个主要用法(都在 Makefile):

make 目标Weaver 子命令产出
table-generationregistry update-markdown把字段表回填进 docs/**/*.md 里的标记区块(Makefile:164-178)
registry-generationregistry generate markdown生成 docs/registry/ 下的属性注册表文档(Makefile:185-197)

两者都把 model/(源)、templates/(模板)、docs/(目标)挂进容器。心智模型:

model/**/*.yaml ─┐
templates/** ├─▶ [ weaver 容器 ] ─▶ docs/**/*.md (表格被替换/生成)
│ ─▶ (下游仓:各语言常量代码)

关键设计——文档不是手写的,是生成的。 所以有个配套校验 table-check:用 --dry-run 跑同样的生成,如果结果和已提交的 docs 不一致就报错(Makefile:199-214)。这保证「YAML 改了但忘了重生成文档」会被 CI 抓住。各语言 SDK 同样用 Weaver 把这份 YAML 生成本语言常量,这就是「一处定义、全栈一致」的兑现方式。

3.2 Rego 策略:构建期的兼容性闸门

光生成还不够——改约定不能破坏已经发布的契约。这由 policies/*.rego(开放策略代理 OPA 的规则语言)在 make check-policies 时强制执行(Makefile:287-300)。

最关键的一行是它和谁比:

# Makefile:298-299 (check-policies 内)
--baseline-registry=https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/v$(LATEST_RELEASED_SEMCONV_VERSION).zip[model]
--policy=/home/weaver/policies

它下载最新发布版当 baseline,把当前 model 和它对比。policies/compatibility.rego 就建立在「baseline vs 当前」两套集合上做差异检查(policies/compatibility.rego:13-30,baseline_attributes / registry_attributes)。意图很明确:稳定字段不许被删或改类型,否则下游已经依赖它的代码会崩。

策略文件按关切分工(policies/ 目录):

策略文件把关什么
compatibility.rego向后兼容:稳定字段不许破坏性变更
deprecation.rego弃用/改名规则自洽(下一节细讲)
attribute_name_collisions.rego / constant_name_collisions.rego名字不许撞
attribute_types.rego类型合法
metric_brief_format.rego / metrics_collisions.regometric 命名与说明规范
group_stability.rego / entity_stability.rego稳定性标记一致

3.3 弃用与改名的策略自洽(deprecation.rego)

policies/deprecation.rego 是个很好的「策略即代码」样本。它检查一类错误:你把字段改名了,但新名字根本不存在或也被弃用了

# policies/deprecation.rego:31-40 (简化解读)
deny contains ... if {
group := input.groups[_]
startswith(group.id, "registry.") # 只查 registry 字段
attr := group.attributes[_]
attr.deprecated.renamed_to != null # 这字段声明改名了
not attr.deprecated.renamed_to in registry_attribute_names # 但新名不在「未弃用字段」集合里
# → 报错:renamed_to 指向了不存在/已弃用的字段
}

它还逐条校验改名前后类型要兼容:string→string、int→int、甚至 string → enum-of-strings 都允许(deprecation.rego:59-73),但 string → enum-of-ints 就拒。同样的检查覆盖 metric 改名(:161-170)和 event 改名(:172-181)。

为什么重要: 弃用不是删除,而是带迁移路径的退休。策略保证每条 renamed_to 都真的指向一个可用的新家,数据迁移才不会断链。

3.4 schema 版本文件:让改名可被机器执行

策略保证「改名声明自洽」,但运行时的老数据怎么自动升级到新名字?靠 schemas/<version> 文件——每个发布版一个,记录从上一版到本版的字段映射。

gen_ai token 改名的真实记录:

# schemas/1.27.0:29-30 (节选)
changes:
- rename_attributes:
attribute_map:
gen_ai.usage.completion_tokens: gen_ai.usage.output_tokens
gen_ai.usage.prompt_tokens: gen_ai.usage.input_tokens

这条映射让任何遥测处理器都能把老字段 gen_ai.usage.prompt_tokens 自动重写成新字段 gen_ai.usage.input_tokens——无需改采集端代码。schema 文件按版本号目录排列,从 1.4.0 一直到 1.42.0(schemas/ 目录),构成一条完整的迁移链。

三件事如何咬合成闭环:

改 YAML(声明 deprecated.renamed_to)

├─▶ check-policies: deprecation.rego 确认新名存在且类型兼容

├─▶ table-generation: 文档同步反映改名

└─▶ schemas/<新版>: 写 rename_attributes,老数据可自动迁移

声明、校验、文档、迁移四步对齐,一个字段才算「合规地改了名」。

3.5 代码地图

主题文件符号/目标名
Weaver 容器版本dependencies.DockerfileFROM otel/weaver:v0.24.2
文档生成Makefiletable-generation, registry-generation
文档与 YAML 一致性校验Makefiletable-check(--dry-run)
兼容性闸门 + baseline 对比Makefilecheck-policies
兼容性规则policies/compatibility.regobaseline_attributes, registry_attributes
弃用/改名自洽规则policies/deprecation.regodeny ... renamed_to 规则族
版本改名映射schemas/1.27.0rename_attributes.attribute_map