Actor-Tracing
Wenn die Tracing-Extension konfiguriert ist, erzeugt das
Framework automatisch einen Span pro Actor-Message plus
Infrastruktur-Spans für Cluster-Wire-Envelopes. Spans
verketten sich über Tells hinweg — ein Message-Handler, der
einem anderen Actor etwas tellt, gibt den aktiven Span-Kontext
weiter, sodass der Span des Empfängers zurück zum Sender
verlinkt.
Auto-instrumentierte Spans
Abschnitt betitelt „Auto-instrumentierte Spans“| Span-Name | Wann | Bemerkenswerte Attribute |
|---|---|---|
actor.receive | Einmal pro an onReceive gelieferter Message. | actor.path, actor.class, messaging.message_kind |
actor.persist | Aufruf von PersistentActor.persist(). | persistence.id, persistence.sequence_nr |
actor.ask | Ein ask(...)-Aufruf. | actor.path des Targets |
cluster.envelope.send | Ausgehender Cross-Cluster-Envelope. | peer.address, messaging.message_kind |
cluster.envelope.receive | Eingehender Cross-Cluster-Envelope. | Gleich |
Für einen typischen Request-Flow:
Jeder Span trägt die Trace-ID — dein Tracing-Backend näht sie zu einem Trace zusammen.
Kausale Verkettung
Abschnitt betitelt „Kausale Verkettung“// Innerhalb eines Actors:override async onReceive(msg) { // tracer.activeSpan() gibt den actor.receive-Span zurück // tell erzeugt einen Envelope mit gesetztem traceparent this.downstream.tell({ kind: 'derived', from: msg.id }); // Der actor.receive-Span des Downstream-Actors hat DIESEN Span als Parent}tell snapshottet den aktiven Span-Kontext auf den Envelope
(via tracer.injectContext()). Beim Empfangen führt das
Framework tracer.withActiveSpan(span, ...) aus, damit das
onReceive des Actors die Kette sieht.
Über Cluster-Nodes reitet derselbe traceparent auf dem Wire-
Envelope. Der empfangende Node extrahiert ihn; sein
actor.receive-Span verlinkt zurück zum Sender.
Span-Attribute
Abschnitt betitelt „Span-Attribute“// actor.receive-Attribute:{ 'actor.path': 'actor-ts://my-app/user/api/sessions/user-42', 'actor.class': 'SessionActor', 'actor.system': 'my-app', 'messaging.message_kind': 'login', // Bei Fehler persistiert: 'error.message': '...',}Für Cluster-Envelopes:
{ 'peer.address': 'actor-ts://my-app@10.0.0.5:2552', 'messaging.message_kind': 'login', 'wire.bytes': 234,}Diese folgen OpenTelemetry-Semantic-Conventions, wo zutreffend, sodass Standard-Dashboards (Honeycomb, Datadog, Grafana Tempo) ohne Anpassung funktionieren.
Auto-Tracing deaktivieren
Abschnitt betitelt „Auto-Tracing deaktivieren“system.extension(TracingExtensionId).configure({ tracer: new OtelTracerAdapter(...), autoSpanReceive: false, // actor.receive nicht auto-spannen autoSpanPersist: false, autoSpanClusterWire: true,});Nützlich, wenn du nur manuelle Spans an spezifischen Code-Stellen willst oder das Span-Volumen in sehr hochdurchsatz- starken Systemen reduzieren möchtest.
Die Auto-Instrumentierung des Frameworks ist standardmäßig opt-in, wenn ein Nicht-Noop-Tracer gesetzt ist; bei Bedarf pro Kategorie deaktivieren.
Anwendungs-Level-Spans hinzufügen
Abschnitt betitelt „Anwendungs-Level-Spans hinzufügen“override async onReceive(msg) { const tracer = this.context.system.extension(TracingExtensionId).tracer; const span = tracer.startSpan('process-order', { attributes: { 'order.id': msg.orderId, 'order.amount': msg.amount, }, }); try { await tracer.withActiveSpan(span, async () => { // ... Verarbeitung ... }); span.setStatus('ok'); } catch (e) { span.recordException(e as Error); span.setStatus('error', (e as Error).message); throw e; } finally { span.end(); }}Anwendungs-Spans erscheinen als Kinder des automatisch
erzeugten actor.receive-Spans — deine Custom-Logik sitzt
natürlich innerhalb der Verarbeitung des Actors.
MDC-Integration
Abschnitt betitelt „MDC-Integration“Wenn Tracing aktiv ist, mergt das Framework traceId und
spanId in den
LogContext, sodass jede Log-Zeile,
die während eines Spans emittiert wird, sie enthält:
[2025-05-13T12:00:00Z] INFO ... actor processing {traceId=abc, spanId=def, correlationId=...}Heißt: deine Logs und Traces teilen IDs — klicke von einem langsamen Trace in Honeycomb zu den passenden Log-Zeilen in Loki. Außerdem: Spans schließen die bestehenden MDC-Keys als Attribute ein.
Performance
Abschnitt betitelt „Performance“Wenn Tracing deaktiviert ist (NoopTracer), ist die Auto- Instrumentierung Null-Overhead — die Hot-Paths des Frameworks short-circuiten, ohne Spans zu allokieren oder Async-Storage-Lookups zu machen.
Wenn aktiviert, fügt jede verarbeitete Nachricht hinzu:
- Eine Span-Allokation (kleines Objekt).
- Ein paar Attribut-Schreibvorgänge.
- Ein AsyncLocalStorage-Scope.
Gesamtkosten pro Nachricht: ~5-10 Mikrosekunden. Signifikant für Millionen-msg-pro-Sekunde-Systeme; ansonsten vernachlässigbar.
Wo es weitergeht
Abschnitt betitelt „Wo es weitergeht“- Tracer-API — das zugrundeliegende Interface.
- OTel-Adapter — zu deinem Tracing-Backend pipen.
- Recording-Tracer — für Test-Assertions.
- Logging — LogContext — der MDC, der mit Trace-IDs angereichert wird.