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 rollout0 grpo-7b-v2 AAAA... 0 0 0 sub_agent rollout 1000 ... 812 6 2 0.73 AArguments
Section titled “Arguments”| 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. |
Columns
Section titled “Columns”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.
Span fields
Section titled “Span fields”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=TrueAttrFilter("route").exists()Discovery
Section titled “Discovery”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.
Distinct attribute values
Section titled “Distinct attribute values”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.