尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OpenTelemetry Collector 编码规范全解:命名约定、模块组织与运行时健壮性原则

OpenTelemetry Collector 编码规范全解:命名约定、模块组织与运行时健壮性原则 OpenTelemetry Collector 编码规范全解命名约定、模块组织与运行时健壮性原则【免费下载链接】opentelemetry-collectorOpenTelemetry Collector项目地址: https://gitcode.com/GitHub_Trending/op/opentelemetry-collector本文基于 OpenTelemetry Collector 仓库中的 编码规范系统梳理该项目的贡献质量标准从组件与 Go API 命名约定、模块组织规则到启动错误处理、优雅关闭、可观测性与破坏性变更管理feature gate 三阶段等运行时健壮性原则。读完后你将能够按照与核心贡献者一致的标准编写 receiver、processor、exporter 组件并正确组织 Go module、定义指标、处理错误与执行兼容性迁移。总则面向长期无人值守运行的代码质量标准Collector 被定位为接近生产质量的软件贡献的门槛与此匹配。规范开篇明确了三条总原则代码必须可读、以可维护性为先编码建议参照 Effective Go代码必须遵守一组健壮性原则这些原则对长期自主连续运行、无人直接交互的软件即 Collector 这类系统尤为关键新代码应当符合仓库推荐的库与默认值旧代码在被逐步改造期间暂可例外。以下各节逐一展开规范中的具体规则并结合仓库源码给出可验证的实现证据。命名约定组件命名配置标识符用 lower_snake_case所有组件receiver、processor、exporter、extension、connector的配置标识符MUST 使用lower_snake_case命名。需要特别注意区分两层命名配置标识符写在 YAML 配置与组件注册中的名称必须lower_snake_case例如memory_limiter而非memorylimiter、otlp_http而非otlphttpGo 包名仍遵循标准 Go 惯例全小写、无下划线例如标识符为memory_limiter的组件其 Go 包名为memorylimiterprocessor。仓库中可直接验证这一规则memory_limiter 处理器 声明type: memory_limiter其组件类型由component.MustNewType(memory_limiter)注册见 generated_status.go而所在包目录是processor/memorylimiterprocessor。规范还给出存量组件的迁移要求当前使用其他命名风格的组件 SHOULD 将lower_snake_case名称作为主标识符MAY 将旧名保留为弃用别名以兼容存量用户MUST 在 README 中记录迁移路径只有遵循lower_snake_case命名约定的组件才能标记为 stable。作为对照OTLP receiver 的类型是otlp单段名称天然符合规则且其 traces/metrics/logs 均已在metadata.yaml中标记为 stable。Go API 命名约定规范为 API 命名定义了八条强制模式目标是让调用意图从名字上就能看出模式规则示例构造器零值安全返回使用零值或仅依赖入参的变量前缀 MUST 为Newfunc NewKinesisExporter(kpl aws.KinesisProducerLibrary)构造器带业务默认值返回会影响业务逻辑的非零默认值前缀 MUST 为NewDefaultfunc NewDefaultKinesisConfig()返回建议默认配置可安全修改而不引发竞态动作型方法签名必须简洁反映实际逻辑不得做名字之外的额外工作func FilterAttributes(attrs []Attribute, match func(attr Attribute) bool) []Attribute只能过滤不得维护全局历史getter首字母大写、不用get前缀大写导出名是区分字段与方法的关键func (p *Person) Name() string而非getNamesetter使用Set前缀func (p *Person) SetName(newName string)包级预置默认变量前缀 MUST 为Defaultvar DefaultMarshallers map[string]pdata.Marshallers{...}单信号相关类型用信号名作形容词type TracesSink interface {...}跨信号类型与函数描述信号间关系函数以信号/类型作直接宾语type TracesToTracesFunc func(...) ...、func ConsumeTraces(...)、func CreateTracesExport(...)NewDefault模式在仓库中大量存在例如 config/configgrpc/configgrpc.go 中的func NewDefaultClientConfig() ClientConfig与后文Default Configuration一节呼应。配置结构体命名与结构配置结构体的命名遵循三条规则分离两类配置把用户在 YAML 中设置的配置与开发者在代码中设置的配置放进不同结构体Config后缀表示用户YAML配置例如configgrpc.ClientConfigSettings后缀表示代码侧配置例如component.TelemetrySettings避免与包名重复的冗余前缀例如用configgrpc.ClientConfig而非configgrpc.GRPCClientConfig。避免内嵌匿名结构体定义配置结构体时避免使用内嵌字段改用显式命名字段。规范给出三条理由反序列化兼容性内嵌结构体可能破坏自定义Unmarshal实现命名冲突即使 YAML 通过嵌套如sending_queue组织得当Go 代码中相同字段名仍会造成歧义清晰度命名字段让配置结构更明确、更易读。规范中的标准对照示例// ❌ BAD: 使用内嵌结构体 type ExporterConfig struct { exporterhelper.TimeoutConfig // embedded exporterhelper.QueueConfig // embedded exporterhelper.RetryConfig // embedded } // ✅ GOOD: 使用命名字段 type ExporterConfig struct { Timeout exporterhelper.TimeoutConfig mapstructure:timeout Queue exporterhelper.QueueConfig mapstructure:sending_queue Retry exporterhelper.RetryConfig mapstructure:retry_on_failure }兼容扁平 YAML 的squash标签当需要保持既有扁平 YAML 结构的向后兼容时可用mapstructure:,squash把嵌套结构体的字段拍平到父配置中// 命名字段 squash 标签保持扁平 YAML type Config struct { ClientConfig confighttp.ClientConfig mapstructure:,squash }这让 YAML 字段保持在顶层同时 Go 代码使用命名字段。规范强调新组件应优先使用显式嵌套不加squash嵌套结构更清晰。仓库中的 config/confighttp/keepalive_test.go 展示了ClientConfig ClientConfig \mapstructure:,squash 的实际用法。模块组织Go 项目惯例是把相关功能聚合成包而 Collector 进一步要求把相关包聚合成独立 module以便各部分 API 独立演进。规范给出八条规则每个顶层目录应是独立 module每个可被 Collector Builder 引用的组件应在独立 module 中OTLP receiver 就与其他 receiver 分开API 可能在不同包组中独立演进的考虑拆分为独立 module——例如 HTTP 与 gRPC 配置各自演进config/configgrpc与config/confighttp就是两个 module组件 module 名以组件种类作后缀OTLP receiver 位于receiver/otlpreceiver为父目录增加特定功能的 module 用父目录名作前缀configauth因属于config目录而带config前缀extensionauth同理带extension前缀测试辅助代码放在带test后缀的子 module例如component/componenttest跨多 module 使用的测试辅助放在internal/testutil将来要并入某 module 的实验包应建独立 module 并直接以集成后的目标名命名例如为pdata增加pprofile包时先建pdata/pprofilemodule要加入稳定 module 中既有包的实验代码可用同名加x前缀的子 module例如config/confighttp/xconfighttp。规则 7、8 在仓库中真实存在目录树中有 pdata/pprofile实验中的新信号与 config/confighttp/xconfighttpconfighttp的实验 API可对照 internal/testutil、component/componenttest 等命名验证上述规则。新增 module 的检查清单规范明确要求为新 module 添加 changelog 条目在 versions.yaml 中登记该 module——该文件按 stable/beta 等 module-set 分组管理各 module 版本当前 stable set 为 v1.66.0beta set 为 v0.160.0运行make crosslink确保整个代码库中的 module replace 关系正确必要时手工补充 replace更新 otelcorecol 清单 cmd/otelcorecol/builder-config.yaml 与 builder 测试开一个后续 PR 更新所有 go.mod 中的 pseudo-version。枚举类型规范为保持命名一致枚举enumeration模式同样被强制约束MUST 用类型定义声明如type Level int32底层类型只能是int或string枚举名应简洁描述用途若包名已表达实体则包名应并入枚举名——例如用component.Type而非component.ComponentType名字应传达有限分类的含义——pcommon.ValueType优于pcommon.Valuecomponent.Kind优于component.KindTypeKind本身已含分类语义枚举常量 MUST 以枚举类型名作前缀pcommon.ValueTypeStr对应pcommon.ValueType、pmetric.MetricTypeGauge对应pmetric.MetricType。推荐库与默认值场景推荐理由哈希标准库hash/fnv项目将其采纳为默认哈希方法效率高适用于非加密用途测试尽量使用t.Parallel()更多测试并行执行加速开发反馈循环仓库中仍有部分旧包未遵循该推荐正在逐步整改但新代码必须遵循。默认配置NewDefault 函数约定为保证向后兼容行为所有配置包都应提供NewDefault[配置名]函数创建配置的默认版本。两点关键限定不要求该函数返回可直接使用的配置——只要求默认值被正确设置。例如某字段如Endpoint无合理默认值时可设为交由用户填充有效值用户应当总是用该函数初始化配置结构体再按需覆盖字段。这与前述NewDefault命名约定一致可参见 configgrpc.NewDefaultClientConfig。健壮性原则错误与崩溃处理这一组规则是规范的核心针对的是长期自主运行软件的独特约束。启动时快速失败启动阶段必须校验配置配置无效就快速失败fail fast。理由人类更容易注意到进程启动时的问题而非运行很久之后的问题监控系统通常会自动标记启动期间以失败退出的进程便于发现问题Collector 应打印能说明问题的合理日志并以非零退出码结束确无干净退出方式时启动阶段崩溃是可接受的但应尽力做到记录日志并以明确退出码结束。把错误传播给调用者在main()之外不要崩溃或退出进程例如不要用log.Fatal或os.Exit启动阶段也不行。应当返回详细的错误由调用者妥善处理。因为除main外的包都可能被第三方应用导入使用第三方应完全掌握错误处理与进程终止的控制权。启动完成后绝不崩溃启动序列结束后的任何时刻都不得崩溃或退出 Collector 进程。原因运行中的 Collector 通常持有已接收但尚未导出的数据如队列与处理器中的缓冲。此时崩溃会直接丢失这些数据——因为 receiver 通常已向发送方确认接收ACK发送方不会重发。恶意输入处理不得因 receiver 或管线中任何位置的恶意输入而崩溃。Crash-only software 在某些场景成立但对 Collector 不适用启动阶段除外。机理是许多发送方在收不到 ACK 时会自动重发同一份数据——崩溃后重启Collector 会再次看到同样的数据、再次崩溃配合自动重试即形成无限崩溃循环。推荐处理方式在 receiver 发现的恶意输入通常应报告回发送方在管线其他位置尤其非同步处理的 processor可能已无法回复发送方两种情况下都建议维护一个计数恶意输入的指标。错误处理与重试对错误处理要严格不要忽略任何错误。对每个错误仔细判断它属于致命问题还是可通过重试消除的瞬时问题致命错误记录日志或计入内部指标给用户可见性瞬时错误设计重试策略并实现之通常采用指数退避连接与发送类重试的退避间隔应加入抖动jitter避免网络恢复或下游恢复时瞬间压垮目标。规范推荐具备上述能力的现成 backoff 库cenkalti/backoff来实现。日志约定记录组件的启动与关闭包括成功但不要过度成功日志保持最少——它们能帮助理解后续其他位置失败时的上下文对高频事件慎用日志避免日志洪水。尤其避免对每个接收或处理的数据项打日志——Collector 的设计目标是每秒处理数千 span 与指标。对这类事件应改为增加内部指标日志消息必须人类可读并包含理解发生了什么、在什么上下文所需的数据。执行外部进程的安全约束组件应避免基于用户输入包括来自网络或配置文件的输入执行任意外部进程与任意命令行参数否则可能被恶意构造的输入演变为任意远程代码执行。推荐限制若必须执行外部进程硬编码限定可执行文件的位置不要让用户输入决定完整路径尽可能把可执行文件名限制在编译期定义的硬编码列表内命令行参数不要直接取自用户输入而应间接组合必要时从用户输入派生值并尽量压缩参数取值空间。可观测性指标声明与生成用户开箱即应能观测组件状态详见 docs/observability.md。使用常规 helper 时关键事件会自动附带指标——例如 exporter 无需任何额外工作就有otelcol_exporter_sent_spans。自定义指标通过组件的metadata.yaml声明权威 schema 是 cmd/mdatagen/metadata-schema.yaml。规范以 tail sampling processor 为参考给出三种指标类型的声明示例histogram / counter / gaugetelemetry: metrics: # histogram 示例 processor.tailsampling.samplingdecision.latency: description: Latency (in microseconds) of a given sampling policy. unit: µs # from UCUM enabled: true histogram: value_type: int # 桶边界可以覆盖 bucket_boundaries: [1, 2, 5, 10, 25, 50, 75, 100, 150, 200, 300, 400, 500, 750, 1000, 2000, 3000, 4000, 5000, 10000, 20000, 30000, 50000] # counter 示例 processor.tailsampling.policyevaluation.errors: description: Count of sampling policy evaluation errors. unit: {errors} enabled: true sum: value_type: int monotonic: true # gauge 示例 processor.tailsampling.tracesonmemory: description: Tracks the number of traces current on memory. unit: {traces} enabled: true gauge: value_type: int在组件根目录执行go generate ./...后应生成三类文件documentation.md指标及其描述internal/metadata/generated_telemetry.go使用 OTel API 定义指标的代码internal/metadata/generated_telemetry_test.go生成代码的健全性测试。这些生成物在仓库中可实际看到例如 memory_limiter 的 generated_telemetry.go 自动生成了otelcol_processor_memory_limiter_accepted/refused_spans等指标且指标名将组件类型memory_limiter编入其中与命名规范呼应配套文档见 processor/memorylimiterprocessor/documentation.md。在组件代码中使用指标的方式是初始化 telemetry builder 并存为组件字段再调用其上生成的指标方法type tailSamplingSpanProcessor struct { ctx context.Context telemetry *metadata.TelemetryBuilder } func newTracesProcessor(ctx context.Context, settings component.TelemetrySettings, nextConsumer consumer.Traces, cfg Config, opts ...Option) (processor.Traces, error) { telemetry, err : metadata.NewTelemetryBuilder(settings) if err ! nil { return nil, err } tsp : tailSamplingSpanProcessor{ ctx: ctx, telemetry: telemetry, } // ... } // 记录测量值 tsp.telemetry.ProcessorTailsamplingSamplingdecisionLatency.Record(ctx, ...)资源使用约束限制 CPU、RAM 等资源的用量不得编写以不受控方式消耗资源的代码。例如有一个可存放未处理消息的队列就必须限制队列大小——除非有其他方式保证消费速率高于入队速率必须对正常负载与远超可接受阈值的异常负载都做性能测试确保异常负载下行为可预测。例如处理速度跟不上接收速度时不可无限分配内存直到 OOM而应有保护机制达到资源上限时丢弃数据并用暴露给用户的指标记录丢弃事实。优雅关闭Graceful Shutdown所有组件必须在component.Component接口定义的Shutdown()函数中准备好优雅关闭——该接口可见于 component/component.go。关闭时组件持有的在途数据必须尽快处理并转发或导出避免数据丢失。具体要求Shutdown()必须在Start()从未被调用、或组件已关闭的情况下仍然可安全调用被调用后必须立即停止接收新数据关闭 listener、拒绝新请求取消或停止Start()启动的所有后台操作返回前把缓冲数据冲刷flush到管线下一组件释放所有持有的资源连接、goroutine、文件句柄。Shutdown()接收的context.Context可能携带 deadline组件必须尊重该 context——取消或超期即返回不得无限阻塞组件生命周期在Shutdown()返回后结束之后不会再调用其任何方法之后可能用相同或不同配置创建新实例并启动如 live reload 场景。测试要求单元测试用单元测试覆盖重要功能贡献不得降低整体代码覆盖率与随时间提升覆盖率的目标一致关注单元测试的执行时间尽可能保持短小。测试库推荐为统一测试实践规范推荐期望校验断言github.com/stretchr/testify/assert必须满足才能继续的断言github.com/stretchr/testify/require模拟外部资源github.com/stretchr/testify/mockHTTP 交互校验标准库net/http/httptest。集成测试项目内鼓励集成测试可用容器镜像搭建本地版本。没有条件时强烈建议 mock 集成对象。CGO禁止使用 CGO可移植性差、跨操作系统管理外部库的复杂性高。例外处理若包确实 MUST 用 CGO必须在 README 中明确说明并给出依赖库的安装指引同时该包必须能编译并在no-op 模式下运行或向 Collector 报告需要 CGO的清晰错误/警告。语义约定SemConv兼容性给组件receiver、processor 等新增指标、属性或实体属性时先查OpenTelemetry Semantic Conventions 项目是否已定义对应约定并检查是否有 open issue 已提出相同或类似的约定若尚无定义组件 code owner 应先发起 SemConv 流程组件实现可以 draft PR 形式先行提交演示拟议 SemConv 的用法同时并行推进 SemConv 项目本身的贡献SemConv PR 由组件 code owner 与既有的领域 SemConv approver 协作评审code owner 有权在相关 SemConv 变更完成前阻塞组件实现 PR。遥测稳定性级别指标稳定性Collector scraper/receiver 发出的指标如system.cpu.time遵循与 Collector 内部指标如otelcol_process_cpu_seconds相同的稳定性级别体系Beta 级强烈鼓励将 beta 阶段指标同时定义为 Semantic Convention遵循上文 SemConv 兼容性流程保证跨项目一致Stable 级晋级 stable 前应讨论是否需要定义为 Semantic Convention。未成为 SemConv 就晋级 stable 的指标有分叉风险——将来其他 OpenTelemetry 项目可能以略有不同的方式引入同一指标或该指标日后被提为 SemConv。一旦出现分叉stable Collector 指标不允许再修改如需更大范围对齐只能弃用并移除。因此未经 stable SemConv 支撑就把指标标记为 stable必须事先由 maintainer 与 code owner 确认该风险并给出理由在 Collector 内直接定义指标时也应遵循 SemConv 的书写指引。破坏性变更管理总体原则尽可能遵循 semver 作为最低标准即便 v1 之前也尽量不为无充分理由的变更破坏兼容性。已知会造成破坏性变更时破坏性变更 MUST 附带清晰的迁移指南用户 SHOULD 能按自己的节奏、独立于其他 Collector 更新来采纳该变更用户 SHOULD 在被迫迁移之前被主动通知用户 SHOULD 能轻松判断自己是否已完成迁移。API 破坏性变更两阶段弃用API 破坏性变更分两阶段完成先弃用vM.N后一版本vM.N1再破坏需移除的东西MUST 在一版标记 deprecatedMAY 在后续版本移除重命名或重构类型/函数/属性MUST 在一版中创建新名并弃用旧名步骤 1MAY 在后续版本移除步骤 2。简单重命名时旧名 SHALL 直接调用新名以既有功能替换某功能时MUST 在一版标记 deprecatedMAY 后续移除。弃用通知 SHOULD 包含生效版本号便于跟踪。例如GetFoo将在v0.45.0弃用时godoc 行应写作package test // Deprecated: [v0.45.0] Use MustDoFoo instead. func DoFoo() {}如适用还应在弃用函数上加//go:fix inline指令辅助迁移。规范给出三个典型迁移示例例 1 —— 重命名函数v0.N有func GetFoo() Bar决定GetBar是更好的名字v0.N1新增func GetBar() Bar把GetFoo改为新函数的别名并加警告日志与 changelog 条目v0.N2MAY 移除GetFoo。例 2 —— 改变返回值v0.N有func GetFoo() Foo现在需要额外返回 error。v0.N1先创建等价的新函数func MustGetFoo() Foo出错时 panic便于现有用户平滑迁移同时弃用GetFoov0.N2把GetFoo改为func GetFoo() (Foo, error)。例 3 —— 改变参数v0.N有func GetFoo() Foo内部要做可能阻塞的操作于是开始接受 context。v0.N1新增func GetFooWithContext(context.Context) Foo弃用旧函数旧函数改为调用GetFooWithContext(context.Background())v0.N2可把旧函数改为func GetFoo(context.Context) Foo或彻底移除。例外对未达到 v1 的 module以下情形可跳过弃用流程但仍须记入 changelog 的 breaking changes可变参数非 variadic 函数可增加一个 variadic 参数以支持可选参数尤其 functional options 模式。在不传 variadic 参数时行为不变即可跳过弃用流程。注意依赖精确函数签名作为类型的用户例如把该函数当参数传递仍会遇到破坏性变更因此只在该函数通常不会被当值传递时才可跳过。面向终端用户的变更feature gate 三阶段终端用户可见的破坏性变更遵循 feature gate 方法与 Kubernetes 等项目的做法一致。feature gate 有三个阶段alpha、beta、stable。其目的是把其他软件变更与破坏性变更解耦——部分用户可提前采纳部分用户可推迟采纳。定义feature gate 应尽可能以声明式方式定义在组件的metadata.yaml中声明式方法与支持字段详见 featuregate/README.md。ID 命名feature gate ID 用点号分层命名空间应尽量具体。组件级 gate 的结构为component kind.component type.base IDbase ID 用动词描述启用 gate 后发生的事。例如为 OTLP receiver 增加默认端点绑定未指定 host的 gate可命名receiver.otlp.UseUnspecifiedHostAsDefaultHost。Alpha 阶段变更 opt-in通知用户变更将至收集早期采纳者反馈。初始发布前应检查若能通过更新文档与示例避免该破坏性变更影响用户就在此时更新提供工具帮助用户理解变更可选地创建/更新 GitHub issue 说明变更内容与影响面可选地增加遥测辅助迁移跟踪例如对将受影响 payload 计数的 counter通知用户添加描述 feature gate 的changelog 条目名字、何时启用、影响此阶段可归类为enhancement可选但强烈推荐当用户的使用方式将受该破坏性变更影响时打一条警告日志指向该 feature gate 与官方文档可选在真实环境中验证若该变更解决了某 issue可请提交者试用并确认一切正常。Beta 阶段变更 opt-out通知用户变更正在发生并告知如何临时回退旧行为。影响较小、或无功能影响的变更如性能类可直接从该阶段起步。从 alpha 进入 beta 前应检查若文档/示例此前未来得及更新安排与破坏性变更发布对齐的更新更新 GitHub issue 记录新默认行为若直接从此阶段起步则新建 issue为 feature gate 添加to version字段添加标记为breaking的第二条 changelog 条目如适用添加错误消息说明该结果是可临时通过禁用 feature gate 回退的破坏性变更并指向相关 issue 或文档。Stable 阶段变更不可回退某些场景可直接从该阶段起步、直接完成变更——此时无需 feature gate但仍应走下面的通知/文档清单。从 beta 进入 stable 前应检查移除死代码更新文档与示例删除所有对 feature gate 与旧行为的引用关闭此前打开的 issue添加最后一条 changelog 条目让用户知道该 gate 处于 beta 的版本区间修改错误消息删除对 feature gate 的引用。规范跟踪Specification TrackingOpenTelemetry 规范有时是快速移动的目标。虽然提前实现规范中正在开发中的新特性看似高效但会带来显著返工且规范变化可能转化为实现的破坏性变更。因此 Collector SIG 的政策是不实现、也不接受实现在规范文本被纳入规范的 stable release 之前的任何新增或修改内容。小结这份规范可以归纳为三层命名与结构层组件标识符lower_snake_case、API 前缀New/NewDefault/Set/Default/信号词、Config与Settings后缀、拒绝内嵌结构体、module 拆分八规则与新增 module 清单运行时健壮性层启动 fail fast、main外不退出、启动后不崩溃、恶意输入不崩溃、致命/瞬时错误分流加指数退避与 jitter、资源有界、Shutdown()可重入且尊重 context演进治理层SemConv 对齐、指标稳定性与 stable 晋级风险、semver 两阶段弃用、feature gate alpha→beta→stable 全周期操作清单、规范文本 stable release 前不实现。遵循这套规范组件代码才能满足 Collector长期自主运行、数据不丢失、行为可观测、变更可迁移的核心质量基线。【免费下载链接】opentelemetry-collectorOpenTelemetry Collector项目地址: https://gitcode.com/GitHub_Trending/op/opentelemetry-collector创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表