news 2026/9/11 16:57:05

Envoy OpenTelemetry Access Logger 修复详解:custom_tags 中 formatter 命令不再被忽略

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Envoy OpenTelemetry Access Logger 修复详解:custom_tags 中 formatter 命令不再被忽略

Envoy OpenTelemetry Access Logger 修复详解:custom_tags 中 formatter 命令不再被忽略

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

本篇技术指南聚焦 Envoy 中 OpenTelemetry 访问日志器(access logger)的一个关键缺陷修复:custom_tags在构建时曾忽略通过formatters字段配置的命令解析器,导致自定义标签值中引用 formatter 扩展命令时以Not supported field in StreamInfo失败。文章将带你理解该缺陷的成因、修复后的调用链,以及如何在配置中正确组合formatterscustom_tags,并给出源码级证据与可复现的配置示例。

背景:OpenTelemetry 访问日志器与 OTLP 日志导出

Envoy 内置的 OpenTelemetry 访问日志扩展(envoy.access_loggers.open_telemetry)将请求访问日志转换为 OpenTelemetry Protocol(OTLP)日志,并通过 gRPC 或 HTTP 两种传输方式推送到上游后端。其配置消息为envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig,定义于 api/envoy/extensions/access_loggers/open_telemetry/v3/logs_service.proto。

每条日志记录(LogRecord)主要由三部分内容构成:

  • body:日志主体,支持%COMMAND%形式的 Envoy 访问日志格式化占位符;
  • attributes:一组键值对(KeyValueList),值同样支持格式化占位符;
  • custom_tags:自定义标签(repeated type.tracing.v3.CustomTag),作为日志属性(attribute)附加到每条记录上,机制与 Envoy tracing 的 custom tag 一致。

formattersrepeated config.core.v3.TypedExtensionConfig,proto 字段 7)则用于挂载 formatter 插件(扩展类别envoy.formatter),例如 CEL 表达式等自定义命令,供bodyattributes乃至custom_tags中的格式化字符串调用。

缺陷现象:custom_tags 中的 formatter 命令解析失败

本次修复对应的变更记录位于 changelogs/current/bug_fixes/open_telemetry__custom-tags-formatters.rst,其描述的问题如下:

OpenTelemetry 访问日志器(gRPC 与 HTTP 两种变体)在构建custom_tags时忽略了已配置的formatters。此前,自定义标签的值若使用了 formatter 扩展命令,会以Not supported field in StreamInfo错误失败,而同样的命令在bodyattributes中却可以正常工作。

也就是说,同样的 formatter 命令存在“位置差异”:

使用位置修复前修复后
body中的格式化字符串正常解析正常解析
attributes中的格式化字符串正常解析正常解析
custom_tagsvalue字段的格式化字符串Not supported field in StreamInfo正常解析

根因分析:命令解析器没有传递到 custom-tag 创建流程

要理解根因,需要追踪custom_tags的构建路径。在 source/extensions/access_loggers/open_telemetry/access_log_impl.cc 的构造函数中:

AccessLog::AccessLog( ::Envoy::AccessLog::FilterPtr&& filter, envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig config, ThreadLocal::SlotAllocator& tls, GrpcAccessLoggerCacheSharedPtr access_logger_cache, const std::vector<Formatter::CommandParserPtr>& commands) : Common::ImplBase(std::move(filter)), tls_slot_(tls.allocateSlot()), access_logger_cache_(std::move(access_logger_cache)), filter_state_objects_to_log_(getFilterStateObjectsToLog(config)), custom_tags_(getCustomTags(config, commands)) { ... }

注意构造函数的最后一个参数commands—— 它正是从config.formatters()解析得到的命令解析器集合。这个集合同时被传入:

  • OpenTelemetryFormatter(用于bodyattributes的格式化),见access_log_impl.cc中的body_formatter_attributes_formatter_初始化;
  • getCustomTags(config, commands),用于构建 custom tags。

而在 source/extensions/access_loggers/open_telemetry/otlp_log_utils.cc 中,getCustomTags的实现为:

std::vector<Tracing::CustomTagConstSharedPtr> getCustomTags( const envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig& config, const Formatter::CommandParserPtrVector& command_parsers) { std::vector<Tracing::CustomTagConstSharedPtr> custom_tags; for (const auto& custom_tag : config.custom_tags()) { custom_tags.push_back(Tracing::CustomTagUtility::createCustomTag(custom_tag, command_parsers)); } return custom_tags; }

关键点在于Tracing::CustomTagUtility::createCustomTag。从 source/common/tracing/custom_tag_impl.cc 可以看到,CustomTagoneof type中有五种取值:literalenvironmentrequest_headermetadatavalue。其中value类型的标签值就是一段 Envoy 访问日志格式化字符串(见 api/envoy/type/tracing/v3/custom_tag.proto 中value字段的注释:与 HTTP 访问日志使用相同的 format specifier),它由FormatterCustomTag负责解析:

FormatterCustomTag::FormatterCustomTag(absl::string_view tag, absl::string_view value, const Formatter::CommandParserPtrVector& command_parsers) : tag_(tag) { auto formatter_or = Formatter::FormatterImpl::create(value, true, command_parsers); THROW_IF_NOT_OK_REF(formatter_or.status()); formatter_ = std::move(formatter_or.value()); }

缺陷根因正是:修复前,getCustomTags创建 custom tag 时没有把配置的command_parsers传递进去。于是value中的 formatter 扩展命令(如 CEL 表达式)在解析阶段就找不到对应解析器,进而产生Not supported field in StreamInfo错误;而body/attributes走的OpenTelemetryFormatter路径一直正确地接收了commands,所以同样的命令在那里可用。

本次修复的核心动作,就是将配置解析得到的命令解析器集合显式地贯穿到 custom-tag 创建流程中getCustomTags增加command_parsers参数并转发给createCustomTag)。修复覆盖 gRPC 与 HTTP 两种访问日志变体,因为两者共用同一套 custom-tag 构建逻辑。

修复后的完整调用链

修复后,从配置到日志属性的完整链路如下(以 gRPC 变体为例,HTTP 变体走 http_access_log_impl.cc 的HttpAccessLog,逻辑等价):

  1. 配置解析:工厂AccessLogFactory::createAccessLogInstance(source/extensions/access_loggers/open_telemetry/config.cc)调用Formatter::SubstitutionFormatStringUtils::parseFormatters(proto_config.formatters(), context, std::move(command_parsers))生成commands向量;
  2. 实例构造commands被同时传给OpenTelemetryFormatter(body/attributes)与getCustomTags(config, commands)(custom tags);
  3. custom tag 创建getCustomTagscommands传给CustomTagUtility::createCustomTag,其中value类型标签经FormatterCustomTagFormatterImpl::create(value, true, command_parsers)解析成 formatter 提供器;
  4. 日志生成emitLog中调用addCustomTagsToAttributes(otlp_log_utils.cc),将 custom tag 的格式化结果写入LogRecordattributes

其中addCustomTagsToAttributes的实现值得注意:它通过一个临时AccessLogCommon调用每个 custom tag 的applyLog,再遍历temp_log.custom_tags()复制到 OTLP attributes;如果请求上下文不存在(如 TCP 场景),会回退到空请求头映射,保证 custom tags 依然可用:

void addCustomTagsToAttributes(const std::vector<Tracing::CustomTagConstSharedPtr>& custom_tags, const Formatter::Context& context, const StreamInfo::StreamInfo& stream_info, opentelemetry::proto::logs::v1::LogRecord& log_entry) { if (custom_tags.empty()) { return; } const Http::RequestHeaderMap* headers_ptr = context.requestHeaders().has_value() ? &static_cast<const Http::RequestHeaderMap&>(context.requestHeaders().value()) : Http::StaticEmptyHeaders::get().request_headers.get(); ... for (const auto& custom_tag : custom_tags) { custom_tag->applyLog(temp_log, tag_context); } for (const auto& [key, value] : temp_log.custom_tags()) { auto* attr = log_entry.add_attributes(); attr->set_key(key); attr->mutable_value()->set_string_value(value); } }

测试验证:custom_tags 正确使用配置的 formatter

修复在 test/extensions/access_loggers/open_telemetry/otlp_log_utils_test.cc 中有专门的回归测试CustomTagsUseConfiguredFormatters(注释中引用了 issue #45453)。测试构造了一个最小命令解析器FakeCommandParser,将%FAKE_COMMAND%解析为固定值fake_value

TEST(OtlpLogUtilsTest, CustomTagsUseConfiguredFormatters) { ... envoy::extensions::access_loggers::open_telemetry::v3::OpenTelemetryAccessLogConfig config; auto* tag = config.add_custom_tags(); tag->set_tag("fmt_tag"); tag->set_value("%FAKE_COMMAND%"); std::vector<Formatter::CommandParserPtr> commands; commands.push_back(std::make_unique<FakeCommandParser>()); auto custom_tags = getCustomTags(config, commands); ... addCustomTagsToAttributes(custom_tags, context, stream_info, log_entry); opentelemetry::proto::logs::v1::LogRecord expected; auto* attr = expected.add_attributes(); attr->set_key("fmt_tag"); attr->mutable_value()->set_string_value("fake_value"); EXPECT_TRUE(TestUtility::protoEqual(log_entry, expected)); }

该测试直接验证了“配置的命令解析器被用于 custom-tag 创建”这一修复要点。此外,同一测试文件中还有两个补充用例:AddCustomTagsToAttributesEmptyTags(空 custom tags 列表是 no-op)与AddCustomTagsToAttributesWithoutRequestHeaders(请求头不可用时回退到空头映射,模拟 TCP 场景),保证修复没有破坏其他路径。

实战配置:在 custom_tags 中安全使用 formatter 命令

基于 logs_service.proto 中定义的字段(log_namegrpc_service/http_servicebodyattributesformatterscustom_tags等),下面给出一个同时使用formatterscustom_tags的完整配置示例(gRPC 传输):

access_log: - name: envoy.access_loggers.open_telemetry typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig log_name: "envoy_accesslog" grpc_service: envoy_grpc: cluster_name: opentelemetry_collector # 挂载 formatter 插件(扩展类别 envoy.formatter),供下方所有 %COMMAND% 使用 formatters: - name: envoy.formatter.cel typed_config: "@type": type.googleapis.com/envoy.extensions.formatter.cel.v3.Cel body: string_value: "%REQ(:METHOD)% %PROTOCOL% %RESPONSE_CODE%" attributes: values: - key: "user_agent" value: string_value: "%REQ(USER-AGENT)%" custom_tags: # literal 类型:静态值 - tag: "region" literal: value: "cn-north-7" # environment 类型:读环境变量,带默认值 - tag: "deploy_env" environment: name: "ENVOY_DEPLOY_ENV" default_value: "dev" # request_header 类型:读请求头 - tag: "trace_client" request_header: name: "X-Client-Id" default_value: "unknown" # value 类型:格式化字符串,可使用 formatters 挂载的命令 - tag: "expr_result" value: "%CEL(environment['HOSTNAME'])%"

配置要点:

  • custom_tagstag字段指定标签名(写入日志的 attribute key),oneof typeliteral/environment/request_header/metadata/value五选一;
  • 只有value类型的自定义标签会走 formatter 解析(对应FormatterCustomTag),因此要在其中使用 formatter 扩展命令,必须保证formatters字段已挂载对应插件——这正是本次修复保证的前提条件;
  • formatters同时服务于bodyattributescustom_tags,三者的命令解析行为在修复后保持一致;
  • 传输方式三选一:common_config.grpc_service(已弃用,将于 3.0 移除)、grpc_servicehttp_service,工厂代码(config.cc)会校验恰好配置一个;
  • 若使用http_service,其request_headers_to_add也支持 substitution formatter(但不能访问 HTTP/连接属性,可加载环境变量、文件或密钥),集成测试 access_log_integration_test.cc 中的HttpExportWithFormatterHeader用例验证了%HOSTNAME%在导出 HTTP 请求头中的展开。

常见问题与排查建议

  • 现象:自定义标签的值中使用 formatter 命令,日志导出时报Not supported field in StreamInfo排查:确认使用的是本次修复之后的 Envoy 版本(修复前 gRPC 与 HTTP 两种访问日志器均存在此缺陷);确认formatters中已挂载对应插件,且custom_tags中标签类型为value(其余类型不参与 formatter 解析)。
  • 现象body/attributes中命令可用,但custom_tags中不可用。这正是本次修复前的典型症状,根因是命令解析器未传递到 custom-tag 创建路径,而非命令本身非法。
  • 现象:未配置任何 custom tags 或请求头不可用。源码中已做兜底:空标签列表直接返回(见addCustomTagsToAttributes开头的custom_tags.empty()检查),请求头缺失时使用静态空头映射,不会导致崩溃或导出失败。

小结

本次修复的实质是在 OpenTelemetry 访问日志器中统一 formatter 命令的解析入口:将从formatters字段解析得到的命令解析器显式传递到custom_tags的构建过程,消除了bodyattributescustom_tags三处格式化行为的不一致。源码层面,修复体现在 otlp_log_utils.cc 的getCustomTags签名与其调用方(access_log_impl.cc 与 http_access_log_impl.cc),并由 otlp_log_utils_test.cc 的CustomTagsUseConfiguredFormatters回归测试锁定。现在,你可以在custom_tags中放心组合 formatter 插件,构建更丰富的 OTLP 日志属性。

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 16:55:04

水稻田杂草检测数据集:YOLO+VOC双格式1373张标注图像

简介&#xff1a;本资源是面向农业AI与计算机视觉初学者的水稻田杂草目标检测专用数据集&#xff0c;适用于YOLO系列及VOC格式模型的训练与验证&#xff0c;助力智慧农业场景下的作物-杂草识别算法开发。压缩包共2000个文件&#xff0c;含1373张高清原始图像&#xff08;JPEGIm…

作者头像 李华
网站建设 2026/9/11 16:54:20

Midscene.js:用自然语言写 E2E 测试,告别 CSS 选择器维护

Midscene.js&#xff1a;用自然语言写 E2E 测试&#xff0c;告别 CSS 选择器维护 【免费下载链接】midscene GUI Agent for E2E Testing 项目地址: https://gitcode.com/GitHub_Trending/mid/midscene 上周前端改了一个 class 名&#xff0c;37 个回归用例当场从绿变红 …

作者头像 李华
网站建设 2026/9/11 16:52:09

云手机真机级仿真:AOSP API与设备指纹克隆技术解析

简介&#xff1a;本资源是一个面向Android系统开发工程师与云服务架构师的AOSP级云手机与云游戏开发平台&#xff0c;聚焦于虚拟化环境下的真机仿真、安全合规与多实例并发能力。平台支持ARM/X86双架构虚拟化&#xff0c;集成真机参数克隆、风控检测绕过、Google Play Integrit…

作者头像 李华
网站建设 2026/9/11 16:51:39

使用 CURLOPT_WILDCARDMATCH 实现 libcurl 的 FTP 目录通配符批量下载

使用 CURLOPT_WILDCARDMATCH 实现 libcurl 的 FTP 目录通配符批量下载 【免费下载链接】curl A command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQ…

作者头像 李华
网站建设 2026/9/11 16:49:07

MuJoCo 机械臂抓取仿真不稳?指尖几何、摩擦与求解器怎么调

MuJoCo 机械臂抓取仿真不稳&#xff1f;指尖几何、摩擦与求解器怎么调 【免费下载链接】mujoco Multi-Joint dynamics with Contact. A general purpose physics simulator. 项目地址: https://gitcode.com/GitHub_Trending/mu/mujoco 手合拢、物体却从指尖滑走&#xf…

作者头像 李华
网站建设 2026/9/11 16:47:45

Vosk 完整指南:一条命令跑起零延迟离线语音识别

Vosk 完整指南&#xff1a;一条命令跑起零延迟离线语音识别 【免费下载链接】vosk-api Offline speech recognition API for Android, iOS, Raspberry Pi and servers with Python, Java, C# and Node 项目地址: https://gitcode.com/GitHub_Trending/vo/vosk-api Vosk …

作者头像 李华