
Grafana Tempo TraceQL 扩展深度解析转义属性名、新增作用域与数据类型【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo本文基于 Grafana Tempo 官方设计文档 2023-11 TraceQL Extensions 编写系统讲解 TraceQL 语言在 2023 年下半年引入的四大扩展能力带引号的属性名转义、trace/scope/event/link 四个新增作用域、作用域内建字段Scoped Intrinsics以及数组与 ID 两类新增数据类型。结合仓库内 pkg/traceql 的源码与测试用例本文既可作为 TraceQL 进阶查询的实战速查也能帮助读者理解扩展能力在解析器、求值引擎中的落地方式。说明本文对应的设计文档定位是“邀请社区对语言扩展的基本结构与概念进行评论”并非完整的语言规范。文中所有语法与行为描述均以该设计文档为准并辅以仓库源码佐证涉及实现细节的推断会明确标注。一、设计背景为什么要扩展 TraceQLTraceQL 是 Grafana Tempo 的查询语言用于在 trace 数据中按结构、属性与值筛选 spans。原版语言主要面向span、resource两个作用域且属性名只能使用有限的字符集。随着 OpenTelemetry 数据模型引入更丰富的字段如 Instrumentation Scope、Span Event、Span Link以及用户对 trace 级、span 级 ID 的检索需求增长原有语法暴露出三方面不足属性名不兼容OpenTelemetry 允许属性名是任意合法 Unicode 序列包括空格、数学符号、花括号等查询语言难以解析的字符作用域不全缺少对 trace 级字段、instrumentation scope、event、link 的访问途径数据类型单一不支持数组类型属性也没有统一的 ID 类型比较规则。该设计文档针对上述三点提出了扩展方案。值得注意的是设计文档明确表示不会移除既有语法后文称为 “Legacy Intrinsics”以保证向后兼容。二、转义属性名Escaping Attribute NamesOpenTelemetry 规范允许属性名包含空白字符、数学符号、各类花括号等“对查询语言不友好”的字符而旧版 TraceQL 无法处理这类名字。仓库中 pkg/traceql/lexer.go 的isAttributeRune函数正是旧行为的体现空白字符unicode.IsSpace等会被判定为非属性字符。扩展方案是允许用双引号包裹属性名使其中任意字符被原样unparsed接受{ span.attribute with spaces foo }同时支持两个转义序列\\与\用于在属性名中表示反斜杠与双引号{ span.this is \bad\ foo }实现层面parseAttribute/parseQuotedAtrribute负责在扫描器层消费带引号片段并通过escapeRunes \“ 白名单校验转义序列遇到非法转义会返回 “invalid escape sequence” 错误参见 pkg/traceql/lexer.go。仓库的解析回归测试 pkg/traceql/test_examples.yaml 覆盖了大量带引号属性名用例包括空格、、制表符\t、换行\n、回车\r以及转义的\与\\- { .foo bar v } - { span.foo bar v } - { .foo\ \bar v } - { .a\\b v } - { .\foo\tbar\ \v\ }测试注释说明这些用例必须保证String()-Parse()往返round-trip后语义不变即带引号属性名是可序列化、可解析的合法语法。三、新增作用域trace、scope、event、link扩展后的 TraceQL 共支持以下属性作用域其枚举定义见 pkg/traceql/enum_attributes.gotrace、resource、span、event、link、instrumentation内部还有一个none表示无作用域。语法约定作用域与属性之间用.连接例如scope.foo作用域与内建字段intrinsic之间用:连接例如trace:duration。下文分述四个新增作用域。3.1 trace 作用域trace作用域用于访问 trace 级的内建字段不存在trace 级属性没有trace.xxx属性形式。典型用法是查询整个 trace 的持续时间{ trace:duration 100ms }完整的 trace 级内建字段清单见下文“作用域内建字段”一节。仓库测试 pkg/traceql/test_examples.yaml 验证了trace:duration、trace:rootName、trace:rootService、trace:id的合法性。3.2 Instrumentation ScopescopeInstrumentation Scope 是 OpenTelemetry 中描述“生成遥测数据所用埋点库信息”的对象包含库的名称、版本等同时既有内建字段也有属性。它使用scope关键字在源码 token 层面实际映射为instrumentation见 pkg/traceql/lexer.go 中instrumentation:与instrumentation.两个 token。属性访问与内建字段访问分别使用.与:{ scope.foo bar } { scope:name ~ .*Java.* }测试用例 pkg/traceql/test_examples.yaml 中同时出现了instrumentation.foo bar形式说明属性作用域为 instrumentation/scope 时两者等价。3.3 event 作用域Span Event 描述 span 生命周期内发生的离散事件如异常、日志包含属性与内建字段。一个 span 可以关联多个 event这是 event/link 与既有作用域的本质区别因此设计上刻意保守只提供最基础的访问能力{ event.exception.message ~ .*Division by zero.* } { event:name exception }当 span 上存在任意一个满足条件的 event 时上述运算符返回 true。注意默认只断言“存在性”无法表达“同一个 event 同时满足两个条件”这类复杂关系详见“未来考虑”一节。3.4 link 作用域Span Link 用于建立 span 与其他 trace 片段之间的关联同样包含属性与内建字段且一个 span 可关联多个 link{ link.foo bar } { link:traceID hex string }与 event 一致只要 span 上存在任意满足条件的 link表达式即为 true。在实现层面SpansetFilter.ReferencesEventOrLink()pkg/traceql/spanset_filter_match.go会检测过滤器是否读取了event:/link:作用域或对应内建字段以便调用方决定是否需要进行“每 span × event/link”的展开求值——这也是“一对多”关系在实现上的直接体现。四、作用域内建字段Scoped Intrinsics随着作用域增多为避免歧义设计文档规定所有既有内建字段Legacy Intrinsics继续保留暂无移除计划但不再新增无作用域的内建字段为所有 legacy intrinsics 提供带作用域的等价写法且未来新增的内建字段一律只能带作用域用:区分“同名的属性与内建字段”span.name访问 span 上名为name的属性而span:name访问 span 的内建字段 namespan 名称。完整内建字段表如下继承自设计文档作用域内建字段Legacy旧写法说明trace:durationtraceDuration该 trace 内任意 span 的最大结束时间减去最小开始时间:idTrace ID:rootNamerootName根 span 的span:name:rootServicerootServiceName根 span 的resource.service.namescope:nameInstrumentation scope 名称:versionInstrumentation scope 版本span:idSpan ID:namenameSpan 名称:durationdurationSpan 持续时间:statusstatusSpan 状态:statusMessagestatusMessageSpan 状态消息:kindkindSpan 类型event:nameEvent 名称link:spanIDLink 关联的 span ID:traceIDLink 关联的 trace IDparent:id父 span 的span:id上述内建字段在源码 pkg/traceql/enum_attributes.go 中有完整的Intrinsic枚举对应例如ScopedIntrinsicSpanStatus、ScopedIntrinsicTraceRootName、ScopedIntrinsicTraceDuration、IntrinsicEventName、IntrinsicLinkSpanID、IntrinsicInstrumentationName等其字符串形式pkg/traceql/enum_attributes.go与设计文档表格一一对应如trace:rootName、trace:rootService、trace:duration、event:name、event:timeSinceStart、link:spanID、link:traceID、instrumentation:name、instrumentation:version。值得注意的是源码中还实现了设计文档表格之外的扩展字段例如event:timeSinceStartevent 距 span 开始的时间与span:parentID它们同样可在查询中使用{ event:timeSinceStart 1s } { span:parentID 84737586494 }见 pkg/traceql/test_examples.yaml另外设计文档表格中trace作用域包含:rootName/:rootService而测试用例 pkg/traceql/test_examples.yaml 注释提示trace:rootServiceName属于应被淘汰的旧拼写正确写法应为trace:rootService。五、新增数据类型数组与 ID5.1 数组ArraysTraceQL 支持数组类型的属性使用[]语法访问按索引访问单个元素0 起始{ span.http.response.header.content-type[0] application/json }空方括号测试所有元素任一元素满足条件即为 true{ span.http.response.header.content-type[] application/json }设计文档特别说明当前没有数组字面量也没有针对数组类型本身的运算上述语法仅用于对数组元素执行既有操作。从源码 pkg/traceql/ast.go 可以印证Static静态值类型已具备TypeIntArray、TypeFloatArray、TypeStringArray、TypeBooleanArray四类数组类型及对应的NewStaticIntArray、NewStaticStringArray等构造函数说明求值引擎已能承载数组数据但语言层面的数组运算仍在设计文档所述的“暂不提供”状态。5.2 ID 类型span:id、trace:id等字段属于新的 “id” 数据类型使用规则如下只能与十六进制字符串比较只支持与!两种运算符{ span.id 8bf5306cb6a28 }比较时忽略前导 0span/trace ID 固定为 64 位或 128 位但前导零不影响相等判断因此下面两种写法等价{ trace.id 0007f2b8d1c69375e0d46a9cf8072bc4 } { trace.id 7f2b8d1c69375e0d46a9cf8072bc4 }这与仓库中 ID 序列化工具的行为一致pkg/util/traceid.go 的TraceIDToHexString会移除 trace ID 的前导零而SpanIDToHexString则保留前导零相关测试见 pkg/util/traceid_test.go。六、未来考虑Future Considerations设计文档对 link 与 event 有意保留了最小语法主要考量如下避免过早承诺不希望引入日后难以收回的语言特性先让社区通过基础访问能力实验并反馈需求当前能力边界目前只能断言“span 上是否存在带特定字段值的 link/event”。虽然覆盖了大量使用场景但存在明显缺口例如断言同一个 event同时满足两个条件跨 link 比较字段如比较多个 link 的字段值。演进方向作者期待社区在探索 link/event 时思考更复杂关系的表达方式以便后续快速跟进高级功能。对于使用者而言现阶段建议先充分利用“存在性断言”解决大部分问题若遇到上述复杂场景可考虑拆分为多条查询或结合 spanset 运算、||、结构运算符等在应用层组合结果。七、在仓库中继续探索以下是验证本文内容的关键源码与测试入口可按需深入阅读关注点文件作用域枚举与内建字段定义pkg/traceql/enum_attributes.go词法分析tokenMap、引号属性、转义pkg/traceql/lexer.go语法解析yacc 文法pkg/traceql/expr.y生成文件 pkg/traceql/expr.y.go静态值与数组类型pkg/traceql/ast.gospanset 过滤与 event/link 展开检测pkg/traceql/spanset_filter_match.go覆盖全部新语法的解析测试用例pkg/traceql/test_examples.yaml解析/词法单元测试pkg/traceql/parse_test.go、pkg/traceql/lexer_test.goTrace/span ID 十六进制转换前导零处理pkg/util/traceid.go设计文档原文docs/design-proposals/2023-11 TraceQL Extensions.md综上TraceQL Extensions 在保持旧语法向后兼容的前提下通过带引号属性名、四个新增作用域、作用域内建字段与数组/ID 类型显著拓宽了可查询的数据面。对使用 Grafana Tempo 进行 trace 检索的开发者掌握trace:、scope:、event:、link:与数组、ID 的语法即可针对“整条 trace 的耗时”“异常事件”“跨 span 关联”等场景写出更精确的查询。【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考