Skip to content

Pass OTel env vars to child processes #44024

@jamietanna

Description

@jamietanna

When running under OpenTelemetry, there are some environment variables such as OTEL_SERVICE_NAME to propagate, but we also make sure that we're propagating the trace parent + state, so we have more in-depth tracing.

Potentially via:

Claude Sonnet 4.6 thoughts
❯ when running OTel, if I call a child process, should I also send env vars to
  them, so they can also trace?

⏺ Yes — OTel context propagation across process boundaries requires passing the
  trace context via environment variables (or another mechanism like HTTP
  headers).

  The standard approach is to use the W3C TraceContext propagation format.
  Before spawning the child, serialize the current span context:

  import { propagation, context } from '@opentelemetry/api';

  const carrier: Record<string, string> = {};
  propagation.inject(context.active(), carrier);

  // carrier now has: { traceparent: '00-<traceId>-<spanId>-01', tracestate:
  '...' }

  spawn('child-process', args, {
    env: { ...process.env, ...carrier },
  });

  In the child process, extract and restore the context:

  import { propagation, context, trace } from '@opentelemetry/api';

  const carrier = {
    traceparent: process.env['traceparent'],
    tracestate: process.env['tracestate'],
  };

  const ctx = propagation.extract(context.active(), carrier);

  // Now start spans under the restored parent context
  const tracer = trace.getTracer('child');
  tracer.startActiveSpan('child-work', {}, ctx, (span) => {
    // ...
    span.end();
  });

  Key points:
  - The traceparent header carries the trace/span ID and sampling flag — without
   it, the child starts a disconnected root span.
  - tracestate carries vendor-specific data and should also be forwarded.
  - The child still needs its own OTel SDK initialized with an exporter —
  propagation only links the spans, it doesn't tunnel telemetry through the
  parent.
  - If the child is short-lived, make sure it flushes before exit
  (provider.shutdown()), otherwise spans may be dropped.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Priority

    None yet

    Datasource

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions