Envoy OpenTelemetry 自定义 Exporter 扩展点:接入专属 OTLP 导出器的完整指南
【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy
导读
本指南围绕 Envoy 的 OpenTelemetry tracer 新增的exporter扩展点展开,介绍如何在OpenTelemetryConfig中通过TypedExtensionConfig挂载自定义 tracing exporter,从而突破内置 gRPC/HTTP 两种 OTLP 导出方式的限制。读完本文,你将掌握envoy.tracers.opentelemetry.exporters扩展点的配置语法、互斥约束与默认行为,并了解如何基于源码级工厂接口开发自己的 exporter。
特性背景:从固定两种导出方式到可插拔扩展点
Envoy 内置的 OpenTelemetry tracer(扩展名envoy.tracers.opentelemetry)原本只支持两种向 OTLP Collector 上报 trace 的方式:
- 通过
grpc_service走 gRPC 通道上报 OTLP traces(对应源码 grpc_trace_exporter.h 中的OpenTelemetryGrpcTraceExporter); - 通过
http_service走 HTTP 通道上报 OTLP traces(对应源码 http_trace_exporter.h 中的OpenTelemetryHttpTraceExporter)。
本次新增的exporter扩展点允许使用者接入自定义 tracing exporter,例如对接私有采集服务、特殊的鉴权体系或定制的批处理管道,而不再局限于 Envoy 内置的两种传输协议。该字段在 opentelemetry.proto 中定义:
// Specifies the custom exporter to be used by the OpenTelemetry tracer. // Only one of ``grpc_service``, ``http_service``, ``exporter`` may be used. // // [#extension-category: envoy.tracers.opentelemetry.exporters] // [#not-implemented-hide:] core.v3.TypedExtensionConfig exporter = 10 [(udpa.annotations.field_migrate).oneof_promotion = "otlp_exporter"];从定义可见,该字段使用标准的TypedExtensionConfig(名称 +typed_config),扩展类别为envoy.tracers.opentelemetry.exporters。oneof_promotion迁移注解说明它未来会被并入名为otlp_exporter的 oneof 字段。
配置形态:三选一的互斥约束
约束规则与校验逻辑
grpc_service、http_service、exporter三者最多只能配置一个,且 OpenTelemetry tracer 必须恰好配置其中一种,否则 Envoy 启动会直接抛异常。该约束在 opentelemetry_tracer_impl.cc 中实现:
const int exporter_count = (opentelemetry_config.has_grpc_service() ? 1 : 0) + (opentelemetry_config.has_http_service() ? 1 : 0) + (opentelemetry_config.has_exporter() ? 1 : 0); if (exporter_count != 1) { throw EnvoyException("OpenTelemetry Tracer must have exactly one of gRPC, HTTP, or custom " "exporter configured."); }对应的异常信息在 config_test.cc 中被测试断言为"OpenTelemetry Tracer must have exactly one of gRPC, HTTP, or custom exporter configured."。
完整的自定义 exporter 配置示例
以下 YAML 展示了在 HTTP tracing 配置中挂载自定义 exporter 的标准写法(出自 config_test.cc 的测试用例):
http: name: envoy.tracers.opentelemetry typed_config: "@type": type.googleapis.com/envoy.config.trace.v3.OpenTelemetryConfig exporter: name: envoy.tracers.opentelemetry.exporters.dummy_config_test typed_config: "@type": type.googleapis.com/google.protobuf.Empty字段说明:
| 字段 | 说明 |
|---|---|
exporter.name | 自定义 exporter 工厂在注册表中注册的扩展名,必须以envoy.tracers.opentelemetry.exporters.为前缀(对应工厂的category()返回值) |
exporter.typed_config | 传给该工厂的自定义配置,@type必须与该工厂声明的configType匹配,经translateOpaqueConfig反序列化后传给createExporter |
创建流程与校验链
在 opentelemetry_tracer_impl.cc 中,当配置了exporter时,Driver 构造流程如下:
- 通过
Envoy::Config::Utility::getFactory<OpenTelemetryTraceExporterFactory>(exporter_config)按名称查找工厂,找不到则抛出"OpenTelemetry trace exporter factory not found: '<name>'"; - 调用
createEmptyConfigProto()得到空配置原型,若返回nullptr则抛出"OpenTelemetry trace exporter factory '<name>' returned nullptr from createEmptyConfigProto()"(该分支由 config_test.cc 的NullConfigTraceExporterFactory用例覆盖); - 调用
translateOpaqueConfig将typed_config反序列化为工厂声明的配置类型,失败则抛出翻译错误; - 在主线程完成校验后,为 worker 线程构造使用
NullValidationVisitor的TracerFactoryContextImpl,以保证多线程安全; - 在 TLS 初始化回调中调用
custom_exporter_factory->createExporter(*shared_unpacked_config, *worker_factory_context)创建 exporter 实例(见 opentelemetry_tracer_impl.cc)。
若 exporter 创建失败或未配置任何有效 exporter,tracer 会记录警告日志"OpenTelemetry tracer initialized without a valid exporter; spans will be dropped.",Span 将被丢弃而不会崩溃。
扩展点源码剖析:Exporter 抽象与工厂接口
基类OpenTelemetryTraceExporter
所有 OTLP exporter 的基类定义在 trace_exporter.h,核心接口只有一个:
class OpenTelemetryTraceExporter : public Logger::Loggable<Logger::Id::tracing> { public: /** * @brief Exports the trace request to the configured OTLP service. * @param request The protobuf-encoded OTLP trace request. * @return true When the request was sent. * @return false When sending the request failed. */ virtual bool log(const ExportTraceServiceRequest& request) = 0; ... };- 入参
ExportTraceServiceRequest是 OTLP 协议collector/trace/v1的 protobuf 请求,由@opentelemetry-proto//:trace_service_proto_cc提供(见 BUILD); - 返回值
bool表示请求是否成功发出;返回false即视为导出失败; - 基类还提供
logExportedSpans()帮助方法,用于在 debug 日志中输出本次导出的 Span 数量。
内置的 OpenTelemetryGrpcTraceExporter 继承自该基类并实现了Grpc::AsyncRequestCallbacks<ExportTraceServiceResponse>,将请求包装为对ExportTraceService的 gRPC 异步调用;而 OpenTelemetryHttpTraceExporter 则实现Http::AsyncClient::Callbacks,通过 ClusterManager 发起 HTTP 异步请求,并用Http::AsyncClientRequestTracker跟踪在途请求以便析构时取消。
工厂接口OpenTelemetryTraceExporterFactory
自定义 exporter 的扩展入口是 trace_exporter.h 中定义的工厂抽象:
class OpenTelemetryTraceExporterFactory : public Envoy::Config::TypedFactory { public: /** * @brief Creates an OpenTelemetryTraceExporter. * * `createExporter` is invoked concurrently from multiple worker threads * during TLS initialization and implementations MUST be stateless, * re-entrant, and thread-safe. */ virtual absl::StatusOr<OpenTelemetryTraceExporterPtr> createExporter(const Protobuf::Message& config, Server::Configuration::TracerFactoryContext& context) const PURE; std::string category() const override { return "envoy.tracers.opentelemetry.exporters"; } };编写自定义 exporter 时必须遵守以下契约:
- 继承
OpenTelemetryTraceExporterFactory并实现createExporter; - 实现
name()返回形如envoy.tracers.opentelemetry.exporters.<your_name>的扩展名; - 实现
createEmptyConfigProto()返回配置原型,不得返回nullptr; createExporter在 worker 线程 TLS 初始化阶段会被并发调用,实现必须是无状态、可重入且线程安全的;- 错误必须通过
absl::StatusOr返回,禁止抛出 C++ 异常。
测试中注册了两个示范工厂作为参考实现:config_test.cc 中的DummyTraceExporterFactory(configType 为google.protobuf.Empty)与NullConfigTraceExporterFactory(configType 为google.protobuf.Struct,用于验证空配置原型报错路径)。
运行时行为与 Span 上报链路
Driver 将 tracer 存储在 TLS(Thread Local Storage)槽位中,每个 worker 线程持有独立的 tracer 实例(opentelemetry_tracer_impl.cc)。startSpan()会先通过SpanContextExtractor尝试从传播头提取父 SpanContext:
- 若存在传播头且提取成功,则以父 SpanContext 为上下文创建子 Span;
- 若提取失败,返回
Tracing::NullSpan(空 Span),不导出; - 若不存在传播头,则根据 tracing 决策直接创建全新 Span,未采样时调用
setSampled(false)丢弃。
Span 的导出由Tracer调用exporter->log(request)完成,期间 Envoy 会按max_cache_size(默认 1024,见 opentelemetry_tracer_impl.cc 的DEFAULT_MAX_CACHE_SIZE)在内存中缓存 Span,以应对后端暂时不可用的场景。
与 OpenTelemetryConfig 其他字段的搭配
exporter扩展点可与以下字段自由组合(完整定义见 opentelemetry.proto):
| 字段 | 作用 | 默认值 |
|---|---|---|
service_name | 填充到 ResourceSpan 的 Resource 属性,service.name缺省为"unknown_service:envoy" | "unknown_service:envoy" |
resource_detectors | 有序的资源探测器列表,类别envoy.tracers.opentelemetry.resource_detectors,仓库内置 dynatrace、environment、static 三类(见 resource_detectors 目录) | 空 |
sampler | 采样器,类别envoy.tracers.opentelemetry.samplers,内置 always_on、parent_based、trace_id_ratio_based、cel、dynatrace 等(见 samplers 目录);不配置时使用 Envoy 默认采样决策 | 空 |
max_cache_size | 后端不可用时的 Span 内存缓存上限 | 1024 |
set_telemetry_sdk_resource_attributes | 是否设置telemetry.sdk.language/name/version属性 | true |
set_service_name_resource_attribute | 是否设置service.name资源属性 | true |
set_instrumentation_scope | 是否在 trace 上设置 instrumentation scope 名称("envoy")与版本 | true |
需要特别说明的是,exporter采用TypedExtensionConfig机制(见 extension.proto 中TypedExtensionConfig的定义),配置会经过 Envoy 严格的 message validation;主线程校验通过后,worker 线程复用已翻译好的配置,避免重复校验带来的竞态问题。
测试覆盖与验证
仓库对 exporter 扩展点提供了完整的测试验证(位于 test/extensions/tracers/opentelemetry 目录):
- config_test.cc:验证三选一互斥约束的异常抛出、自定义 exporter 的正常创建流程、
createEmptyConfigProto()返回空指针时的报错路径; - grpc_trace_exporter_test.cc 与 http_trace_exporter_test.cc:分别覆盖内置 gRPC/HTTP exporter 的请求组装、成功/失败回调;
- grpc_trace_exporter_integration_test.cc 与 http_trace_exporter_integration_test.cc:集成环境下验证真实上报链路;
- opentelemetry_tracer_impl_test.cc:验证 Driver 生命周期、Span 采样决策与导出行为。
适用前提与限制
exporter字段在 proto 中带有[#not-implemented-hide:]标注,表示该扩展点面向自定义实现开放;启用自定义 exporter 需要将对应扩展编译进 Envoy(通过bazel的扩展构建系统注册到envoy.tracers.opentelemetry.exporters类别下);- 当前版本中
grpc_service、http_service、exporter三者互斥且必须恰好其一,未来按oneof_promotion = "otlp_exporter"迁移后会成为真正的 oneof 字段,配置语义保持一致; - 若自定义 exporter 未就绪或创建失败,Span 会被静默丢弃(仅记录 warn 日志),生产环境应配合监控日志确保 exporter 持续可用。
【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考