生成语义约定库
此存储库中定义的 OpenTelemetry 语义约定代码可以自动生成。
OpenTelemetry 语言 SIG 组可以生成符合其语言习惯的语义约定代码,并可选择(或不选择)将其作为独立库发布。
本文档概述了通用模式,并提供了关于如何构建语义约定构件和生成代码的非规范性指导。
稳定性与版本控制
语义约定包含不同级别的稳定性。发布语义约定库的语言 SIG 组可以决定发布一个稳定的构件,其中包含语义约定的稳定部分,一个包含所有语义约定的预览构件,或者其他符合该语言习惯并提供 SemVer 2.0 稳定性保证的组合。
可能的解决方案包括
- 为给定版本生成所有语义约定到特定文件夹,同时保持旧版本不变。这由 opentelemetry-go 使用,但如果构件大小是一个问题,可能会有问题。
- 遵循特定语言的约定来注解不稳定的部分。例如,Python 中的语义约定将不稳定的属性放在
opentelemetry.semconv._incubating导入路径中,该路径被认为是(遵循 Python 下划线约定)内部的,可能会发生变化。 - 发布两个不同的构件:一个包含稳定的语义约定,另一个包含所有可用的约定。例如,Java 中的 语义约定 在两个构件中发布:
opentelemetry-semconv和opentelemetry-semconv-incubating。
注意:发布同一构件的两个版本(稳定版和预览版)可能会因钻石依赖问题而出现问题。例如,如果用户应用程序依赖于
semconv v1.0.0-preview,而某个库引入了对semconv v1.1.0的传递依赖(该版本不包含实验性约定),则后者将被解析,从而导致应用程序出现编译或运行时问题。
Instrumentation 库应依赖稳定的(部分)语义约定构件,或将相关定义复制到其自己的代码库中。不稳定的语义约定构件供最终用户应用程序使用。
已弃用的约定
建议为已弃用的属性、指标和其他约定生成代码。使用适当的注解将其标记为已弃用。约定具有 stability 属性,提供弃用时期的稳定性级别(development、alpha、beta、release_candidate 或 stable),以及 deprecated 属性,该属性描述了弃用原因,可用于生成文档。
- 达到稳定性的已弃用约定不应在未进行主要版本更新的情况下移除(根据 SemVer)。
- 在不稳定状态下被弃用的约定仍应生成并保留在预览(部分)语义约定构件中。这最大限度地减少了用户应用程序的运行时问题和中断性更改。
将稳定的约定定义保留在预览(部分)语义约定构件中。这可以防止语义约定稳定后用户代码中断。在预览构件中弃用稳定的定义,并在生成的文档中指向稳定的位置。例如,在 Java 中,属性 http.request.method 在稳定版和预览版构件中都被定义为已弃用(例如,io.opentelemetry.semconv.incubating.HttpIncubatingAttributes.HTTP_REQUEST_METHOD、io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD)。
语义约定构件结构
本节包含关于如何构建语义约定构件的建议。
- 构件名称
opentelemetry-semconv- 稳定约定opentelemetry-semconv-incubating-(如果适用)包含所有(稳定和不稳定)约定的预览构件
- 命名空间:
opentelemetry.semconv和opentelemetry.semconv.incubating - 应列出所有支持的 Schema URL,以便同一应用程序中的不同 instrumentation 可以提供它们遵循的确切约定版本。
- 属性、指标和其他约定定义应按约定类型和根命名空间进行分组。请参阅下面的示例
├── SchemaUrls.code
├── attributes
│ ├── ClientAttributes.code
│ ├── HttpAttributes.code
│ └── ...
├── metrics
│ ├── HttpMetrics.code
│ └── ...
└── events
└── ...
生成语义约定
本节介绍如何使用 weaver 进行代码生成。
代码生成基于特定版本的语义约定中的 YAML 定义。通常,这涉及几个步骤,其中一些步骤可以半自动完成
- 手动更新配置文件中的语义约定版本
- 将新的 Schema URL 添加到支持的版本列表中
- 如果不是自动化的,那么它至少可以自动检查。
- 检出(或下载)新版本的语义约定
- 运行代码生成脚本(详细信息请参阅下文)
- 修复自动生成代码中的 lint 违规(如果有)
- 将包含新代码的 PR 发送到相应的存储库
以下是 Python 和 Erlang 实现步骤 2-5 的示例。 Python 和 Erlang。
步骤 4(运行代码生成)取决于特定语言的定制。它也是受工具迁移影响的唯一步骤。
从构建工具迁移
从 build-tools 迁移涉及更改 Jinja 模板并添加 weaver 配置文件。
Weaver 配置
这是一个生成所有属性的简化配置文件示例。
params:
excluded_namespaces: [ios, aspnetcore, signalr, android, dotnet, jvm, kestrel]
templates:
- pattern: semantic_attributes.j2
filter: >
semconv_grouped_attributes({
"exclude_root_namespace": $excluded_namespaces
})
| map({
root_namespace: .root_namespace,
attributes: .attributes,
output: $output + "attributes/"
})
application_mode: each
您可以在配置文件中 params 部分配置特定语言的参数,或在代码生成脚本中运行 weaver 命令时(类似于 build-tools)使用 -DparamName=value 参数传递它们。
Weaver 能够一次运行多个模板(在相应部分定义)的代码生成。
在执行 Jinja 之前,weaver 允许在每个模板的 filter 部分进行过滤或处理语义约定定义。在此示例中,它使用 semconv_grouped_attributes 过滤器 - 一个将属性定义按根命名空间分组并排除与该语言无关的属性的辅助方法。您可以使用 JQ 编写替代或附加过滤器并处理语义约定数据。
在某些情况下,使用命名空间排除和稳定性过滤器调用 semconv_grouped_attributes 可能就足够了,不需要进行后处理。
application_mode: each 配置 weaver 为每个语义约定组运行代码生成,并因此将代码生成到不同的文件中。还支持 single 应用程序模式,一次将模板应用于所有组。
请参阅 weaver 代码生成文档 以获取有关配置、数据模式、JQ 过滤器等的详细信息。
Jinja 模板
Jinja 模板需要进行更改以利用(更好的)数据结构和辅助方法。第一个主要区别是每个 jinja 模板都可以定义相应文件(或文件)的命名方式。如果您没有通过 set_file_name 方法指定输出文件的名称,Weaver 将使用相对路径和模板本身的名称来确定输出文件。
例如,这里有一个示例,它在 output 参数中提供的子文件夹中使用根命名空间。
{% set file_name = ctx.output + (ctx.root_namespace | snake_case ) ~ "_attributes.py" -%}
{{- template.set_file_name(file_name) -}}
数据结构上的显著变化
attributes_and_templates->ctx.attributesenum_attributes->ctx.attributes | select("enum")metrics->ctx.metricsroot_namespace->ctx.root_namespace(仅在使用semconv_grouped_attributes或类似过滤器时可用)- 所有自定义参数都作为
ctx变量下的属性提供。 attribute.fqn->attribute.nameattribute.type | instantiated_type(获取枚举值的底层类型)attribute.attr_type.members->attribute.type.members(获取枚举成员)member.member_id->member.id(获取枚举成员的 ID)
辅助方法上的显著变化
attr.fqn | to_const_name->attr.name | screaming_snake_caseattr.fqn | to_camelcase(True)->attr.name | pascal_caseattr.brief | to_doc_brief | indent->attr.brief | comment(indent=4),请查看广泛的 注释格式配置- 稳定性/弃用检查
attribute is stable(如果检查单个属性),attributes | select("stable")用于过滤稳定的属性attribute is deprecated(如果检查单个属性),attributes | select("deprecated")用于过滤已弃用的属性
- 检查属性是否为模板:
attribute.type is template_type - 简化 switch-like 逻辑的新方法:
key | map_text("map_name")。地图可以在 weaver 配置中定义。这对于将语义约定属性类型转换为特定语言的类型非常有用。