Vector Remap 语言(VRL)指标事件支持实战指南:用脚本统一处理 Log 与 Metric 元数据

发布时间:2026/9/13 3:49:17
Vector Remap 语言(VRL)指标事件支持实战指南:用脚本统一处理 Log 与 Metric 元数据
Vector Remap 语言VRL指标事件支持实战指南用脚本统一处理 Log 与 Metric 元数据【免费下载链接】vectorA high-performance observability data pipeline.项目地址: https://gitcode.com/GitHub_Trending/vect/vector本文基于 rfcs/2020-11-01-remap-metrics-support.md 撰写结合当前仓库源码验证实现细节。在 Vector 的观测数据管道中日志与指标是两种最基本的事件类型。早期remap转换只能操作日志事件而对指标事件仅能通过add_tags、remove_tags、rename_tags这类功能单一的转换来调整标签。RFC 35502020-11-01提出并落地了Remap 语言支持指标事件的能力让 VRL 脚本能够直接读取和修改指标的元数据名称、命名空间、时间戳、类型、标签从而用一份简单脚本替换多个*_tag转换并把绝大多数原本需要Lua转换才能完成的标签处理下沉到 VRL。读完本文你将掌握 VRL 中操作指标元数据的全部字段、严格类型约束、路径限制以及背后的VrlTarget实现原理与配置要点。背景与动机*_tag转换的局限在引入本功能之前Vector 处理指标元数据主要依赖三个转换add_tags为指标添加标签remove_tags删除指标标签rename_tags重命名指标标签。RFC 明确指出这些转换有用但受限useful, but limited。它们无法依据指标周围的其他数据来决定如何处理标签——例如你无法根据.name或.namespace的内容条件化地增删标签无法在同一个脚本里同时修改名称、命名空间、时间戳和标签也无法用分支、循环等语言结构表达复杂的处理逻辑。因此凡是超出这三个转换能力范围的用户只能回退到Lua转换编写更复杂、更难维护的脚本。设计目标与范围只动元数据不动指标值RFC 首先划定了清晰的能力边界The aim of the Remap language is to remain simple and yet provide the power to allow the user to replace most of their transform configuration with a simple Remap script.VRL 的定位是简单但强大让用户用一份脚本替换大部分转换配置而超出其复杂度上限的需求仍由Lua或Wasm转换承接。因此本 RFC 的范围并非让 Remap 语言全面、综合地操作指标数据而是允许用户对指标周围的元数据metadata做简单修改指标值本身如 Counter 的数值、Histogram 的桶等不纳入 VRL 的直接操作范围。这一取舍非常关键它保证了 VRL 语言的简单性同时覆盖了现实中最高频的指标处理场景改名、改命名空间、增删标签、调整时间戳。内部方案为 Metric 实现独立的 Target 适配从Objecttrait 到VrlTarget的演进RFC 的 Internal Proposal 部分描述了最初的实现思路VRL 通过路径访问数据时要求目标对象实现remap::Objecttrait当时该实现只针对日志事件虽然Object已对Event实现但其内部假定事件就是日志事件。方案是分别对LogEvent和Metric实现Object由 Remap 转换根据事件类型把LogEvent或Metric传入。在当前仓库中这一设计已演进为 lib/vector-core/src/event/vrl_target.rs 中的VrlTarget枚举它有三个变体pub enum VrlTarget { LogEvent(Value, EventMetadata), Metric { metric: Metric, value: Value, tag_mode: MetricTagMode, }, Trace(Value, EventMetadata), }当事件是指标时VrlTarget::new会进入Event::Metric(metric)分支见 vrl_target.rsEvent::Metric(metric) { // 预生成指标各字段的 Value 类型允许多次访问同一字段时返回引用 let value precompute_metric_value(metric, info, tag_mode); VrlTarget::Metric { metric, value, tag_mode } }指标路径的预计算优化一个值得注意的实现细节是precompute_metric_valuevrl_target.rs它根据 VRL 编译期收集的ProgramInfo::target_queries脚本实际访问了哪些路径只把脚本用到的字段预先物化为Value。如果脚本访问了根路径.则一次性预填充全部字段name、kind、type、namespace、interval_ms、timestamp、tags。这既避免了每次访问都从Metric结构解包的开销也让多次读取同一字段能拿到稳定的引用。路径校验固定路径 运行时错误RFC 指出指标的可用路径是相当固定的The paths that can be used for Metrics events are fairly fixed并且核心方法返回Result因此指定了非法路径时Object只需返回Error。这一校验在最初是运行时行为编译器无法在编译期捕获非法路径只有未来引入 schema metadata对应 issue 4599后才能从 schema 层面捕获非法字段的使用这属于 RFC 范围外的后续工作。当前实现中这一固定路径 运行时错误的设计体现在两个常量上vrl_target.rsconst VALID_METRIC_PATHS_SET: str .name, .namespace, .interval_ms, .timestamp, .kind, .tags; const VALID_METRIC_PATHS_GET: str .name, .namespace, .interval_ms, .timestamp, .kind, .tags, .type; /// 最长路径为 2如 .tags.host需检查不存在第三段如 .tags.host.thing const MAX_METRIC_PATH_DEPTH: usize 3;当写入路径不在集合内时target_insert会返回MetricPathError::InvalidPath错误其展示信息为invalid path {path}: expected one of {VALID_METRIC_PATHS_SET}。也就是说.tags.host.thing这类路径会被拒绝——不允许嵌套这与 RFC 中tags 是 string key 到 string value 的映射不允许嵌套的约束完全对应。文档级设计可用字段与严格类型约束RFC 的 Doc-level Proposal 给出了 VRL 处理指标事件时可直接访问的字段集合。结合当前实现完整的可访问字段如下字段含义可读可写写入类型要求.name指标名称✅✅必须是字符串否则报错.namespace指标命名空间✅✅必须是字符串否则报错可用del置空.timestamp指标时间戳✅✅必须是时间戳可用del置空.kind指标类型Incremental增量或Absolute绝对值✅✅字符串形式非法值报错.tags指标标签映射✅✅string key → string value不允许嵌套.tags.tag_name单个标签✅✅字符串赋null表示空标签.interval_ms指标上报间隔毫秒✅✅非零正整数实现中为u32且非零.type指标数据类型如 Counter、Gauge、Histogram 等✅❌只读不可写入值得注意的是.type是后加的只读字段RFC 原文只列出 5 个字段当前实现VALID_METRIC_PATHS_GET比VALID_METRIC_PATHS_SET多出.type允许读取指标的类型信息但不允许写入——因为我们可以在 Remap 中获取指标的 type但不能设置它注释原文。这与 RFC 划定的不改指标值边界一致。.interval_ms同样为后加的字段对应指标数据的采集间隔写入时要求非零正整数vrl_target.rs 中经i64 → u32 → NonZero转换转换失败即报错。.namespace与.timestamp可以置空RFC 明确说明二者可被设为None以移除其值实现方式是使用del函数。这一点在target_remove中得到印证[namespace]、[timestamp]、[interval_ms]均通过.take()移除值而[name]和[kind]不在可删除列表中。kind的实现细节MetricKind定义于 lib/vector-core/src/event/metric/mod.rs注释说明Metrics can be either absolute or incremental. Absolute metrics represent a sort of last write wins scenario...其TryFromValue实现接受字符串形式映射到枚举incremental→Incrementalabsolute→Absolutemetric/mod.rs其它字符串则返回错误。RFC 原文写作Incremental/Absolute实际脚本中写入的字符串与TryFromValue的匹配形式保持一致即可。实战用 VRL 修改指标元数据RFC 给出了两个核心操作范式赋值与删除。添加或修改标签将.tags.host设为localhost.tags.host localhost如果要整体替换标签集合可以把.tags整体赋值为一个对象当前实现target_insert中[tags]分支会先metric.remove_tags()再逐项写入。删除标签使用del函数del(.tags.host)修改名称、命名空间与类型.name request_count .namespace application .kind incremental移除命名空间与时间戳del(.namespace) del(.timestamp)变量不受类型限制RFC 特别注明变量以$开头的标识符在脚本中始终可用且对其可赋值的类型没有限制。你可以把从日志字段解析出的值存入变量再赋给指标字段这也是结合指标周围数据决定如何处理的关键能力——例如$host parse_host!(.source_host) # 从其他数据计算出标签值 .tags.host $host配置 remap 转换完整参数说明要启用上述能力只需在拓扑中配置remap转换实现见 src/transforms/remap.rs。其input()返回Input::all()即日志、指标、Trace 三种事件都可进入VRL 脚本对指标的处理通过VrlTarget适配层完成remap.rs。关键配置项配置项默认值说明source无与file/files三选一必须提供其一内联的 VRL 程序文本file无VRL 程序文件路径相对路径以当前工作目录为根files无多个 VRL 程序文件路径内容按顺序拼接执行metric_tag_valuessingle标签值在 VRL 中的暴露方式single单值字符串多值取最后赋值忽略 null、full全部以字符串/null 数组暴露、auto单值标签为字符串、多值标签为数组按底层形状读写timezone全局timezone应用于不包含显式时区的时间戳转换drop_on_errorfalse脚本报错时丢弃事件否则原样未修改下发drop_on_aborttrue脚本abort时丢弃事件否则原样下发reroute_droppedfalse被丢弃的事件转发到名为dropped的特殊输出端口并附加丢弃原因、组件 ID 等元数据一个同时处理日志与指标元数据的典型配置transforms: enrich_metrics: type: remap inputs: - my_source source: | # 统一补充环境标签 .tags.env production # 按命名空间条件化处理 if .namespace legacy { .namespace modern } # 删除不需要的标签 del(.tags.internal) drop_on_error: true若使用file方式transforms: enrich_metrics: type: remap inputs: [my_source] file: /etc/vector/remap/metrics.vrl其中metrics.vrl内容与内联source等价。注意source、file、files三者必须且只能提供其一否则构建期会报错must provide exactly one of source or file or files configuration见 remap.rs 及对应单元测试。metric_tag_values三种模式的语义在 remap.rs 有完整说明其底层对应MetricTagModevrl_target.rsSingle标签以单字符串暴露多值标签取最后一个值写入总是产生单值标签Full标签总是以数组暴露写入总是产生多值标签Auto按底层形状暴露——单值标签为字符串、多值标签为数组写入时标量产生单值标签、数组产生多值标签长度 1 的数组会被存储层归一化为单值标签。源码验证指标处理的关键测试仓库中的测试直接印证了 RFC 的设计落地。最典型的是check_remap_metricsrc/transforms/remap.rslet conf RemapConfig { source: Some( r#.tags.host zoobub .name zork .namespace zerk .kind incremental# .to_string(), ), ... };该测试构造一个名为counter、Absolute类型的指标事件经 VRL 处理后断言名称变为zork、类型变为Incremental、命名空间变为zerk、标签变为{ host zoobub }——与 RFC 文档级提案中.name/.namespace/.kind/.tags的读写行为完全一致。另一个值得关注的是check_remap_branchingsrc/transforms/remap.rs它验证了同一脚本对日志和指标的分支处理if exists(.tags) { # 指标分支 .tags.foo bar if string!(.tags.hello) goodbye { abort } } else { # 日志分支 .foo bar if string(.hello) goodbye { abort } }这里用exists(.tags)区分指标与日志事件并验证了abort/报错时reroute_dropped的行为被丢弃的指标事件会在标签中写入metadata.dropped.reason、metadata.dropped.component_id等注解见 remap.rs 的Event::Metric分支。这说明指标与日志在错误处理路径上同样具备对等支持。备选方案评估为什么没有走函数式 APIRFC 的 Alternatives 部分详细评估了另外四条路线这些讨论对理解最终设计非常有价值方案一提供操作指标的函数如get_name() - string、set_name(string)、get_namespace()、set_namespace(string)、get_timestamp()、set_timestamp(timestamp)、get_kind()、set_kind(string)、get_tag(string)、add_tag(string, string)、rename_tag(string, string)、remove_tag(string)等一整套函数式 API。RFC 指出此方案中指标事件不能使用路径访问accessing path valuesidentifiers starting with a.are not available for metric events。最终选定的路径方案本 RFC 主体比函数式 API 更贴合 VRL 处理日志事件时的既有心智模型且不排斥后续补充函数。方案二直接操作指标数据本身例如get_gauge_value/set_gauge_value。RFC 认为这很复杂因为指标类型众多Counter、Gauge、AggregatedHistogram……每个函数只对正确类型的指标生效类型不符即报错。此能力已由Lua转换提供用户仍有出路。注意采纳原提案并不排斥此方案可作为下一阶段实现。方案三日志与指标事件互转例如. to_log()把当前指标事件写入根路径生成日志事件或make_counter(value)、make_gauge(value)、make_set([value])按类型构造指标事件。RFC 认为这也可在后续阶段实现。方案四针对高频用例提供专用函数例如把一串 gauge 采样值转成采样分布sample_gauge_values([buckets], sample_rate)。其难点在于输入与输出事件不再是严格的一对一关系多个输入事件汇总为一个输出事件还需考虑脚本中重复调用时的语义。RFC 设想若能在 VRL 内覆盖约 80% 的高频用例将是配置简化上的重大胜利但仍留作下一阶段。权衡、遗留问题与实施计划代价RFC 坦承了两个 drawback为维护指标路径VALID_METRIC_PATHS_SET等带来持续维护负担把指标数据结构暴露给 Remap 语言后若未来需要修改内部模型将受到向后兼容约束——暴露给语言的越多未来变更自由度越小。遗留问题RFC 提出的 Outstanding Question 是是否应为 remap 转换增加event_type选项强制用户声明脚本面向日志还是指标从而在加载期而非运行期就对使用了错误类型函数的脚本报错从当前实现看RemapConfig中并未出现该选项脚本通过exists(.tags)等方式在运行期区分事件类型——这也是 RFC 中初步在运行时报错方案的延续。实施计划Plan Of AttackRFC 规划的增量实施步骤为调整 Remap 转换使其同时处理日志与指标事件为指标事件实现路径访问remap-lang::Object。对照当前仓库这两步均已落地VrlTarget::Metric变体、MetricTagMode标签模式、precompute_metric_value预计算、MAX_METRIC_PATH_DEPTH深度约束以及check_remap_metric/check_remap_branching等测试共同构成了指标事件在 VRL 中的完整读写链路。总结RFC 3550 为 Vector 的 Remap 语言补齐了指标事件支持让remap转换从日志专用升级为日志 指标统一处理。通过.name、.namespace、.timestamp、.kind、.tags以及实现中补充的.interval_ms与只读的.type这组固定路径配合严格的类型约束与不允许嵌套的深度限制用户可以用一份简单 VRL 脚本替换add_tags/remove_tags/rename_tags乃至大部分需要Lua兜底的标签处理场景。在配置管理上善用metric_tag_valuessingle/full/auto可以精确控制标签值在 VRL 中的暴露形态在排错上drop_on_error、drop_on_abort与reroute_dropped共同保障了指标处理失败时的可观测性。对于更复杂的指标值运算、事件类型转换与采样聚合RFC 明确将其留给了Lua/Wasm转换或未来阶段这一清晰的边界正是 VRL 保持简单而强大的关键。【免费下载链接】vectorA high-performance observability data pipeline.项目地址: https://gitcode.com/GitHub_Trending/vect/vector创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考