Skip to content

Searching traces

fetch_spans is the trace list: one row per trace — or, with include_children, per span — of one or more runs, or of a project’s own inference traces.

from metrana.query import AttrFilter, SpanFilter, SpanOrderBy, TraceSpanKind
traces = client.fetch_spans(
runs="grpo-7b-v2",
filter=(SpanFilter.step >= 1000) & (AttrFilter("reward") >= 0.5),
order_by=SpanOrderBy.step.asc(),
attributes=["reward", "rollout"],
max_spans=500,
)
run trace_id span_id parent_span_id ordinal kind op_name step ... token_count span_count iteration_count reward rollout
0 grpo-7b-v2 AAAA... 0 0 0 sub_agent rollout 1000 ... 812 6 2 0.73 A
Argument Meaning
runs One run or up to 10; empty for the project’s own (inference) traces.
include_children False (default) returns roots only, the trace list. True returns every matching span, and the filter must then name the operations with SpanFilter.op_name == ... or .is_in([...]) as a top-level & term.
filter A SpanFilter condition on the span’s own fields, an AttrFilter on its logged attributes, or both combined with & and |.
order_by SpanOrderBy.<field>.asc() / .desc(), or AttrOrderBy over a logged attribute. Default: step descending, newest trace first. Across several runs only a SpanOrderBy is allowed.
attributes Logged attributes to return as columns: names, or an AttributeFilter resolved against list_tracing_attribute_definitions.
max_spans, skip Cap and offset. Across several runs the first 1,000 matches are reachable.

run (None for a project trace), trace_id, span_id, parent_span_id, ordinal, kind, op_name, step, start_time, end_time, duration_ms, status, token_count, span_count, iteration_count, then one column per requested attribute. kind and status hold the enum values ("llm_call", "ok"), which compare equal to TraceSpanKind and TraceStatus members. frame.attrs["total_count"] carries the full match size. trace_id and run are what fetch_trace_spans and fetch_span_payloads take.

SpanFilter exposes the span’s own fields; AttrFilter("name") the attributes you logged.

Field Type Notes
SpanFilter.op_name string The trace’s name on a root, the span’s name elsewhere. ==, .is_in, .contains, .matches(regex).
SpanFilter.step integer The policy step. Stored with op_name as the key, so narrowing on them is cheap.
SpanFilter.status TraceStatus RUNNING, OK, ERROR, TRUNCATED, CANCELLED.
SpanFilter.kind TraceSpanKind THOUGHT, ITERATION, LLM_CALL, TOOL_CALL, SUB_AGENT, ARTIFACT, FUNCTION, SESSION_SUMMARY.
SpanFilter.start_time, .end_time timestamp .end_time.exists() selects closed spans.
SpanFilter.duration duration Milliseconds, or a timedelta.
SpanFilter.token_count, .span_count, .iteration_count integer The last two are set on roots only.

Examples:

SpanFilter.status == "ERROR" # failed rollouts
(SpanFilter.kind == TraceSpanKind.SUB_AGENT) & (AttrFilter("reward") < 0.2)
SpanFilter.iteration_count >= 5 # rollouts that looped
(SpanFilter.op_name == "policy") & (AttrFilter("model") == "claude-y") # with include_children=True
AttrFilter("route").exists()
client.list_tracing_op_names(run="grpo-7b-v2", include_children=True) # [TracingOpName(op_name, kind), ...]
client.list_tracing_attribute_definitions(run="grpo-7b-v2") # [AttributeDefinition(path, kind), ...]

The first lists every operation name logged, with its span kind — the values SpanFilter.op_name takes. The second lists the attribute names logged on spans, which AttrFilter and attributes= take. Both take run=None for the project’s own traces.

fetch_spans returns one row per span, so a value shared by many spans comes back as many times. To list each value once, ask for the attribute’s distinct values over the same search:

from metrana.query.tracing_well_known_attrs import (
SESSION_AGENT_ATTRIBUTE,
SESSION_ID_ATTRIBUTE,
SESSION_REWARD_ATTRIBUTE,
SESSION_SUMMARY_OP_NAME,
)
# Every session the support agent served, each id once.
client.list_tracing_attribute_values(
attribute_name=SESSION_ID_ATTRIBUTE,
filter=(SpanFilter.op_name == SESSION_SUMMARY_OP_NAME) & (AttrFilter(SESSION_AGENT_ATTRIBUTE) == "support-bot"),
)
# Sessions scored above a threshold.
client.list_tracing_attribute_values(
attribute_name=SESSION_ID_ATTRIBUTE,
filter=(SpanFilter.op_name == SESSION_SUMMARY_OP_NAME) & (AttrFilter(SESSION_REWARD_ATTRIBUTE) > 0.8),
)

runs, include_children and filter mean what they mean for fetch_spans: the spans searched are the ones that search would return, and of those, the ones that logged attribute_name contribute their value. Values come back as Python values of the attribute’s logged type, ascending; max_results caps how many are fetched. An attribute never logged under the run or project yields an empty list. Across several runs only the first 1000 distinct values are reachable, as for fetch_spans: more than that raises QueryValueError unless max_results asks for fewer, so narrow the filter rather than page deeply.