1. Introduction
Before talking about parent and child spans, we must first clearly understand what a span is in OpenTelemetry.
A span represents a single unit of work in your system. That unit of work could be:
- Handling one HTTP request in a controller
- Calling a database
- Calling another microservice
- Publishing or consuming a Kafka message
- Doing a computation inside your code
A trace is simply a collection of spans that all belong to the same request or transaction.
So, you should always think like this: A trace is a tree of spans, not a flat list. And that brings us naturally to parent–child relationships.
2. Why Do We Even Need Parent–Child Relationships?
Let us take a very realistic example from your kind of systems:
Imagine a request comes to your application:
/placeholder
Inside your backend, the following things happen:
- Controller receives the request
- Service validates the order
- Service calls Payment Service
- Service calls Inventory Service
- Service writes to database
- Service publishes Kafka event
Now, conceptually, all of this is one logical operation: Place Order.
But technically, it is made of many smaller operations.
So OpenTelemetry models this as:
- One root span = “Handle /placeOrder request”
- Many child spans = “Validate order”, “Call payment service”, “DB insert”, “Publish Kafka message”, etc.
This gives us a hierarchical structure, not a flat log.
3. The Tree Structure of a Trace
A trace always looks like a tree. Let us draw a textual diagram:
Trace: PlaceOrder (TraceId = abc123)
└── Span A: HTTP POST /placeOrder (Root Span)
├── Span B: Validate Order
├── Span C: Call Payment Service
│ └── Span D: HTTP POST /pay
├── Span E: Call Inventory Service
│ └── Span F: HTTP POST /reserve
├── Span G: Insert Order into DB
└── Span H: Publish Kafka Event
Important things to notice:
- Span A is the root span (it has no parent).
- Every other span has exactly one parent.
- A parent span can have many children.
- This forms a tree, not a graph and not a cycle.
4. What Exactly Is a Parent Span?
A parent span is simply:
The span that represents the operation that caused another operation to happen.
For example:
- The HTTP request handler span causes the DB call → so it is the parent of the DB span.
- The service method span causes the REST call to another service → so it is the parent of that HTTP client span.
In simple words:
If operation B happened because of operation A, then span A is the parent of span B.
5. What Exactly Is a Child Span?
A child span is:
A span that represents a sub-operation inside a bigger operation.
For example:
- “Insert into DB” is a child of “Process Order”.
- “Call Payment Service” is a child of “Process Order”.
The child span always:
- Shares the same Trace ID as the parent.
- Has its Parent Span ID set to the parent’s Span ID.
6. The Two IDs That Make This Work
Every span has:
- Trace ID → identifies the whole trace (the whole request journey)
- Span ID → identifies this particular span
- Parent Span ID → identifies who its parent is
So internally it looks like this:
Span A: TraceId = abc123 SpanId = 111 Parent = null Span B: TraceId = abc123 SpanId = 222 Parent = 111 Span C: TraceId = abc123 SpanId = 333 Parent = 111 Span D: TraceId = abc123 SpanId = 444 Parent = 333
This is how tools like Tempo, Jaeger, Zipkin, Dynatrace, etc. reconstruct the tree view.
7. Nested Spans: Depth Is Unlimited
Spans can be nested to any depth.
Example:
HTTP Request
└── Service Method
└── Business Rule Check
└── DB Query
└── JDBC Driver Call
This means:
A span can be both a child and a parent at the same time.
