🎯Exercises
1. Basic Queries
1.1 Basic Syntax
Base syntax:
{}- returns all traces.{resource.service.name = "checkout"}- filtering by resource attribute{duration > 10ms}- filters spans lasting longer than 10ms. Note:{ trace:duration > 10ms }filters by the duration of the entire trace, not a single span. Full list is here{ duration > 10ms && .http.method = "GET" && .http.status_code = 200 }- conditions can also be combined. Where attributes starting with.are implicit references to bothspanandresource.
🎯 What does this query do:
{ resource.service.name = "cart" && (duration > 10ms || .http.status_code >= 500 ) }
⚠️ Tips and tricks:
- Analyze the results and what they have in common.
- Think about how Tempo is designed and how search works.
1.2 Data Types
They are compatible with OpenTelemetry types, meaning we have:
- String: - text, case-sensitive.
- Numeric: - supports basic comparisons: =, !=, >, >=, etc.
- Duration: - supports convenient notation like: “10ms” or “15.5s,” and comparisons: =, <, <=, etc.
- Bool: - supports comparisons like true and false
- Status: - allows comparison to built-in keywords: ok, error, and unset
❗Note❗
Since Tempo has no data schema and has no type casting:
{ .userID = 1000 }- will return results only for ints{ .userID = "1000" }- will return results only for strings
1.3 Operators
Available:
=(equality)!=(inequality)>(greater than)>=(greater than or equal to)<(less than)<=(less than or equal to)=~(regular expression)!~(negated regular expression)
1.4 Scopes
In TraceQL we have two important objects:
resource- describes the entity producing telemetry (service, host, process, container), e.g.,resource.service.namespan- properties of the operation itself.
Searching by them can be done as follows:
span.xxx.yyy- specific spanresource.xxx.yyy- resource properties.xxx.yyy- searches everything (span and resource)
Knowing that Tempo works on columns, which operation will be more efficient:
{ resource.service.name = "cart" && span.url.path = "/oteldemo.CartService/GetCart" }{ .service.name = "cart" && .url.path = "/oteldemo.CartService/GetCart" }?
1.5 Multi-Span Queries
It is possible to define the following relationships:
-
{ resource.service.name = "cart" } && { resource.service.name="frontend" }- finds traces that contain spans fromcartandfrontend. Without specifying the relationship between them. -
{ resource.service.name = "cart" } || { resource.service.name="frontend" }- finds traces that contain spans fromcartorfrontend. Without specifying the relationship between them.
Structural Operators
Structural operators allow filtering by parent-child relationships in the trace tree. They always return spans from the right-hand side of the operator (RHS).
| Operator | Name | Description |
|---|---|---|
> |
Child | RHS is a direct child of LHS |
>> |
Descendant | RHS is a descendant of LHS (not necessarily direct) |
< |
Parent | RHS is a direct parent of LHS |
<< |
Ancestor | RHS is an ancestor of LHS (not necessarily direct) |
~ |
Sibling | RHS and LHS have the same direct parent |
Examples:
-
{ resource.service.name="frontend" } > { resource.service.name = "checkout" }-checkoutis a direct child offrontend. -
{ resource.service.name="frontend" } >> { resource.service.name = "checkout" }-checkoutis a descendant offrontend, but not necessarily a direct child. -
{ resource.service.name="checkout" } ~ { resource.service.name="payment" }-checkoutandpaymenthave the same direct parent. -
{ resource.service.name="checkout" } < { resource.service.name="frontend" }-frontendis a direct parent ofcheckout.
Union Operators (structural with union)
Work the same as regular structural operators, but return spans from both sides of the operator (LHS and RHS). Useful when you want to see both parent and child in results.
| Operator | Name | Description |
|---|---|---|
&> |
Union Child | Like >, but returns both spans |
&>> |
Union Descendant | Like >>, but returns both spans |
&< |
Union Parent | Like <, but returns both spans |
&<< |
Union Ancestor | Like <<, but returns both spans |
&~ |
Union Sibling | Like ~, but returns both spans |
Example:
{ span.http.url = "/api/checkout" && status = error } &>> { status = error }- finds both the span with endpoint/api/checkoutand all its descendant spans with errors.
Negations (experimental)
Invert the structural condition. Marked as experimental — may return false positives.
| Operator | Name | Description |
|---|---|---|
!> |
Not-Child | RHS is not a direct child of LHS |
!>> |
Not-Descendant | RHS is not a descendant of LHS |
!< |
Not-Parent | RHS is not a direct parent of LHS |
!<< |
Not-Ancestor | RHS is not an ancestor of LHS |
!~ |
Not-Sibling | RHS and LHS do not have the same parent |
Example:
{ } !< { resource.service.name = "foo" }- finds leaf spans (without children) in servicefoo.
1.6 Pipelines
🎯 What does this query do?
{ resource.service.name = "frontend-proxy" } | count() > 2 | avg(duration) > 10ms
⚠️ Tips and tricks:
- Run the query.
- Remember how Tempo works and how it analyzes search.
- Remove and/or modify the query and analyze what changed.
Tempo has various aggregate functions
2. Exercises
2.1 Find traces that contained an HTTP error
🎯 Goal: Find traces that anywhere contain a span with an HTTP request that ended with a status code in the 500 family.
2.2 Spans with database queries
🎯 Goal: Find spans that contain PostgreSQL database query content.
⚠️ Tips and tricks:
- Query content is in span attributes. Attribute
db.xxxx
2.3 More than one database query
🎯 Goal: Find traces that contain more than one call to the Redis database.
2.4 Filtering by duration and status
🎯 Goal:
Find traces from the payment service that lasted longer than 0.1ms and ended with an error.
⚠️ Tips and tricks:
- Use the intrinsic field
statuswith values:ok,error,unset - Combine the
durationcondition with thestatuscondition - The
paymentservice has thepaymentFailurefeature flag enabled (~10% of transactions end with an error)
2.5 Parent-child relationship (>>)
🎯 Goal:
Find traces where frontend calls (directly or indirectly) the payment service and that payment call ends with an error.
⚠️ Tips and tricks:
- The
>>operator means a parent-child relationship (not necessarily direct) - First spanset is the parent, second is the child
- Place the
statuscondition in the child spanset
2.6 Sibling relationship (~)
🎯 Goal:
Find traces where the checkout service within a single operation calls both payment and email (as siblings — both calls are children of the same PlaceOrder span).
⚠️ Tips and tricks:
- The
~operator means both spans have the same parent - Think about the trace topology:
checkout/PlaceOrderis the parent, and its children include calls topaymentandemail - Both spans in the query should be from the
checkoutservice — because checkout creates the spans for calls to payment and email
2.7 Pipeline — average duration and grouping
🎯 Goal: Find services where the average span duration exceeds 20ms. Group results by service name.
⚠️ Tips and tricks:
- Use
avg(duration)in the pipeline - For grouping use
by(resource.service.name) - Syntax:
{ CONDITIONS } | AGGREGATION | by(ATTRIBUTE) - Check the aggregator documentation
2.8 select() — displaying selected attributes
🎯 Goal:
Find traces from the product-catalog service that contain an error. Display the service name, status, and span duration in the results.
⚠️ Tips and tricks:
select()allows choosing which attributes are visible in results- Syntax:
{ CONDITIONS } | select(ATTRIBUTE1, ATTRIBUTE2, ...) - You can use both intrinsic fields (
duration,status) and attributes (resource.service.name) - The
product-catalogservice has theproductCatalogFailurefeature flag which generates errors