Learn
Spans

Spans

Spans are the underlying primitive Traceway stores. Everything else (Endpoints, Tasks, AI Traces, Issues) is a materialized view derived from the spans you ingest. The shape is OpenTelemetry-native, so your existing OTel instrumentation works without translation.

What a span captures

FieldDescription
trace_idThe OTel trace id the span arrived with, 32 hex characters
span_id16 hex characters for OTLP input; native and migrated UUID IDs can be 32 in the Traceway API
parent_span_idThe span's direct parent within the trace, empty for a root span
nameDescriptive label (db.users.find, POST https://api.stripe.com/v1/charges, etc.)
kindOne of OTel's SERVER, CLIENT, INTERNAL, PRODUCER, CONSUMER
start_time / durationWhen it started and how long it ran
attributesArbitrary key-value metadata (HTTP, DB, AI, custom)
eventsTimestamped events on the span; "exception" events become Issues

A trace is a trace, a span is a span

Traceway stores accepted spans after sampling, permission and healthcheck filtering, with the trace id, span id and parent span id it arrived with. Nothing is renamed or re-rooted at ingestion.

An endpoint, a task and an AI trace are each a copy of one span, the one that started the request, the job or the LLM call. The copy carries the same three ids as its span. That is all the pages need:

  • The waterfall of an endpoint is the spans of its trace that sit below its span.
  • Everything connected to it is whatever else shares its trace id, in any project you can open.
  • An exception records the trace and the span it happened on. The endpoint or task it belongs to is the nearest one above that span.

Canonical span storage preserves IDs across export batches. Entity classification can depend on visible ancestry: a nested HTTP server span is suppressed only when a local HTTP server ancestor is present in its resource block. Missing parents are treated as unknown, so separately exported nested server spans can produce extra endpoint projections. A child can join its parent once both are stored and included in the query window and graph limits.

Span reads look 24 hours either side of the endpoint, task or AI trace you opened. A span recorded further away than that is not shown on its page.

What spans are good for

Spans tell you where the time went. If an endpoint takes 500ms and 450ms of that is in a db.users.find span, you know what to optimize. The dashboard's drill-down (Endpoints / Tasks / AI Traces → instance → waterfall) bottoms out in spans.

Common things to wrap in a span:

  • Database queries (auto-instrumented in most stacks)
  • External HTTP calls (http.client auto-instrumentation)
  • Cache operations
  • File I/O
  • Business-logic steps you want to time

Naming

Hierarchical names group well in the dashboard's filters:

db.users.find
db.orders.insert
api.stripe.charge
api.sendgrid.send
cache.redis.get
logic.pricing.calculate

category.resource.action is a useful default. Pick something and stay consistent.

Nesting

In Traceway's storage, span nesting is reconstructed from parent_span_id. The waterfall view renders children indented under their parent in chronological order, exactly the way the OTel spec defines a trace tree.

endpoint POST /api/checkout: 200ms
├── validate.cart           20ms
├── pricing.calculate       80ms
│   └── db.products.batch    40ms
├── stripe.charge           60ms
└── db.orders.insert        30ms

Manual spans

The Traceway SDKs and any standard OTel SDK can create spans manually. See the per-framework client docs for the exact API; the underlying shape is always the same OTel span model described above.