fix: route spans into Dynatrace OneAgent's provider instead of an undefined span processor (#502) - #504
fix: route spans into Dynatrace OneAgent's provider instead of an undefined span processor (#502)#504sjvans wants to merge 3 commits into
Conversation
…ering an undefined span processor (#502)
SummaryThe following content is AI-generated and provides a summary of the pull request: Fix: Route Spans into Dynatrace OneAgent's Provider Instead of Crashing on Undefined Span ProcessorBug Fix🐛 Resolves a startup crash-loop (CF exit 137) when Dynatrace OneAgent is active and The fix restores the 1.6.0 behavior: on the OneAgent path, skip building a custom tracer provider entirely and instead return OneAgent's already-registered global delegate. CDS spans flow into OneAgent's provider automatically via the global OpenTelemetry API. Changes
Jira Issues
PR Bot InformationVersion:
|
There was a problem hiding this comment.
I didn’t find any additional blocking issues in the provided changes. The fix is narrowly scoped to the Dynatrace OneAgent path, and the added regression coverage exercises both the prior crash mode and the intended routing behavior.
PR Bot Information
Version: 1.31.20
- Correlation ID:
b411bf60-ad10-11f1-8f90-4351429b0cde - Experiment Variant:
treatment - Event Trigger:
pull_request.opened - File Content Strategy: Full file content
- LLM:
gpt-5.5
…ll back to the live global proxy Instead of returning trace.getTracerProvider().getDelegate() on the via_one_agent path, return nothing. registerInstrumentations() in lib/index.js already falls back to trace.getTracerProvider() (the global ProxyTracerProvider) when its tracerProvider argument is falsy, and the proxy is late-binding: a delegate registered after tracers are handed out is picked up retroactively. getDelegate() snapshots the current delegate, which would pin the auto-instrumentations to the Noop provider forever if OneAgent's provider were not yet registered. Drops the now-unused @opentelemetry/api import; updates the regression test to assert the factory returns undefined and leaves OneAgent as the global delegate.
…elemetry, when OneAgent transports traces (#502)
Fixes #502
Startup crash-loop (CF exit 137) when Dynatrace OneAgent is active, hit by two independent reporters upgrading
@cap-js/telemetry1.6.0 → 2.x.Root cause
On the OneAgent path —
DT_NODE_PRELOAD_OPTIONSset, kind*-to-dynatrace, and no@opentelemetry/exporter-trace-otlp-protodependency — the 2.x rewrite leftprocessorundefinedbut still rannew NodeTracerProvider({ spanProcessors: [processor] }).[undefined]was handed to the provider, andMultiSpanProcessor.onStartdereferenced it on the first span → crash. The CALMaddDelegate(processor)branch had the same hole.This is a regression: up to 1.6.0 the factory reused OneAgent's already-registered global tracer provider (via
getDelegateTracer()/getDelegate()) instead of building its own, so CDS spans flowed into OneAgent (preserving PurePath/service topology).Fix (restores 1.6.0 behavior — "Option A")
On the OneAgent path, don't build or register our own provider at all, and create no span processor — return nothing:
CDS spans obtain their tracer via the global OpenTelemetry API (
lib/tracing/trace.js→otel.trace.getTracer(...)), so they route into OneAgent's provider automatically.registerInstrumentations(the only consumer of this return value) falls back totrace.getTracerProvider()— the globalProxyTracerProvider— when the argument is falsy, so the auto-instrumentations attach to OneAgent too. This removes both undefined-processor crash sites at once; all other kinds/paths are unchanged (the processor-construction block just moved out of theelse).Why return nothing rather than
trace.getTracerProvider().getDelegate()? The proxy is late-binding — a delegate registered after tracers have been handed out is picked up retroactively.getDelegate()snapshots the current delegate, which would pin the auto-instrumentations to the Noop provider forever if OneAgent's provider weren't registered yet. LettingregisterInstrumentationssupply the live proxy is both safer and less code. (Verified under OTel 2.0:@opentelemetry/api@1.9.x'sProxyTracerProviderstill exposes this late-binding behavior; the 1.6.0provider.addSpanProcessor(...)call is gone in 2.0, but the OneAgent path never added a processor, so Option A doesn't need it.)Tests
New
test/tracing-one-agent.test.js:new NodeTracerProvider({ spanProcessors: [undefined] })throws/onStart/on the first span.via_one_agentpath, and asserts it doesn't throw, returnsundefined(registers no provider of its own), leaves OneAgent as the global delegate, and a span created via the global API lands in OneAgent's exporter.22 passed | 2 skippedacross the provider-setup suites;format:checkclean;package.json/package-lock.jsonuntouched.Behavior note (docs)
Because Option A registers no provider on the OneAgent path,
tracing.sampler(incl.ignoreIncomingPaths) andtracing.propagatorsare governed by OneAgent, not@cap-js/telemetry— the same contract as 1.6.0. This is architecturally unavoidable: under OneAgent there is a single global TracerProvider and it must be OneAgent's for spans to reach Dynatrace. Documented in the README (Leveraging Dynatrace OneAgent, with cross-references from the Sampler and Propagators sections).Follow-up
Worth a 2.0.x backport — both reporters are dead-on-boot on 2.0.1 (what's on
maintoday).