Runlane
Run in production

Export traces, metrics, and logs

Connect Runlane to your OpenTelemetry SDK and send runtime signals to your monitoring backend.

Runlane emits runtime traces, metrics, and structured logs through your application's OpenTelemetry SDK. The SDK's exporters send them to a Collector or monitoring backend. Configure the SDK in every producer and worker process.

If you already initialize an OpenTelemetry SDK, add @runlane/observability and its OpenTelemetry peers, then pass openTelemetryRunlaneTelemetry() to createRunlane({ observability: { telemetry } }). Import the adapter from @runlane/observability/opentelemetry.

Runtime signals are best-effort. For checkpointed export of persisted run events, steps, and wait-token transitions, also export durable observations.

Set up the SDK

This example uses Node.js 22.18.0 or newer, OpenTelemetry SDK 2.9.x / experimental packages 0.220.x, and a Collector accepting OTLP/HTTP on port 4318. Keep @opentelemetry/api-logs on the peer range supported by your Runlane version.

npm install @runlane/observability @opentelemetry/api@^1.9.1 @opentelemetry/api-logs@~0.220.0
npm install @opentelemetry/sdk-node@~0.220.0

Create the SDK before constructing your Runlane runtime. NodeSDK includes the exporters and configures the trace, metric, and log pipelines from standard OpenTelemetry settings:

instrumentation.mts
import { NodeSDK } from '@opentelemetry/sdk-node'

export const sdk = new NodeSDK({ serviceName: 'runlane-worker' })
sdk.start()

In this SDK version, all three signals default to OTLP over HTTP/protobuf. The SDK supplies batching, a periodic metric reader, the async context manager, and propagators. Construct exporters, processors, or readers explicitly only when you need to override that setup. See the Node SDK configuration.

Set OTEL_EXPORTER_OTLP_ENDPOINT to your Collector's base URL, such as http://localhost:4318. The SDK exporters append /v1/traces, /v1/metrics, and /v1/logs. Use OTEL_EXPORTER_OTLP_HEADERS for authentication and OTEL_EXPORTER_OTLP_PROTOCOL if the receiver requires another encoding. To disable a signal, set its OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER, or OTEL_LOGS_EXPORTER variable to none. See the OTLP exporter settings.

Attach Runlane and verify a trace

The following smoke check uses the local lane. Install @runlane/core, @runlane/lane-local, and zod if you have not followed the quickstart.

telemetry-check.mts
import { trace } from '@opentelemetry/api'
import { createRunlane, queue, task } from '@runlane/core'
import { createLocalLane } from '@runlane/lane-local'
import { openTelemetryRunlaneTelemetry } from '@runlane/observability/opentelemetry'
import * as z from 'zod'

import { sdk } from './instrumentation.mts'

const check = task({
  id: 'telemetry.check',
  schema: z.undefined(),
  run: () => undefined,
})
const runlane = createRunlane({
  lane: createLocalLane(),
  queues: [queue({ name: 'default', default: true })],
  tasks: [check],
  observability: { telemetry: openTelemetryRunlaneTelemetry() },
})

try {
  await trace.getTracer('application').startActiveSpan('telemetry.check.request', async (span) => {
    try {
      await runlane.trigger(check)
    } finally {
      span.end()
    }
  })
  await runlane.drain()
} finally {
  try {
    await runlane.close()
  } finally {
    await sdk.shutdown()
  }
}

Run OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 node telemetry-check.mts. In your backend, find telemetry.check.request under service runlane-worker. Its trace should contain runlane.trigger, followed by runlane.run.attempt and runlane.task.handler. Look for runlane.worker.execution.count and the runnable scan counters in metrics. A successful run need not emit a runtime log; these logs describe operational events such as failures and ignored deliveries.

The SDK owns batching, sampling, exporter retries, and buffers. Runlane closes its runtime resources; the application must flush and shut down its SDK after workers, maintenance, and observation export have stopped. If your application also needs HTTP or database instrumentation, initialize that through your existing SDK setup before loading those libraries.

Trace propagation

Ordinary trigger() and runNow() calls capture active context automatically. Queued runs persist the context of the trigger span so a later worker attempt can join the same trace. Handler spans, step spans, and child triggers use the active execution context. Retries and resumed attempts retain the run's stored carrier.

An explicit traceCarrier takes precedence over automatic capture. This is useful when you receive propagation headers outside an instrumented request. Custom tracers can implement the optional synchronous RunlaneTracer.captureContext() method; existing tracers continue to work without it. Capture failures are reported to observability.onTelemetryError with event type propagation and do not fail run creation.

The OpenTelemetry adapter uses the application's context manager and propagator. With no valid active span or configured propagator, it cannot capture a carrier. A child trigger falls back to its parent run's stored carrier when capture is unavailable.

Captured and explicitly supplied carriers use the same traceCarrierSchema, bounded by contractDefaults.traceCarrier.maxBytes. The limit measures UTF-8 JSON bytes, including header names, values, and escaping. An oversized explicit carrier rejects trigger() or runNow() with configuration_invalid before creating a run. An oversized captured carrier is discarded and reported through onTelemetryError; the run can still proceed.

Send signals to Datadog or Sentry

BackendRouteSignals
DatadogSend OTLP to a configured Datadog Agent, Collector, or Datadog OTLP intake.Traces, metrics, and logs; enable the matching pipelines.
SentryUse Sentry's Node OpenTelemetry integration for traces, or its OTLP endpoints for traces and logs.Sentry's OTLP intake does not support metrics; route Runlane metrics elsewhere.

Follow Datadog's OTLP ingestion setup for endpoints and credentials. For Sentry, follow its Node OpenTelemetry integration or OTLP intake documentation. Sentry's OTLP intake is in open beta. When Sentry owns OpenTelemetry initialization, use that setup instead of registering another global SDK.

Runlane records sanitized error codes on spans. It does not call Sentry's captureException() or send original exception stacks through its telemetry adapter. Capture application exceptions through your application's error reporting policy if you need Sentry issues.

If signals are missing

Check that SDK initialization runs in the process emitting the signal, that the SDK has the corresponding trace exporter, metric reader, or log processor, and that the Collector enables that signal's pipeline. The OpenTelemetry API alone does not export data. Check SDK diagnostics for network, credentials, sampling, or buffer problems; asynchronous SDK export errors do not reach Runlane's synchronous onTelemetryError hook.

Use the telemetry reference to check signal names, attributes, and emission conditions.

On this page