TraceQL
TraceQL 受 PromQL 和 LogQL 的启发,是一种为 Tempo 中选择追踪而设计的查询语言。目前,TraceQL 查询可以基于以下内容选择追踪:
- Span 和资源属性、时间以及时长
- 基本聚合:
count()
、avg()
、min()
、max()
和sum()
阅读博文 认识 TraceQL,了解 TraceQL 及其功能的简介。
这里应该有一个视频,但由于某种原因没有。可能是我们输入的 ID 有误(抱歉!),也可能是 Vimeo 服务中断。如果是后者,我们预计他们很快会恢复正常。在此期间,请访问我们的博客!
在可能的情况下,TraceQL 语言使用与 PromQL 和 LogQL 类似的语法和语义。
查看 Tempo 发布说明,了解 TraceQL 的最新更新。
要求
TraceQL 需要 Parquet 列式格式,该格式默认启用。有关 Parquet 的信息,请参阅 Apache Parquet 后端文档。
使用 TraceQL 查询
您可以在 Tempo 数据源中使用 TraceQL 查询编辑器和查询构建器来构建查询并下钻到结果集。编辑器和构建器可在 Grafana Explore 的 Tempo 数据源中使用。
此外,您可以使用 Traces Drilldown 来调查您的追踪数据,而无需编写 TraceQL 查询。有关更多信息,请参阅 Traces Drilldown 文档。
流式查询结果
通过将结果流式传输到客户端,您可以在整个查询完成之前开始查看与查询匹配的追踪。
查询前端的 GRPC 流式 API 端点允许客户端从 Tempo 流式传输搜索结果。tempo-cli
也使用此流式端点。有关更多信息,请参阅 Tempo CLI 文档。
要在 Grafana 中使用流式传输,必须在 Tempo 中启用 stream_over_http_enabled: true
。有关信息,请参阅 Tempo GRPC API。
构造 TraceQL 查询
在 TraceQL 中,查询是一个表达式,它一次对一个追踪进行求值。查询被构造为一组链式表达式,称为管道。管道中的每个表达式选择或丢弃要包含在结果集中的 spanset。例如:
{ span.http.status_code >= 200 && span.http.status_code < 300 } | count() > 2
在此示例中,搜索将追踪范围缩小到以下 span:
http.status_code
在200
到299
的范围内,并且- 一个追踪中匹配的 span 数量大于 2。
查询选择 span 集,并通过聚合器和条件的管道对其进行过滤。如果对于给定的追踪,此管道生成一个 spanset,则将其包含在查询结果中。
有关 TraceQL 指标查询的示例,请参阅 TraceQL 指标查询。
查找特定操作的追踪
假设您想查找特定操作的追踪,则应指定操作名称(span 属性 name
)和包含此操作的服务名称(资源属性 service.name
)以进行适当过滤。在下面的示例中,追踪是根据 resource.service.name
值 frontend
和 span name
值 POST /api/order
进行过滤的。
{resource.service.name = "frontend" && name = "POST /api/orders"}
当将相同的 Grafana stack 用于多个环境(例如,production
和 staging
)或具有共享相同名称但通过其命名空间区分的服务时,查询如下所示:
{
resource.service.namespace = "ecommerce" &&
resource.service.name = "frontend" &&
resource.deployment.environment = "production" &&
name = "POST /api/orders"
}
查找具有特定结果的追踪
此示例查找操作 POST /api/orders
上所有包含出错 span 的追踪:
{
resource.service.name="frontend" &&
name = "POST /api/orders" &&
status = error
}
此示例查找操作 POST /api/orders
上所有返回 HTTP 5xx 错误的追踪:
{
resource.service.name="frontend" &&
name = "POST /api/orders" &&
span.http.status_code >= 500
}
查找具有特定行为的追踪
您可以使用查询对追踪的多个 span 进行过滤。此示例查找访问数据库的 GET /api/products/{id}
操作的所有追踪。这是一项方便的请求,用于识别缓存问题引起的数据库异常访问率。
{span.service.name="frontend" && name = "GET /api/products/{id}"} && {.db.system="postgresql"}
查找经过 production
和 staging
实例的追踪
此示例查找经过 production
和 staging
实例的追踪。这是一项方便的请求,用于识别跨生产和非生产环境的配置错误和泄露。
{ resource.deployment.environment = "production" } && { resource.deployment.environment = "staging" }
查找带有数组的追踪
TraceQL 自动查询数组中包含的数据。vParquet4 及更高版本支持数组。
如果 span.foo
是一个数组且包含值 bar
,则此查询将找到它。
{ span.foo = "bar" }
您可以使用正则表达式匹配数组的多个值 {span.http.request.header.Accept=~"application.*"}
,并使用 .*
正则表达式获取数组的所有值。
{span.http.request.header.Accept=~".*"}
使用结构运算符
查找包含 frontend
服务,且该服务或下游服务包含设置了错误的 span 的追踪。
{ resource.service.name="frontend" } >> { status = error }
查找所有以 productcatalogservice
结尾的叶子 span。
{ } !< { resource.service.name = "productcatalogservice" }
查找 productcatalogservice
和 frontend
是否为兄弟。
{ resource.service.name = "productcatalogservice" } ~ { resource.service.name="frontend" }
其他示例
查找 http 状态为 200 的服务,并列出 span 所属的服务名称以及返回的追踪。
{ span.http.status_code = 200 } | select(resource.service.name)
查找任何设置了未指定范围的 deployment.environment
属性为 production
和 http.status_code
属性为 200
的追踪。
{ .deployment.environment = "production" && span.http.status_code = 200 }
查找任何追踪,其中 span 设置了 deployment.environment
资源属性为 production
,并且 span 具有 http.status_code
属性为 200
。在前面的示例中,所有条件都必须在同一个 span 上为 true。这些条件可以在不同的 span 或同一个 span 上为 true。
{ resource.deployment.environment = "production" } && { span.http.status_code = 200 }
查找任何追踪,其中任何 span 设置了 http.method
属性为 GET
和 status
属性为 ok
,并且任何其他 span 设置了 http.method
属性为 DELETE
,但没有设置 status
属性为 ok
。
{ span.http.method = "GET" && status = ok } && { span.http.method = "DELETE" && status != ok }
查找任何具有匹配正则表达式 prod-.*
的 deployment.environment
属性和设置了 http.status_code
属性为 200
的追踪。
{ resource.deployment.environment =~ "prod-.*" && span.http.status_code = 200 }
选择 Span
在 TraceQL 中,花括号 {}
总是从可用的追踪中选择一组 span。花括号通常与条件配对,以减少获取的 span 数量。
TraceQL 区分两种类型的 span 数据:内在属性 (intrinsics),它们是 span 的基础;以及属性 (attributes),它们是可定制的键值对。您可以使用内在属性和属性来构建过滤器和选择 span。
内在字段对于范围来说是基础性的。内在字段是固有存在的,与开发者添加的其他键值对(属性)不同。
内在属性总是使用 <scope>:
表示。请参阅 内在属性表 了解所有当前的内在属性。
内在属性示例
{ span:name = "foo" }
{ event:name = "foo" }
{ trace:id = "1234" }
{ link:traceID = "1234" }
自定义属性以 <scope>.
为前缀,例如 span.
、resource.
、link.
或 event
。资源没有内在值。它只有自定义属性。
属性用点号 (.
) 分隔,而内在字段使用冒号 (:
)。trace
范围只是一个内在属性,在追踪级别没有任何自定义属性。
属性示例
{ span.foo = "bar" }
{ resource.foo = "bar" }
{ link.foo = "bar" }
{ event.foo = "bar" }
内在字段
下表显示了当前可用的范围内在字段
字段 | 类型 | 定义 | 示例 |
---|---|---|---|
span:status | 状态枚举 | 状态:error, ok, 或 unset | { span:status = ok } |
span:statusMessage | string | span 状态的可选文本描述 | { span:statusMessage = "Forbidden" } |
span:duration | 时长 | span 的结束时间 - 开始时间 | { span:duration > 100ms } |
span:name | string | 操作或 span 名称 | { span:name = "HTTP POST" } |
span:kind | 种类枚举 | 种类:server, client, producer, consumer, internal, unspecified | { span:kind = server } |
span:id | string | 使用十六进制字符串表示的 span ID | { span:id = "0000000000000001" } |
trace:duration | 时长 | 追踪中 span 的最大结束时间 - 最小开始时间 | { trace:duration > 100ms } |
trace:rootName | string | 如果存在,追踪中根 span 的名称 | { trace:rootName = "HTTP GET" } |
trace:rootService | string | 如果存在,追踪中根 span 的服务名称 | { trace:rootService = "gateway" } |
trace:id | string | 使用十六进制字符串表示的追踪 ID | { trace:id = "1234567890abcde" } |
event:name | string | 事件名称 | { event:name = "exception" } |
event:timeSinceStart | 时长 | 事件相对于 span 开始时间的时间 | { event:timeSinceStart > 2ms} |
link:spanID | string | 使用十六进制字符串表示的链接 span ID | { link:spanID = "0000000000000001" } |
link:traceID | string | 使用十六进制字符串表示的链接追踪 ID | { link:traceID = "1234567890abcde" } |
instrumentation:name | string | 仪表化范围名称 | { instrumentation:name = "grpc" } |
instrumentation:version | string | 仪表化范围版本 | { instrumentation:version = "1.0.0" } |
追踪级别内在属性 trace:duration
、trace:rootName
和 trace:rootService
对于同一追踪中的所有 span 都是相同的。此外,这些内在属性性能显著更高,因为它们检查的数据比 span 级别内在属性少得多。应尽可能优先使用它们而非 span 级别内在属性。
有时您可能希望按追踪级别内在属性进行搜索。例如,使用 span:name
会查找追踪中 span 的名称。如果您希望按追踪名称 perf
进行搜索,请使用 trace:rootName
进行匹配。
此示例搜索所有名为 service-name
的 Kubernetes 集群,这些集群包含根名称包含 perf
的 span。
{ resource.k8s.cluster.name="service-name" && trace:rootName !~ ".*perf.*"}
属性字段
TraceQL 支持这些不同的属性范围:span 属性、资源属性、事件属性、链接属性和仪表化范围属性。
通过在 Grafana UI 中展开 span,您可以看到其 span 属性(屏幕截图中的 1)和资源属性(屏幕截图中的 2)。
属性字段派生自 span 并且可以定制。进程和 span 属性类型 由属性本身定义,而内在字段具有内置类型。您可以引用 span 或 span 资源的动态属性(也称为标签)。
查询中的属性以 span、resource、event 或 link 范围开头。例如,根据您想要查询的内容,可以使用 span.http
或 resource.namespace
。这提供了显著的性能优势,因为 Tempo 只扫描您感兴趣的数据。
要查找使用 GET HTTP
方法的追踪,您的查询可以如下所示
{ span.http.method = "GET" }
有关属性和资源的更多信息,请参阅 OpenTelemetry Resource SDK。
示例
查找经过 production
环境的追踪
{ resource.deployment.environment = "production" }
查找连接到 Postgres 或 MySQL 数据库的任何数据库连接字符串
{ span.db.system =~ "postgresql|mysql" }
您可以使用 event
范围来查询 span 中发生的事件。span 事件是 span 持续期间的一个独特时间点。虽然 span 有助于构建服务的结构层级,但 span 事件可以提供更深层次的粒度,帮助您更快地调试应用程序并保持最佳性能。要了解如何使用 span 事件,请阅读 什么是 Span 事件? 博文。
您可以在 span 事件中查询异常
{ event.exception.message =~ ".*something went wrong.*" }
如果您已为 span 链接对追踪进行仪表化,则可以使用 link
范围查询链接数据。span 链接将一个 span 与一个或多个具有因果关系的其他 span 相关联。有关 span 链接的更多信息,请参阅 Open Telemetry 项目中的 Span 链接文档。
您可以在链接中搜索属性
{ link.opentracing.ref_type = "child_of" }
仪表化范围允许您查询 仪表化范围 字段,以便您可以根据追踪的仪表化位置和方式进行过滤和探索。此范围的主要用途是根据生成数据的各种库和客户端查询追踪数据。
查找仪表化范围编程语言
{ instrumentation.language = "java" }
查找为给定服务生成仪表化的库
{ resource.service.name = "foo" } | rate() by (instrumentation:name)
Tempo 2.7 发布视频 从 30 秒开始演示和解释了 instrumentation
范围。
无范围属性字段
如果您不确定请求的属性是否存在于 span 或资源上,则属性可以没有范围。如果可能,请使用范围属性而不是无范围属性。范围属性提供更快的查询结果。
例如,查找设置为 critical
的 sla
属性的追踪:
{ .sla = "critical" }
带引号的属性名称
属性名称可能包含终端字符,例如点号 (.
)。要搜索包含终端字符的 span 属性,可以使用带引号的属性语法。将带引号的属性用双引号括起来,例如,"示例一"
。双引号之间的所有字符都被视为属性名称的一部分。
示例
要查找属性名称为 attribute name with space
的 span,请使用以下查询:
{ ."attribute name with space" = "value" }
您可以将带引号的属性语法与非带引号的属性语法一起使用,以下是有效的 TraceQL 查询:
{ span.attribute."attribute name with space" = "value" }
注意
目前,仅支持
\"
和\\
转义序列。
比较运算符
比较运算符用于测试表达式中的值。
已实现的比较运算符包括:
=
(等于)!=
(不等于)>
(大于)>=
(大于或等于)<
(小于)<=
(小于或等于)=~
(正则表达式匹配)!~
(正则表达式不匹配)
TraceQL 使用 Golang 正则表达式。像 https://regex101.com/ 这样的在线正则表达式测试网站方便用于验证 TraceQL 查询中使用的正则表达式。所有正则表达式都被视为完全锚定的。
正则表达式在两端都被锚定。这种锚定使查询更快,并与 PromQL 的行为相匹配,PromQL 中的正则表达式也是完全锚定的。
未锚定的查询,例如:{ span.foo =~ “bar” } 现在被视为:{ span.foo =~ “^bar$” }。
如果您在 Grafana 仪表盘中使用带有正则表达式的 TraceQL,并且希望获得未锚定的行为,请更新查询以使用未锚定版本,例如 { span.foo =~ “.bar.”}。
例如,查找 span 中 http.status_code
属性大于 400
但小于等于 500
的所有追踪:
{ span.http.status_code >= 400 && span.http.status_code < 500 }
这对于字符串类型的 http.status_code
值也同样适用,使用词典排序:
{ span.http.status_code >= "400" }
查找 http.method
属性为 GET
或 DELETE
的所有追踪:
{ span.http.method =~ "DELETE|GET" }
查找 any_attribute
不为 nil
或 span 中存在 any_attribute
的所有追踪:
{ .any_attribute != nil }
字段表达式
字段还可以以各种方式组合,以允许更灵活的搜索条件。字段表达式是多个字段的组合,它们定义了必须匹配的所有条件才能返回结果。
示例
查找具有 success
http.status_code
代码的追踪
{ span.http.status_code >= 200 && span.http.status_code < 300 }
查找使用了 DELETE
HTTP 方法且内在 span 状态不是 OK 的追踪:
{ span.http.method = "DELETE" && status != ok }
两个表达式都要求在同一个 span 上所有条件都为 true。一对 {}
内的整个表达式必须在单个 span 上计算为 true,才能将其包含在结果集中。
在上面的示例中,如果一个 span 包含设置为 DELETE
的 .http.method
属性,并且该 span 也包含设置为 ok
的 status
属性,则该追踪将不包含在返回结果中。
组合 Spansets
Spanset 运算符允许您从追踪中选择不同的 span 集,然后在它们之间进行判断。
逻辑
这些 spanset 运算符在 span 集之间执行逻辑检查。
{condA} && {condB}
- 与运算符 (&&
) 检查两个条件是否都找到了匹配项。{condA} || {condB}
- 或运算符 (||
) 检查任一条件是否找到了匹配项。
例如,查找经过两个特定 cloud.region
的追踪:
{ resource.cloud.region = "us-east-1" } && { resource.cloud.region = "us-west-1" }
注意上一个示例与此示例之间的区别:
{ resource.cloud.region = "us-east-1" && resource.cloud.region = "us-west-1" }
第二个表达式不返回任何追踪,因为单个 span 不可能同时将 resource.cloud.region
属性设置为两个区域值。
结构
这些 spanset 运算符查看追踪的结构以及 span 之间的关系。结构运算符总是返回运算符右侧的匹配项。
{condA} >> {condB}
- 后代运算符 (>>
) 查找与{condA}
匹配的 span 的后代 span 中与{condB}
匹配的 span。{condA} << {condB}
- 祖先运算符 (<<
) 查找与{condA}
匹配的 span 的祖先 span 中与{condB}
匹配的 span。{condA} > {condB}
- 子运算符 (>
) 查找与{condA}
匹配的父 span 的直接子 span 中与{condB}
匹配的 span。{condA} < {condB}
- 父运算符 (<
) 查找与{condA}
匹配的子 span 的直接父 span 中与{condB}
匹配的 span。{condA} ~ {condB}
- 兄弟运算符 (~
) 查看与{condB}
匹配的 span,这些 span 至少有一个与{condA}
匹配的兄弟。
例如,查找特定 HTTP API 与特定数据库交互的追踪:
{ span.http.url = "/path/of/api" } >> { span.db.name = "db-shard-001" }
联合结构
这些 spanset 运算符查看追踪的结构以及 span 之间的关系。这些运算符的独特之处在于它们返回与运算符两侧都匹配的 span。
{condA} &>> {condB}
- 后代运算符 (>>
) 查找与{condA}
匹配的 span 的后代 span 中与{condB}
匹配的 span。{condA} &<< {condB}
- 祖先运算符 (<<
) 查找与{condA}
匹配的 span 的祖先 span 中与{condB}
匹配的 span。{condA} &> {condB}
- 子运算符 (>
) 查找与{condA}
匹配的父 span 的直接子 span 中与{condB}
匹配的 span。{condA} &< {condB}
- 父运算符 (<
) 查找与{condA}
匹配的子 span 的直接父 span 中与{condB}
匹配的 span。{condA} &~ {condB}
- 兄弟运算符 (~
) 查看与{condB}
匹配的 span,这些 span 至少有一个与{condA}
匹配的兄弟。
例如,在一个查询中获取失败的端点 AND 所有后代失败的 span:
{ span.http.url = "/path/of/api" && status = error } &>> { status = error }
实验性结构
这些 spanset 运算符查看追踪的结构以及 span 之间的关系。这些运算符被标记为实验性,因为有时会返回误报。但是,这些运算符可能非常有用(请参阅以下示例)。
{condA} !>> {condB}
- 非后代运算符 (!>>
) 查找与{condA}
匹配的父 span 的非后代 span 中与{condB}
匹配的 span。{condA} !<< {condB}
- 非祖先运算符 (!<<
) 查找与{condA}
匹配的子 span 的非祖先 span 中与{condB}
匹配的 span。{condA} !> {condB}
- 非子运算符 (!>
) 查找与{condA}
匹配的父 span 的非直接子 span 中与{condB}
匹配的 span。{condA} !< {condB}
- 非父运算符 (!<
) 查找与{condA}
匹配的子 span 的非直接父 span 中与{condB}
匹配的 span。{condA} !~ {condB}
- 非兄弟运算符 (!~
) 查看与{condB}
匹配的 span,这些 span 没有至少一个与{condA}
匹配的兄弟。
阅读 Tempo 2.3 博客文章 获取更多示例和详细信息。
例如,查找服务“foo”中包含叶子 span 的追踪:
{ } !< { resource.service.name = "foo" }
查找一系列级联错误中最后一个错误的 span:
{ status = error } !< { status = error }
聚合器
到目前为止,所有示例查询表达式都涉及单个 span。您可以使用聚合函数来询问有关一组 span 的问题。目前包括:
count
- spanset 中 span 的数量。avg
- spanset 中给定数字属性或内在属性的平均值。max
- spanset 中给定数字属性或内在属性的最大值。min
- spanset 中给定数字属性或内在属性的最小值。sum
- spanset 中给定数字属性或内在属性的总和。
聚合函数允许您对匹配的结果执行操作,以进一步优化返回的追踪。有关未来计划工作的更多信息,请参阅TraceQL 工作原理。
例如,查找总 span 数量大于 10
的追踪:
count() > 10
查找追踪中 span 的平均时长大于 20ms
的追踪:
avg(duration) > 20ms
例如,查找具有 3 个以上 http.status_code
属性值为 200
的 span 的追踪:
{ span.http.status_code = 200 } | count() > 3
查找制造的属性 bytesProcessed
总计超过 1 GB 的 span:
{ } | sum(span.bytesProcessed) > 1000000000
分组
TraceQL 支持分组管道运算符,可用于按任意属性进行分组。这对于查找具有多个错误的服务非常有用:
{ status = error } | by(resource.service.name) | count() > 1
算术运算
TraceQL 支持在您的查询中进行任意算术运算。这对于使查询更易于理解很有用:
{ span.http.request_content_length > 10 * 1024 * 1024 }
或您想到的任何其他内容。
选择
TraceQL 可以从 span 中选择任意字段。这种性能尤其高,因为只有在满足所有其他条件后才检索选定的字段。
{ status=error } | select(span.http.status_code, span.http.url)
实验性 TraceQL 指标
TraceQL 指标是实验性的,但很容易上手。有关更多信息,请参阅TraceQL 指标文档。
您还可以使用 TraceQL 指标查询。有关详细信息,请参阅TraceQL 指标查询。