OTTL Metric Context 完整解析:在 OpenTelemetry Collector 中以 OTTL 读写 pdata 指标数据

OTTL Metric Context 完整解析:在 OpenTelemetry Collector 中以 OTTL 读写 pdata 指标数据 OTTL Metric Context 完整解析在 OpenTelemetry Collector 中以 OTTL 读写 pdata 指标数据【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo导读本文围绕 OpenTelemetry Collector Contrib 中的Metric Contextottlmetric展开讲解如何在 OTTLOpenTelemetry Transformation Language语句与条件中访问、校验和改写单条 OTLP 指标Metric及其关联的 Resource、Instrumentation Scope 与缓存。文章覆盖 Metric Context 的全部受支持 Path、枚举符号并结合本仓库 vendor 目录下的pkg/ottl/contexts/ottlmetric源码与 filter/transform 处理器的真实调用链帮助读者在配置 filter processor、transform processor 等组件时准确编写可用的指标级 OTTL 表达式并理解其底层实现。一、Metric Context 是什么Metric Context 是 OTTL 针对pdata Metric的一种 Context 实现pdatapmetric是 OpenTelemetry Collector 内部对 OTLP 指标opentelemetry/proto/metrics/v1/metrics.proto的内存表示。它适用于“与单条 OTLP 指标交互”的场景例如在filter processor的metric_conditions中按指标名、指标类型、Resource 属性等条件丢弃指标在transform processor中改写指标描述、单位、类型等字段在自定义 Collector 组件中基于该 Context 构造布尔表达式判断指标是否应被处理。在 OTTL 体系里每个信号Signal都有自己的 ContextResource、Instrumentation Scope、Span、Span Event、Metric、DataPoint、Log、Profile。Metric Context 处于指标信号的中间层级其层级关系为resource → scope → metric → datapoint该层级也体现在 filter processor 的文档中vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/filterprocessor/README.md。版本说明Metric Context 文档仅适用于0.120.0及以后的 opentelemetry-collector-contrib 版本早期版本行为请查阅对应的 release 分支文档。二、源码定位与上下文结构Metric Context 的实现位于仓库 vendor 目录文档vendor/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric/README.md实现vendor/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/ottlmetric/metrics.go内部指标上下文vendor/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal/ctxmetric/context.go从metrics.go的源码可以看到TransformContext携带一条指标及其完整层级type TransformContext struct { resourceMetrics pmetric.ResourceMetrics scopeMetrics pmetric.ScopeMetrics metric pmetric.Metric cache pcommon.Map }它同时实现了ctxresource.Context、ctxscope.Context、ctxmetric.Context三个内部接口因此同一个 TransformContext 既能暴露resource.*、instrumentation_scope.*路径也能暴露metric.*路径。ctxmetric.Context接口只要求一个方法GetMetric() pmetric.Metric对应metric.*路径的字段访问都由此展开。2.1 上下文的生命周期管理值得注意的源码细节是Metric Context 使用sync.Pool复用 TransformContext 对象NewTransformContextPtr(resourceMetrics, scopeMetrics, metric, options...)从对象池取出实例并填充数据Close()清空字段并放回池中tcPool.Put(tCtx)。这意味着在 filter/transform 处理器内部每个指标都会创建一个上下文、求值后立即归还避免高频指标处理场景下的频繁分配。调用方例如filterprocessor/metrics.go中的processSkipExpression会严格按照NewTransformContextPtr(...)→Eval(...)→Close()的顺序使用tCtx : ottlmetric.NewTransformContextPtr(rm, smetrics, metric) skip, err : fmp.skipMetricExpr.Eval(ctx, tCtx) tCtx.Close()2.2 解析器的组装NewParser(functions, telemetrySettings, options...)通过ctxcommon.NewParser组装路径解析器将metric、resource、scope含instrumentation_scope旧名、otelcol等多个子上下文注册进统一路径表map[string]ottl.PathExpressionParser[*TransformContext]{ ctxresource.Name: ctxresource.PathGetSetter[*TransformContext], ctxscope.Name: ctxscope.PathGetSetter[*TransformContext], ctxscope.LegacyName: ctxscope.PathGetSetter[*TransformContext], ctxmetric.Name: ctxmetric.PathGetSetter[*TransformContext], ctxotelcol.Name: ctxotelcol.PathGetSetter[*TransformContext], }因此在 metric 上下文中你可以同时使用metric.*、resource.*、instrumentation_scope.*以及旧写法scope.*和otelcol.*路径而解析器会依据路径前缀把它们分发到对应的 GetSetter 实现上。三、完整 Path 参考表Metric Context 支持通过 metrics proto 中的字段名来访问 pdata 数据。所有整数值以int64读取/写入所有浮点值以float64读取/写入。下表为当前文档声明的全部受支持路径path访问的字段类型metric.cache当前转换上下文临时缓存的值可在复杂转换中作为数据占位pcommon.Mapmetric.cache[]cache 中某个条目的值支持多重索引访问嵌套字段string,bool,int64,float64,pcommon.Map,pcommon.Slice,[]byte或nilresource被处理指标所属的 resourcepcommon.Resourceresource.attributes被处理指标的 resource attributespcommon.Mapresource.attributes[]resource 属性的值支持多重索引访问嵌套字段string,bool,int64,float64,pcommon.Map,pcommon.Slice,[]byte或nilinstrumentation_scope被处理指标的 instrumentation scopepcommon.InstrumentationScopeinstrumentation_scope.nameinstrumentation scope 的名称stringinstrumentation_scope.versioninstrumentation scope 的版本stringinstrumentation_scope.attributesinstrumentation scope 的属性pcommon.Mapinstrumentation_scope.attributes[]instrumentation scope 属性的值支持多重索引访问嵌套字段string,bool,int64,float64,pcommon.Map,pcommon.Slice,[]byte或nilmetric.name指标名称stringmetric.description指标描述stringmetric.unit指标单位stringmetric.type指标数据类型int64metric.metadata与指标关联的元数据pcommon.Mapmetric.aggregation_temporality指标的聚合时态temporalityint64metric.is_monotonic指标的单调性boolmetric.data_points指标的数据点pmetric.NumberDataPointSlice、pmetric.HistogramDataPointSlice、pmetric.ExponentialHistogramDataPointSlice或pmetric.SummaryDataPointSliceotelcol.*ottlotelcol 上下文暴露的全部路径视具体字段而定3.1 使用要点metric.cache与metric.cache[key]是本上下文独有的临时缓存能力缓存从 OTTL 上下文创建时初始化sync.Pool的New函数中cache: pcommon.NewMap()每次Close()时通过cache.Clear()清空。它适合在多个语句之间暂存中间计算结果。属性/缓存的多重索引语法形如resource.attributes[a][b]可用于访问嵌套的pcommon.Map或pcommon.Slice。metric.data_points的类型是切片slice具体是 Number、Histogram、ExponentialHistogram 还是 Summary 取决于metric.type的取值因此在条件中通常需要先判断metric.type再使用对应类型的数据点。otelcol.*将 otelcol 上下文pkg/ottl/contexts/ottlotlcol暴露的全部路径注入本上下文可用于访问 Collector 自身的运行时信息例如组件 ID、配置信息等具体路径以 ottlotelcol 文档为准。3.2 底层实现枚举解析与 SymbolTablemetrics.go中的parseEnum负责将 OTTL 语句里的枚举符号解析为数值它直接查询ctxmetric.SymbolTablefunc parseEnum(val *ottl.EnumSymbol) (*ottl.Enum, error) { if val ! nil { if enum, ok : ctxmetric.SymbolTable[*val]; ok { return enum, nil } return nil, fmt.Errorf(enum symbol, %s, not found, *val) } return nil, errors.New(enum symbol not provided) }也就是说Metric Context 中可用的枚举名称并非魔法字符串而是由内部ctxmetric包维护的符号表集中定义语句中写错枚举名会在解析阶段直接报enum symbol, X, not found。四、枚举Enums参考表Metric Context 支持来自 metrics proto 的枚举名同时也支持 pdata 定义的指标数据类型枚举其数值定义见go.opentelemetry.io/collector/pdata/pmetric/metrics.go。完整枚举清单如下枚举符号值AGGREGATION_TEMPORALITY_UNSPECIFIED0AGGREGATION_TEMPORALITY_DELTA1AGGREGATION_TEMPORALITY_CUMULATIVE2METRIC_DATA_TYPE_NONE0METRIC_DATA_TYPE_GAUGE1METRIC_DATA_TYPE_SUM2METRIC_DATA_TYPE_HISTOGRAM3METRIC_DATA_TYPE_EXPONENTIAL_HISTOGRAM4METRIC_DATA_TYPE_SUMMARY54.1 实践建议聚合时态Aggregation Temporality枚举用于与metric.aggregation_temporalityint64比较例如判断 Sum/Histogram 是 Delta 还是 Cumulativemetric.aggregation_temporality AGGREGATION_TEMPORALITY_CUMULATIVE数据类型枚举用于与metric.typeint64比较例如只处理 Summetric.type METRIC_DATA_TYPE_SUM这两个枚举类别不可混用METRIC_DATA_TYPE_*只对metric.type有意义AGGREGATION_TEMPORALITY_*只对metric.aggregation_temporality有意义。五、Metric Context 在 Collector 处理器中的真实应用5.1 filter processor 中的 metric_conditionsfilter processor 使用 OTTL 条件决定丢弃哪些遥测数据条件之间是 OR 关系任一条件满足即丢弃。在metric_conditions列表中可以使用的上下文包括resource、scope、metric和datapoint见 filterprocessor/README.md。典型配置来源于该 README 的 Basic Config 示例processors: filter: error_mode: propagate metric_conditions: - metric.name my.metric and resource.attributes[my_label] abc123 - metric.type METRIC_DATA_TYPE_HISTOGRAM - resource.attributes[service.name] my_service_name底层求值调用链在 filter processor 的实现中vendor/github.com/open-telemetry/opentelemetry-collector-contrib/processor/filterprocessor/metrics.gofilterMetricProcessor同时持有三个表达式skipResourceExpr expr.BoolExpr[*ottlresource.TransformContext] skipMetricExpr expr.BoolExpr[*ottlmetric.TransformContext] skipDataPointExpr expr.BoolExpr[*ottldatapoint.TransformContext]处理指标时按层级自顶向下执行先对ResourceMetrics求值skipResourceExpr再对每个Metric求值skipMetricExpr即本篇文章的 Metric Context最后对 DataPoint 求值skipDataPointExpr。每层丢弃后上层会检查剩余数量若全部被丢弃则连带移除上层对象——这与 README 中“如果某个 metric 的所有 datapoint 都被丢弃则该 metric 也会被丢弃”的行为完全一致。processMetrics中还通过metricDataPointCountBeforeFilters - metricDataPointCountAfterFilters统计被过滤的数据点数量并记录到处理器遥测中。条件序列的构建filterottl.NewBoolExprForMetric见vendor/github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter/filterottl/filter.go负责把配置中的条件字符串解析为可求值的条件序列func NewBoolExprForMetric(conditions []string, functions map[string]ottl.Factory[*ottlmetric.TransformContext], errorMode ottl.ErrorMode, set component.TelemetrySettings) (*ottl.ConditionSequence[*ottlmetric.TransformContext], error) { parser, err : ottlmetric.NewParser(functions, set, parserOptions...) statements, err : parser.ParseConditions(conditions) c : ottlmetric.NewConditionSequence(statements, set, ottlmetric.WithConditionSequenceErrorMode(errorMode)) return c, nil }即ottlmetric.NewParser解析路径/枚举→ParseConditions语法解析→NewConditionSequence组合为条件序列。解析、构造失败会在配置加载阶段直接报错。5.2 error_mode 的三种行为filter processor 的error_mode决定条件求值出错时的处理策略error_mode说明ignore忽略条件返回的错误、记录日志并继续执行下一条条件推荐且在多数发行版为默认silent忽略错误且不记录日志继续执行下一条条件propagate将错误向上传递导致该批次数据被丢弃对应到代码WithConditionSequenceErrorMode(errorMode)把该模式注入ConditionSequence条件求值时按此策略处理错误。5.3 transform processor 与语句序列在 transform processor 中Metric Context 同样被用作语句求值上下文。与 filter 不同transform 以Statement函数 可选条件形式改写指标。metrics.go提供了配套的语句序列 APINewStatementSequence(statements, telemetrySettings, options...)创建语句序列NewConditionSequence(conditions, telemetrySettings, options...)创建条件序列WithStatementSequenceErrorMode/WithConditionSequenceErrorMode分别设置语句/条件求值的错误模式。一个典型的 transform 语句形如set(metric.description, new description) where metric.name http.server.duration5.4 调试技巧当 OTTL 语句或条件行为不符合预期时可在 Collector 配置中把日志级别调为 debug参考 pkg/ottl/README.mdservice: telemetry: logs: level: debug此时 OTTL 会输出初始 TransformContext、每条语句执行后的 TransformContext包含 resource、scope、metric 与 cache 的快照序列化逻辑见metrics.go的MarshalLogObject帮助你准确判断 OTTL 视角下底层数据的真实形态。六、Metric Context 相关的 OTTL 背景知识6.1 OTTL 语句的两部分结构OTTL 语句由两部分组成一个转换函数 一个可选的执行条件。例如set(span.attributes[test], pass) where span.attributes[test] nil在 metric 场景下对应地set(metric.unit, s) where metric.name http.server.duration其中set是函数where之后是条件条件中引用的metric.*、resource.*等路径即由 Metric Context 提供。OTTL 目前不支持跨信号引用因此不能在 metric 上下文中访问 span 或 log 的字段。6.2 条件的层级推断与执行顺序filter processor 会基于条件中出现的路径前缀自动推断上下文Context Inference。例如metric.name x推断为 metric 上下文resource.attributes[host] localhost推断为 resource 上下文。同一条信号下的多个条件按层级顺序执行resource → scope → metric → datapoint高层对象被丢弃后低层条件不会再被检查。若单条条件混用了不同层级的路径如metric.name a or resource.attributes[b] c处理器会在较低层级metric上求值——因为高层信息在低层上下文中总是可见的。6.3 可用的 OTTL 函数filter processor 与 transform processor 可访问全部 OTTL 转换函数Converter并额外提供少量处理器自有函数。与 metric 相关的有两个HasAttrKeyOnDatapoint(key)若指标的任一 datapoint 的属性 Map 中存在给定 key返回trueHasAttrOnDatapoint(key, value)若指标的任一 datapoint 的属性 Map 中存在给定 key 且值为给定 value非字符串属性值按比较返回true。例如HasAttrOnDatapoint(http.method, GET)需要说明的是这两个函数在实现中要求使用 metric 上下文并且依赖对指标 datapoint 的遍历——从源码结构看这类“指标级但涉及 datapoint 内容”的函数正是 Metric Context 的典型使用场景过滤粒度是整条指标判断依据来自其数据点。七、在 Grafana Tempo 中的关联位置本仓库Grafana Tempo将 opentelemetry-collector-contrib 作为 vendor 依赖引入pkg/ottl及其上下文实现位于 vendor/github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl 下。从仓库源码结构看Tempo 的modules/distributor/forwarderforwarder.go引用了pkg/ottl与processor/filterprocessor用于在 trace 转发链路中基于 OTTL 表达式进行过滤同目录的测试 ottl_ismatch_test.go 也印证了 OTTL 表达式在 Tempo 分发链路中的实际运用。因此本仓库中的 OTTL 相关文档与实现对于在 Tempo 及其依赖的 Collector 组件中编写指标级过滤/转换规则具有直接的参考价值。八、小结Metric Contextottlmetric是 OTTL 访问 pdata Metric 的标准入口本文覆盖了它的全部受支持路径与枚举、上下文对象结构、解析器组装方式以及它在 filter/transform 处理器中的真实调用链与配置示例。实践时可遵循以下要点字段路径以 metrics proto 字段名为准整数用int64、浮点用float64判断指标类型用metric.type METRIC_DATA_TYPE_*判断聚合时态用metric.aggregation_temporality AGGREGATION_TEMPORALITY_*可同时使用metric.*、resource.*、instrumentation_scope.*与otelcol.*路径临时数据放metric.cache跨语句传递出错处理优先使用error_mode: ignore配合 debug 日志排查条件行为。【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考