Ein Span ist die Grundeinheit eines Distributed Traces: eine benannte, zeitlich erfasste Operation wie ein eingehender HTTP-Request, eine Datenbankabfrage oder das Senden einer Nachricht. Jeder Span enthält Trace-ID, eigene Span-ID, Parent-Span-ID, Start- und Endzeit, Kind, Attribute, Events, Links und Status. Zusammen zeigen sie, wo Zeit verbraucht wurde und wo Fehler auftraten.
Was ist ein Span?
Ein Span steht für eine einzelne Arbeitseinheit innerhalb eines Requests: ein Service verarbeitet GET /orders/{id}, ein ausgehender Aufruf an eine Payment-API, eine SQL-Abfrage, eine aus Kafka konsumierte Nachricht. Spans mit derselben Trace-ID bilden einen Trace. Über Parent-Child-Beziehungen wird daraus ein Baum, der zeigt, welchen Weg ein Request durch das System genommen hat und wie lange jeder Schritt dauerte. Das ist die Grundlage von Distributed Tracing.
Der erste Span eines Traces ohne Parent ist der Root Span. Spans entstehen durch Instrumentierung, entweder automatisch durch Bibliotheken und Agents oder manuell im Anwendungscode, und werden über Prozessgrenzen hinweg per Context Propagation verknüpft.
Span-Felder laut OpenTelemetry-Spezifikation
| Feld | Bedeutung |
|---|---|
| Name | Beschreibung der Operation mit niedriger Kardinalität, z. B. GET /orders/{id} oder SELECT orders |
| Span Context | Trace-ID (16 Byte, 32 Hex-Zeichen), Span-ID (8 Byte, 16 Hex-Zeichen), Trace Flags (inklusive Sampled-Bit) und Trace State |
| Parent-Span-ID | Span-ID des Parents; leer beim Root Span |
| Span Kind | SERVER, CLIENT, PRODUCER, CONSUMER oder INTERNAL |
| Start- und Endzeit | Zeitstempel, aus denen sich die Dauer ergibt |
| Attribute | Schlüssel-Wert-Paare wie http.request.method, http.response.status_code, db.system |
| Events | Ereignisse mit Zeitstempel innerhalb des Spans, z. B. eine Exception mit Typ, Meldung und Stacktrace |
| Links | Verweise auf Spans in anderen Traces, etwa bei Batch-Verarbeitung oder Fan-in aus einer Queue |
| Status | Unset, Ok oder Error, bei Fehlern mit optionaler Beschreibung |
Jeder Span gehört außerdem zu einer Resource (der erzeugenden Einheit, identifiziert über Attribute wie service.name und k8s.pod.name) und einem Instrumentation Scope (der Bibliothek, die ihn erzeugt hat).
Span Kinds und ihre Verbindung
Der Span Kind sagt dem Backend, wie ein Span mit anderen zusammenhängt. Ein CLIENT-Span im Checkout-Service und der passende SERVER-Span im Payment-Service beschreiben denselben Aufruf von beiden Seiten; die Differenz entfällt auf Netzwerk und Warteschlangen. PRODUCER und CONSUMER beschreiben asynchrone Kommunikation, bei der der Consumer lange nach dem Producer laufen kann. INTERNAL steht für Arbeit innerhalb eines Prozesses. Erst korrekte Span Kinds erlauben es Werkzeugen, Service-Maps zu zeichnen und Latenzen pro Service exakt zu berechnen.
Span-Dauern richtig lesen
Die Dauer eines Spans umfasst die Zeit aller seiner Child Spans. Um den eigentlichen Engpass zu finden, betrachten Sie die Self Time: den Anteil der Dauer, den kein Child Span abdeckt. Ein Server-Span mit 900 ms und einem Datenbank-Child mit 850 ms deutet auf die Abfrage. Ein Span mit 900 ms, dessen Children zusammen nur 50 ms ergeben, deutet auf Arbeit im Service selbst, etwa CPU-intensiven Code, Lock-Konflikte oder Garbage Collection. Hier übernimmt Continuous Profiling. Parallel laufende Child Spans können sich zudem überlappen, ihre Dauern ergeben in der Summe also nicht zwingend die Dauer des Parents.
Beispiel: ein vereinfachter Span
{
"name": "POST /orders",
"kind": "SERVER",
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"parentSpanId": "",
"startTime": "2026-10-15T09:12:03.120Z",
"endTime": "2026-10-15T09:12:03.512Z",
"attributes": {
"http.request.method": "POST",
"http.route": "/orders",
"http.response.status_code": 500
},
"events": [{"name": "exception",
"attributes": {"exception.type": "TimeoutError"}}],
"status": {"code": "ERROR"}
}
Dieser Root Span zur Veranschaulichung dauerte 392 ms und schlug fehl. Seine Child Spans, etwa eine Datenbankabfrage und ein Aufruf des Inventory-Services, würden zeigen, welcher Schritt die Zeit verbraucht hat.
Best Practices
- Span-Namen mit niedriger Kardinalität wählen. Nutzen Sie Routen-Templates; IDs gehören in Attribute, nicht in den Namen.
- Semantic Conventions einhalten, damit Abfragen über Sprachen und Bibliotheken hinweg funktionieren.
- Status bewusst setzen. Per Konvention ist ein Server-Span bei HTTP 5xx ein Fehler, bei 4xx liegt das Problem meist beim Aufrufer, und der Server-Span bleibt unset. Instrumentierungsbibliotheken lassen erfolgreiche Spans auf
Unset;Okist der expliziten Bestätigung durch Anwendungscode vorbehalten. - Exceptions als Events erfassen, damit Stacktraces an der fehlgeschlagenen Operation hängen.
- Fachlichen Kontext gezielt ergänzen: Attribute wie Kundensegment oder Feature Flag machen Traces deutlich nützlicher, personenbezogene Daten gehören aber nicht hinein.
Typische Fehler
- Für jeden Funktionsaufruf einen Span anlegen, was Overhead und Rauschen ohne Erkenntnisgewinn erzeugt.
- Spans nicht beenden, was Speicher kostet und irreführende Laufzeiten erzeugt.
- Abgerissener Kontext zwischen Services, der viele isolierte Root Spans statt eines Traces erzeugt.
- Links verwenden, wo eine Parent-Beziehung richtig wäre, oder umgekehrt.
Wie Obseria unterstützt
Obseria nimmt Spans per OTLP von jedem OpenTelemetry-SDK oder Collector entgegen und speichert sie ohne Vorab-Indexierung in Obseria Lake. So filtern und aggregieren Sie nach beliebigen Attributen. Spans sind mit Logs, Metriken und Profilen desselben Services und Zeitfensters verknüpft, und Obseria TopoGraph erstellt Service-Maps aus Span Kinds und Parent-Beziehungen. Mehr zu OpenTelemetry und dazu, wie Spans das Application Performance Monitoring speisen.