Grafana Tempo 中的 OTTL Profile Context:面向 OTLP Profile 数据的转换与过滤编程指南
2026/9/19 20:01:26 网站建设 项目流程

Grafana Tempo 中的 OTTL Profile Context:面向 OTLP Profile 数据的转换与过滤编程指南

【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo

导读

本文围绕 OTTL Profile Context 展开,它是 OpenTelemetry Collector Contrib 中 OTTL(OpenTelemetry Transformation Language)为pdata Profiles(OTLP profile 数据的内部表示)提供的专用上下文实现。在 Grafana Tempo 这类以 OTLP 为数据面的分布式追踪后端中,OTTL Profile Context 是编写 Profile 数据过滤与变换语句(如 filter processor 的profile_conditions)时的核心编程入口。读完本文,你将掌握 Profile Context 支持的完整路径清单、各字段的读写语义、其与资源/作用域上下文的层级关系,以及如何在 filter processor 中落地实战。

[!NOTE] 本文涉及的文档与源码均来自当前仓库的 vendor 依赖目录,文档声明该能力仅适用于0.124.0及更高版本。


一、背景:OTTL 与 Profile 数据模型

OTTL 是 OpenTelemetry Collector Contrib 提供的一套面向遥测数据的变换/过滤语言,它通过"上下文(Context)"把语言表达式与具体的遥测数据结构绑定。目前仓库 vendor 中提供了一系列上下文实现:

  • ottldatapoint
  • ottllog
  • ottlmetric
  • ottlprofile(本文主题)
  • ottlresource
  • ottlscope
  • ottlspan
  • ottlspanevent

Profile Context 是其中的"Profile 专用"实现,它面向 pdata Profiles,即 collector 内部对 OTLP profile 数据的表示。凡是需要与 OTLP profiles 交互的场景(例如按 Profile 字段过滤采样、改写 profile 属性、读取/写入 profile 时间与周期),都应使用 Profile Context 作为语句求值的载体。


二、Profile Context 的核心机制

2.1 上下文层级

从源码结构看,Profile 数据在 OTLP 中遵循ResourceProfiles -> ScopeProfiles -> Profile的三级嵌套关系。TransformContext正是对这一层级的封装。见 ottlprofile/profile.go:

type TransformContext struct { profile pprofile.Profile dictionary pprofile.ProfilesDictionary instrumentationScope pcommon.InstrumentationScope resource pcommon.Resource cache pcommon.Map scopeProfiles pprofile.ScopeProfiles resourceProfiles pprofile.ResourceProfiles }

它同时实现了ctxresource.Contextctxscope.Contextctxprofile.Context三个内部接口(见同文件 L37-L42),这意味着在一条 OTTL 语句中你可以无缝访问 Resource 层与 InstrumentationScope 层的字段——这正是下文路径表中resource.*instrumentation_scope.*得以存在的底层原因。

2.2 实例生命周期:对象池复用

为了支撑高吞吐的 profile 处理,TransformContext通过sync.Pool复用实例(见 profile.go):

var tcPool = sync.Pool{ New: func() any { return &TransformContext{cache: pcommon.NewMap()} }, }
  • NewTransformContextPtr(...)从池中取出实例并填充字段;
  • 使用完毕后必须调用Close(),该方法会清空各字段并归还池中(见 profile.go);
  • NewTransformContext(非指针版本)已在v0.145.0被标记为 Deprecated,新代码应使用指针版本。

2.3 枚举与 Context 名

ContextName = ctxprofile.Name,其值为字符串"profile"(见 ctxprofile/context.go)。Profile Context 不支持枚举符号(parseEnum直接返回enum symbol not found错误,见 profile.go)。


三、支持的路径(Paths)完整清单

Profile Context 整体遵循 profiles proto 中的字段命名。所有整数类型均以int64读写,所有双精度浮点类型均以float64读写。下表是文档声明支持的全部路径,字段类型引用自 pdata 数据类型:

pathfield accessedtype
cache当前变换上下文临时缓存的值。cache可在复杂变换中作为数据临时占位符pcommon.Map
cache[""]cache 中某个条目的值。支持多重索引以访问嵌套字段。string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte 或 nil
resource被处理 profile 的 resourcepcommon.Resource
resource.attributes被处理 profile 的 resource 属性pcommon.Map
resource.attributes[""]被处理 profile 的 resource 属性值。支持多重索引以访问嵌套字段。string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte 或 nil
instrumentation_scope被处理 profile 的 instrumentation scopepcommon.InstrumentationScope
instrumentation_scope.name被处理 profile 的 instrumentation scope 名称string
instrumentation_scope.version被处理 profile 的 instrumentation scope 版本string
instrumentation_scope.attributes被处理数据点的 instrumentation scope 属性pcommon.Map
instrumentation_scope.attributes[""]被处理数据点的 instrumentation scope 属性值。支持多重索引以访问嵌套字段。string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte 或 nil
profile.attributes被处理 profile 的属性pcommon.Map
profile.attributes[""]被处理 profile 的属性值。支持多重索引以访问嵌套字段。string, bool, int64, float64, pcommon.Map, pcommon.Slice, []byte 或 nil
profile.sample_type被处理 profile 的 sample typepprofile.ValueType
profile.sample_type.typeprofile.sample_type关联的类型string
profile.sample_type.unitprofile.sample_type关联的单位string
profile.sample被处理 profile 的 samplespprofile.SampleSlice
profile.time_unix_nano被处理 profile 的 unix nano 时间int64
profile.time被处理 profile 的time.Time时间time.Time
profile.duration_unix_nano被处理 profile 的 unix nano 时长int64
profile.duration被处理 profile 的纳秒时长int64
profile.period_type被处理 profile 的 period typepprofile.ValueType
profile.period_type.typeprofile.period_type关联的类型string
profile.period_type.unitprofile.period_type关联的单位string
profile.period被处理 profile 的 periodint64
profile.profile_id被处理 profile 的 profile idpprofile.ProfileID
profile.profile_id.stringprofile id 的字符串表示string
profile.attribute_indices被处理 profile 的 attribute indices[]int64
profile.dropped_attributes_count被处理 profile 的 dropped attributes countint64
profile.original_payload_format被处理 profile 的原始载荷格式string
profile.original_payload被处理 profile 的原始载荷[]byte
otelcol.*ottlotelcol 上下文暴露的所有路径varies

注:上表为原文档的完整表格,未做删减;其中cache[""]resource.attributes[""]等索引写法中的""表示具体键名/索引。


四、路径的源码级实现:每个字段的 Get/Set 语义

路径的解析入口在 ctxprofile/profile.go 的PathGetSetter:它对path.Name()做 switch 分发,把sample_typesampletime_unix_nanotimeduration_unix_nanodurationperiod_typeperiodprofile_idattribute_indicesdropped_attributes_countoriginal_payload_formatoriginal_payloadattributes等字段分别映射到对应的 accessor。理解这些 accessor 的实现,能帮你预判 OTTL 语句在读写时会遇到的行为与边界条件。

4.1 时间与时长:int64 与非负校验

  • profile.time_unix_nano的 Getter 返回Time().AsTime().UnixNano(),Setter 接受int64并通过pcommon.NewTimestampFromTime(time.Unix(0, i))写回(profile.go L89-L103)。
  • profile.time的 Getter 直接返回time.Time,Setter 同样接受time.Time(L105-L119)。
  • profile.duration_unix_nanoprofile.duration在 Setter 中对负值直接报错duration_unix_nano must be non-negative,因为底层DurationNanouint64(L121-L157)。这意味着负时长无法通过 OTTL 写入

4.2 Profile ID:二进制与十六进制字符串

  • profile.profile_id读写pprofile.ProfileID,且 Setter 拒绝空 ID(profile ids must not be empty,L189-L206)。
  • profile.profile_id.string将 ID 以hex.EncodeToString编码为小写十六进制字符串返回;Setter 则通过ctxcommon.ParseProfileID解析字符串并校验非空(L208-L230)。在条件表达式中比较 profile ID 时,使用.string形式更便于与日志中常见的十六进制表示对齐。

4.3 ValueType 与字符串字典(strindex)

profile.sample_typeprofile.period_type都是pprofile.ValueType,其底层存储并非直接字符串,而是指向 profile 数据字典字符串表的索引(TypeStrindex/UnitStrindex)。见 ctxprofile/value_type.go:

  • 读取profile.sample_type.type时,先检查currIndex是否越界(strindex %d is out of range),再从ProfilesDictionary.StringTable()中取回字符串;
  • 写入时,若当前索引对应的字符串与新值相同则复用索引;否则调用pprofile.SetString向字符串表追加新值并返回新索引,从而在保持 profile 数据"字典化"存储的同时提供字符串级读写语义。

对使用方而言,这意味着你可以像操作普通字符串一样读写sample_type.typesample_type.unitperiod_type.typeperiod_type.unit,而无需关心字典索引细节。

4.4 属性与原始载荷

  • profile.attributes支持两种形态:无索引时返回整个pcommon.Map(通过ctxprofilecommon.AccessAttributes),带索引(如profile.attributes["key"])时返回具体值(AccessAttributesKey,L60-L67),与resource.attributes[""]instrumentation_scope.attributes[""]行为一致。
  • profile.original_payload[]byte形式读写OriginalPayload().FromRaw(...),可用于携带 pprof 等原始格式(L275-L288)。
  • profile.attribute_indices[]int64形式读写底层int32切片(L232-L241)。

五、实战:在 Filter Processor 中使用 Profile Context

Profile Context 最典型的生产场景是 filter processor,它允许通过profile_conditions按 OTTL 布尔表达式丢弃匹配的 profile。在 filter processor 的 profiles.go 中可以看到其调用方式:

dic := pd.Dictionary() pd.ResourceProfiles().RemoveIf(func(rp pprofile.ResourceProfiles) bool { resource := rp.Resource() // 先按 resource 级条件过滤 ... rp.ScopeProfiles().RemoveIf(func(sp pprofile.ScopeProfiles) bool { sp.Profiles().RemoveIf(func(profile pprofile.Profile) bool { tCtx := ottlprofile.NewTransformContextPtr(rp, sp, profile, dic) defer tCtx.Close() skip, err := fpp.skipProfileExpr.Eval(ctx, tCtx) ... }) return sp.Profiles().Len() == 0 }) return rp.ScopeProfiles().Len() == 0 })

对应到配置文件中,filter processor 的 config.go 定义了ProfileFilters结构,其中ProfileConditions的注释明确说明:"如果任一条件求值为 true,该 profile 将被丢弃"。

filter processor 支持两种等价写法(二者不可混用,混用会报cannot use context inferred profile conditions ...错误,见 config.go):

写法一:统一的profile_conditions(推荐,支持resource.scope.profile.前缀混合)

processors: filter/profiles: error_mode: ignore profile_conditions: - resource.attributes["host.name"] == "test" - profile.duration_unix_nano > 3000 - instrumentation_scope.name == "pyroscope"

说明:profile_conditions通过condition.NewProfileParserCollection构建,配置了WithProfileCommonParsersWithProfileParser,因此同一列表中既可出现resource./scope.前缀的表达式(resource/scope 上下文),也可出现profile.前缀的表达式(profile 上下文),前缀决定了语句归属的求值层级。

写法二:分层的profiles.resourceprofiles.profile(旧式,已标记 Deprecated)

processors: filter/profiles: error_mode: ignore profiles: resource: - resource.attributes["host.name"] == "test" profile: - profile.duration_unix_nano > 3000

两者的求值顺序与丢弃语义在源码中体现为:

  1. 先对 ResourceProfiles 求值skipResourceExpr,命中则整组 ResourceProfiles 被移除(profiles.go L110-L123);
  2. 再逐层下钻到 Profile,对每个 profile 求值skipProfileExpr,命中则移除该 profile(L127-L142);
  3. 处理完后,如果ResourceProfiles().Len() == 0,processor 返回ErrSkipProcessingData跳过下游(L101-L103),并通过pd.SampleCount()前后差值记录被过滤的样本数(L84-L94)。

六、上下文扩展:otelcol.*路径与路径上下文名

6.1otelcol.*委托路径

路径表中最后一行的otelcol.*表示 Profile Context 继承了 ottlotelcol 上下文暴露的全部路径(典型如otelcol.resource.attributes等与 collector 元信息相关的字段)。在pathExpressionParser(见 profile.go)中,ctxotelcol.PathGetSetterctxresourcectxscopectxprofile并列注册,实现了这一委托关系。

6.2 显式路径前缀(EnablePathContextNames)

对于存在歧义的语句(例如同一路径名在不同上下文中的含义不同),可以启用EnablePathContextNames()选项(见 profile.go L161-L171),它注册了以下合法上下文前缀:

  • profilectxprofile.Name
  • scopescope(legacy 名)
  • resource
  • otelcol

启用后,所有语句路径必须带合法上下文前缀,否则报错。该选项被标记为 Experimental,未来可能变更或移除。

6.3 错误模式与语句序列

NewParser支持注入自定义函数(functions map[string]ottl.Factory[*TransformContext])与 OTTL 选项;NewStatementSequence/NewConditionSequence分别支持通过WithStatementSequenceErrorMode/WithConditionSequenceErrorMode设置ErrorMode(如ignorepropagatesilent),这是 filter processor 中error_mode配置的底层实现入口(见 profile.go L146-L209)。


七、调试与可观测性

TransformContext实现了zapcore.ObjectMarshaler(见 profile.go L56-L62),序列化输出包含resourcescopeprofilecache四部分。这在开启 OTTL 调试日志时非常有用——filter processor 的 README 示例展示了形如condition evaluation result {"match": true, "TransformContext": {"resource": ..., "scope": ..., "profile": ..., "cache": {}}}的日志输出,可直接查看条件求值时上下文中的实际字段值,用于排查条件未命中或误命中问题。


八、小结

OTTL Profile Context 将 OTLP profile 的 pdata 模型以路径形式完整暴露给 OTTL 语言,是编写 profile 过滤、变换、校验逻辑的统一入口。其要点可归纳为:

  • 层级完整:一个 TransformContext 同时承载resourceinstrumentation_scopeprofile三层数据,语句可跨层访问;
  • 类型统一:整数以int64、双精度浮点以float64读写,复合类型(Map/Slice/ValueType/ProfileID)遵循 pdata 类型;
  • 字典透明sample_type/period_type的 type/unit 虽底层存储为字符串表索引,但对 OTTL 用户呈现字符串语义;
  • 实战落地:在 filter processor 中通过profile_conditions(或已弃用的profiles.profile)即可基于上述路径完成 profile 级过滤,配合error_mode控制错误行为。

如需进一步深入,可继续阅读仓库中的相关实现:Profile Context 源码、路径 accessor 实现、filter processor profiles 处理 以及 filter processor 配置定义。

【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询