Span Links
Span links record causal relationships between spans that are not parent-child, such as batch fan-out or asynchronous follow-up work.
Definition
A span link is a reference from one span to another span that shares a causal relationship without being its parent. Parent-child edges model nested synchronous calls; links model many-to-many or deferred relationships — a batch consumer whose one span is caused by a hundred producer spans, or a follow-up job triggered later by an earlier request. Links let a trace graph express these connections without forcing a false hierarchy.
Context and scope
Needed whenever work is batched, queued, or resumed, so the causal chain is preserved even though the execution is not nested.
Common misconceptions
- Links replace parent-child — links complement it; the synchronous call structure still uses parent-child edges
- A linked span is part of the same trace — links cross trace boundaries, which is exactly why they are needed
- Links are optional metadata — without them, causal chains through queues break and debugging loses the path from cause to effect
Anti-patterns
- Faking a parent relationship for batched work, so a single consumer span has a misleading single parent
- Dropping the link when a message is re-enqueued, so retried work loses its original cause
- Overusing links to approximate a hierarchy that parent-child already expresses
Related concepts
Related examples
Authoritative source
What it is
In a trace model, spans form a tree through parent-child edges, which fits nested synchronous calls. Real systems also produce non-nested causality: a message consumer processes a batch produced by many requests, or a cron job continues work from earlier asynchronous steps. A span link attaches a reference from such a span to the related spans, possibly across traces, without inventing a fake tree.
Why it matters
When causality is forced into a tree that does not fit, traces become misleading: the batch consumer looks like it belongs to one arbitrary producer, and the other ninety-nine are invisible. Links preserve the true many-to-many relationships, so a trace search can answer “what caused this job?” or “what did this request eventually trigger?” across asynchronous boundaries.
How to use it well
Add links where execution is batched, deferred, or retried rather than nested, and carry identifiers — message ids, trace ids — on the link so it survives transport. Keep parent-child edges for direct synchronous calls so the tree stays readable. Instrument queue producers and consumers together, and make sure re-enqueue flows re-attach the original link rather than starting a fresh, causally orphaned trace.