Skip to content

OTel tracing adapter

OtelTracerAdapter is the production-grade tracer. It plugs the framework’s Tracer interface into the OpenTelemetry SDK, so spans flow to whatever backend you’ve configured (Jaeger, Tempo, Honeycomb, Datadog, New Relic, Grafana Cloud).

import { trace as otelTrace } from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { ActorSystem, TracingExtensionId, OtelTracerAdapter } from 'actor-ts';
// 1. Set up the OTel SDK
const provider = new NodeTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(
new OTLPTraceExporter({ url: 'https://otel-collector.example.com/v1/traces' }),
));
provider.register();
// 2. Wire the adapter
const system = ActorSystem.create('my-app');
system.extension(TracingExtensionId).configure({
tracer: new OtelTracerAdapter({
tracer: otelTrace.getTracer('actor-ts', '1.0.0'),
}),
});

With this set up, the framework’s auto-spans (one per actor message) flow to your tracing backend, joined with W3C traceparent context across cluster nodes.

interface OtelTracerAdapterSettings {
tracer: OtelTracer; // from @opentelemetry/api
sampler?: Sampler; // optional — usually configured at SDK level
}

Minimal — the OTel SDK does the heavy lifting (export, sampling, batching, etc.). The adapter just translates between APIs.

import { ParentBasedSampler, TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base';
const provider = new NodeTracerProvider({
sampler: new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(0.1), // sample 10 % of traces
}),
});

For high-throughput systems, sampling is essential:

  • TraceIdRatioBasedSampler(0.1) — 10 % of traces.
  • AlwaysOnSampler() — every trace (dev / low-volume).
  • AlwaysOffSampler() — none (debugging only).
  • ParentBasedSampler(...) — defer to parent’s sampling decision; samples downstream of an already-sampled trace.

The framework records spans at full rate; the SDK decides which to export. Unsampled spans still propagate context for correlation but don’t export — zero backend cost.

OTel SDK supports many exporters:

ExporterBackend
@opentelemetry/exporter-trace-otlp-httpOTLP (Tempo, Jaeger, generic collectors)
@opentelemetry/exporter-trace-otlp-grpcOTLP via gRPC
@opentelemetry/exporter-jaegerJaeger native
@opentelemetry/exporter-zipkinZipkin
Vendor-specificDatadog, New Relic, Honeycomb, …

Pick by your backend’s preferred protocol. OTLP-over-HTTP is the most general — works with the OpenTelemetry Collector, which then routes to any backend.

import { Resource } from '@opentelemetry/resources';
const provider = new NodeTracerProvider({
resource: new Resource({
'service.name': 'my-app',
'service.version': '1.2.3',
'deployment.environment': 'production',
'host.name': process.env.HOSTNAME,
}),
});

Resource attributes are stamped on every span and the most useful place to put global context (service name, version, region, pod name).

Terminal window
npm install @opentelemetry/api @opentelemetry/sdk-trace-node
# Plus an exporter:
npm install @opentelemetry/exporter-trace-otlp-http

Bring your own SDK + exporter combo — the framework doesn’t bundle either.

// Set up tracer:
system.extension(TracingExtensionId).configure({
tracer: new OtelTracerAdapter({ tracer: otelTrace.getTracer('actor-ts') }),
});
// Set up metrics:
const adapter = new OtelMetricsAdapter({
source: metrics.registry,
meter: otelMetrics.getMeter('actor-ts'),
});
adapter.start();

The OTel SDK ties traces, metrics, and logs together via shared context. See OTel metrics adapter.