intermediate
Spans
Represent timed units of work with names, attributes, status, parent-child relationships, and useful boundaries.
A span is a timed unit of work with a name, start/end timestamps, attributes, status, and optional parent-child links. Good span boundaries match operations engineers care about: HTTP handler, DB query, cache fetch, external API call.
await tracer.startActiveSpan('stripe.charge', async (span) => {
span.setAttribute('payment.provider', 'stripe');
try {
await stripe.charges.create(payload);
span.setStatus({ code: SpanStatusCode.OK });
} catch (err) {
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR });
throw err;
}
});
| Attribute | Example | |-----------|---------| | http.route | `/checkout` | | db.system | `postgresql` | | messaging.destination | `receipts` |
Avoid high-cardinality attributes (raw SQL with literals, full URLs with IDs). Use semantic conventions (OpenTelemetry) for interoperability.
On interviews: span naming, parent/child vs links for fan-out, and marking internal vs client spans.
Common pitfalls: one giant span per request hiding DB time; missing error status on failed spans; attributes that explode cardinality in the backend.
The trade-off is diagnostic detail versus trace size and indexing limits.
Checklist:
- Name spans after operations, not classes.
- Set error status and record exceptions.
- Keep attributes low-cardinality.
- Nest spans to show critical path.