Skip to main content

TraceQL Span Queries

A span query selects traces by matching conditions against their spans. The simplest query, { }, matches every span. Everything else narrows that down.

{ resource.service.name = "frontend" && name = "POST /api/orders" }

Intrinsic fields​

Fields every span, trace, event, or link has — always written <scope>:<field>.

FieldTypeMeaning
span:statusenumerror, ok, or unset
span:statusMessagestringtext accompanying the status
span:durationdurationend − start of the span
span:namestringoperation name
span:kindenumserver, client, producer, consumer, internal, unspecified
span:id / span:parentIDstringhex span / parent ID
span:childCountintegernumber of direct children
trace:durationdurationmax(end) − min(start) across the trace
trace:rootName / trace:rootServicestringname / service of the root span
trace:idstringhex trace ID
event:namestringspan event name
event:timeSinceStartdurationevent time relative to span start
link:spanID / link:traceIDstringlinked span/trace ID
instrumentation:name / instrumentation:versionstringinstrumentation scope
{ span:duration > 100ms }
{ trace:rootService = "gateway" }
{ span:childCount = 0 } # leaf spans

Trace-level intrinsics (trace:duration, trace:rootName, trace:rootService) are the same for every span in a trace and cheaper to evaluate than an equivalent span-level condition — prefer them.


Attribute fields​

Custom key/value data, written <scope>.<key> — span., resource., event., link., or instrumentation.. resource and trace have no intrinsics of their own beyond what's listed above; everything else on them is an attribute.

{ span.http.method = "GET" }
{ resource.deployment.environment = "production" }
{ event.exception.message =~ ".*timeout.*" }
{ link.opentracing.ref_type = "child_of" }
{ instrumentation.name = "grpc" }

An attribute name with terminal characters (a period, a space) needs quoting:

{ span."attribute name with space" = "value" }
{ span.attribute."nested name" = "value" }

Only the \" and \\ escape sequences are supported inside a quoted name.

Arrays​

If an attribute holds an array, = / =~ match when any element satisfies the condition; != / !~ match only when no element does.

{ span.foo = "bar" } # true if any element of the array equals "bar"
{ span.foo != "bar" } # true only if no element equals "bar"

Field expressions​

All conditions inside one {} must hold on the same span.

{ span.http.status_code >= 200 && span.http.status_code < 300 }
{ span.http.method = "DELETE" && status != ok }

Combining spansets​

Logical (&&, ||)​

Separate {} blocks can match on different spans within the same trace.

{ resource.cloud.region = "us-east-1" } && { resource.cloud.region = "us-west-1" }
{ resource.cloud.region = "us-east-1" } || { resource.cloud.region = "us-west-1" }

Contrast with putting both conditions in one {} — that requires a single span to match both, which is impossible here since a span has one region.

=~ with an alternation is usually more efficient than ||:

{ resource.cloud.region =~ "us-east-1|us-west-1" }

Structural​

These look at the trace's parent/child/sibling structure. They always return matches from the right-hand side.

OperatorMatches spans on the right that are…
A >> Bdescendants of a span matching A
A << Bancestors of a span matching A
A > Bdirect children of a span matching A
A < Bdirect parents of a span matching A
A ~ Bsiblings of a span matching A
{ span.http.url = "/checkout" } >> { span.db.name = "orders-db" }

Union structural (&>> &<< &> &< &~) return matches from both sides — use them to pull the triggering span and its matching relatives in one result:

{ span.http.url = "/checkout" && status = error } &>> { status = error }

Experimental not-structural operators (!>> !<< !> !< !~) can produce false positives but are useful for finding leaf/terminal spans:

{ } !< { resource.service.name = "checkout" } # leaf spans in checkout
{ status = error } !< { status = error } # last error in a cascade

Aggregate filters​

Run inside the pipeline of a single query to filter on properties of the whole matched spanset — distinct from metrics queries, which produce a time series.

FunctionReturns
count()number of matching spans
avg(field) / min(field) / max(field) / sum(field)aggregate of a numeric attribute or intrinsic
{ span.http.status_code = 200 } | count() > 3
avg(duration) > 20ms
{ } | sum(span.bytesProcessed) > 1000000000

Grouping​

{ status = error } | by(resource.service.name) | count() > 1

Arithmetic​

{ span.http.request_content_length > 10 * 1024 * 1024 }

select()​

Pull specific fields onto the result without them being part of the match condition — cheap, since selected fields aren't fetched until every other condition is already satisfied.

{ status = error } | select(span.http.status_code, span.http.url)

most_recent hint (experimental)​

By default the query engine returns the first N matches it finds, which may not be the newest. with (most_recent=true) forces a deeper search that returns the freshest traces first — useful when triaging a live incident.

{ } with (most_recent=true)
{ span.foo = "bar" } >> { status = error } with (most_recent=true)

This makes the search non-deterministic (repeating it can return a different list) and costs more, so reach for it only when recency matters more than speed.