Spans
BothUnderstanding 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:
| Attribute | Description |
|---|---|
span_id | Unique identifier for this span (16-byte hex) |
trace_id | ID of the parent trace this span belongs to (32-byte hex) |
parent_span_id | ID of the parent span, if this is a nested operation |
name | Human-readable operation name (e.g., "call_openai", "research_agent") |
start_time | Timestamp when the operation started (nanosecond precision) |
end_time | Timestamp when the operation completed |
duration | How long the operation took (calculated from start/end time) |
status | Status code: OK, ERROR, or UNSET |
attributes | Key-value pairs with additional metadata |
events | Timestamped 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
| Attribute | Description |
|---|---|
llm.model | Model name (e.g., "gpt-4", "claude-3-opus") |
llm.provider | Provider (e.g., "openai", "anthropic") |
llm.usage.prompt_tokens | Number of tokens in the prompt |
llm.usage.completion_tokens | Number of tokens in the completion |
llm.usage.total_tokens | Total tokens used |
llm.usage.source | Where token counts came from (provider_usage, tiktoken, mixed) |
llm.cost.usd | Estimated cost in USD |
llm.pricing.source | Pricing snapshot source used for cost calculation |
llm.temperature | Model temperature parameter |
llm.max_tokens | Max tokens parameter |
Agent Metadata
| Attribute | Description |
|---|---|
agent.id | Agent identifier |
session.id | Session identifier |
user.id | User identifier |
tenant.id | Tenant/organization identifier |
project.id | Project identifier |
HTTP Spans
| Attribute | Description |
|---|---|
http.method | HTTP method (GET, POST, etc.) |
http.url | Request URL |
http.status_code | Response status code |
http.target | Request target path |
Attribute truncation
attr_truncation_limit config option.Common Span Types
Traccia automatically creates different types of spans based on what's being traced:
Creating Custom Spans
You can manually create spans for operations not automatically traced:
from traccia import span
# Using the span context managerwith 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 controlfrom traccia import get_tracerfrom 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)) raiseLong-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.
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:
Depth limits
max_span_depth configuration option.Next Steps
© 2026 Traccia.