Spans

Both

Understanding spans in distributed tracing.

A span represents a single unit of work within a trace. It captures the start time, duration, and metadata of an operation—whether that's an LLM call, a tool invocation, or a custom function.

Core Span Attributes

Every span in Traccia contains these standard OpenTelemetry fields:

AttributeDescription
span_idUnique identifier for this span (16-byte hex)
trace_idID of the parent trace this span belongs to (32-byte hex)
parent_span_idID of the parent span, if this is a nested operation
nameHuman-readable operation name (e.g., "call_openai", "research_agent")
start_timeTimestamp when the operation started (nanosecond precision)
end_timeTimestamp when the operation completed
durationHow long the operation took (calculated from start/end time)
statusStatus code: OK, ERROR, or UNSET
attributesKey-value pairs with additional metadata
eventsTimestamped events that occurred during the span (e.g., exceptions, log messages)

Traccia-Specific Span Attributes

Traccia enriches spans with AI-specific metadata through span processors:

LLM Spans

AttributeDescription
llm.modelModel name (e.g., "gpt-4", "claude-3-opus")
llm.providerProvider (e.g., "openai", "anthropic")
llm.usage.prompt_tokensNumber of tokens in the prompt
llm.usage.completion_tokensNumber of tokens in the completion
llm.usage.total_tokensTotal tokens used
llm.usage.sourceWhere token counts came from (provider_usage, tiktoken, mixed)
llm.cost.usdEstimated cost in USD
llm.pricing.sourcePricing snapshot source used for cost calculation
llm.temperatureModel temperature parameter
llm.max_tokensMax tokens parameter

Agent Metadata

AttributeDescription
agent.idAgent identifier
session.idSession identifier
user.idUser identifier
tenant.idTenant/organization identifier
project.idProject identifier

HTTP Spans

AttributeDescription
http.methodHTTP method (GET, POST, etc.)
http.urlRequest URL
http.status_codeResponse status code
http.targetRequest target path

Attribute truncation

By default, attribute values longer than 1000 characters are truncated to prevent oversized spans. You can adjust this limit with the attr_truncation_limit config option.

Common Span Types

Traccia automatically creates different types of spans based on what's being traced:

Agent/Function SpansCreated by @observe() decorator on your functions
LLM SpansAuto-instrumented for OpenAI and Anthropic API calls
HTTP Client SpansAuto-instrumented for requests library and HTTP calls
HTTP Server SpansAuto-instrumented for FastAPI endpoints
Tool SpansAuto-instrumented for tool/function calls when enabled
Custom SpansCreated manually with tracer.start_span() or span() context manager

Creating Custom Spans

You can manually create spans for operations not automatically traced:

example.py
python
from traccia import span
# Using the span context manager
with span("database_query", {"query_type": "select"}) as s:
results = db.execute(query)
s.set_attribute("row_count", len(results))
# Or get a tracer for more control
from traccia import get_tracer
from opentelemetry.trace import StatusCode
tracer = get_tracer("my-service")
with tracer.start_as_current_span("complex_operation") as span:
span.set_attribute("user_id", user_id)
span.add_event("Starting processing")
try:
result = process_data()
span.set_status(StatusCode.OK)
except Exception as e:
span.record_exception(e)
span.set_status(StatusCode.ERROR, str(e))
raise

Long-Lived Spans (Streaming)

Standard context managers and startActiveSpan end the span when the block exits. For streaming responses or multi-callback async flows, use span_scope() / spanScope() and call end() explicitly when the stream finishes.

streaming.py
python
from traccia import span_scope
async def stream_reply(user_msg: str):
turn = span_scope("chat.turn", attributes={"span.type": "llm"})
try:
async for token in turn.run_async(lambda: llm.stream(user_msg)):
yield token
except Exception as e:
turn.end(error=e)
raise
else:
turn.end()

Span Hierarchy and Nesting

Spans automatically form parent-child relationships based on the call stack. Traccia enforces a maximum depth to prevent runaway nesting:

Root Span: agent_workflow(auto-started)
Span: research_task(@observe)
Span: openai.chat.completions(auto-instrumented)
Span: fetch_context(requests, auto-instrumented)
Span: write_report(@observe)
Span: openai.chat.completions(auto-instrumented)

Depth limits

By default, Traccia limits span depth to 10 levels. You can adjust this with themax_span_depth configuration option.

Next Steps

© 2026 Traccia.