feat(tracing): support Dynatrace OneAgent in-process trace capture - #512
Conversation
…n-process capture On the via_one_agent path (DT_NODE_PRELOAD_OPTIONS set, kind *-to-dynatrace, no OTLP trace exporter installed), register a recording NodeTracerProvider with an empty span-processor list instead of warning and bailing out. The provider records but exports nothing itself, so the CDS spans exist for Dynatrace OneAgent's in-process OpenTelemetry capture to pick up without duplicating them. The CALM path (falsy resource) still defers to @sap/xotel-agent-ext-js. README/CHANGELOG document that current OneAgent versions do not yet capture the OpenTelemetry JS SDK version this package depends on, so the OTLP exporter remains required until OneAgent adds support.
SummaryThe following content is AI-generated and provides a summary of the pull request: Enable OneAgent In-Process Trace Recording for DynatraceNew Features✨ Registers a recording Changes
GitHub Issues
PR Bot InformationVersion:
|
There was a problem hiding this comment.
I found a likely user-facing mismatch: the startup log now says it is “relying on in-process trace capture,” while the README/CHANGELOG explicitly state current OneAgent versions do not capture these spans and OTLP remains required. Since I can’t make more tool calls, I’m not posting an inline comment, but I recommend changing that log to a warning that clearly tells users CDS spans may not reach Dynatrace without the OTLP exporter.
PR Bot Information
Version: 1.31.43
- Event Trigger:
pull_request.opened - LLM:
gpt-5.5 - Correlation ID:
8c6a24b0-b32e-11f1-8cfc-611f4849a691 - File Content Strategy: Full file content
Fold the OneAgent branch into the two-axis flow (via_one_agent × resource) instead of an early self-contained block: - reject the unverified CALM + OneAgent combination fail-fast - OneAgent standalone falls through to the provider path with no processor (spanProcessors: []), so recording works without an export path of our own
Drop the temporary caveat that the OTLP exporter is still required under OneAgent. The in-process path is the intended behavior; the current OneAgent-side SDK-layout gap is transient and tracked separately.
Fixes #509. Follow-up to #502 / #504.
What this changes
On the OneAgent path — Dynatrace OneAgent present (
DT_NODE_PRELOAD_OPTIONSset), kind*-to-dynatrace, and no@opentelemetry/exporter-trace-otlp-protoinstalled — the tracing factory now registers a recordingNodeTracerProviderwith an empty span-processor list instead of warning and bailing out.The provider records spans but exports nothing itself, so the CDS spans actually come into existence for OneAgent's in-process OpenTelemetry capture to pick up — without us exporting (and thus duplicating) them.
The factory's branching was refactored around two orthogonal axes —
via_one_agent(OneAgent present, no exporter) andresource(truthy = standalone, falsy = CALM under@sap/xotel-agent-ext-js):resource)resource)@sap/xotel-agent-ext-jsthrowThe CALM + OneAgent combination is now rejected fail-fast: two agents owning tracing at once is a contradictory, unverified setup, so the factory throws rather than silently doing nothing.
This is the plugin-side prerequisite for OneAgent in-process capture. It is correct and necessary — but, as the analysis below shows, it is not sufficient on its own with current OneAgent versions.
Analysis / findings
I live-verified this against a OneAgent-injected app on SAP BTP CF (classic-managed Dynatrace tenant, OneAgent 1.339). Findings:
OneAgent's in-process OTel capture is version-sensitive to the OpenTelemetry JS SDK layout. OneAgent instruments OTel-JS by wrapping
getTraceron the class defined in@opentelemetry/sdk-trace-base'sBasicTracerProvider.js, then applying its span transformer to the returned tracer. (Its other, deeperTracer/Spanpatch is version-gated to SDK^1.0.0and is skipped on 2.x.)OpenTelemetry JS SDK
sdk-trace-base2.9.0 restructured the packages:getTracermoved out ofsdk-trace-baseinto a new package@opentelemetry/sdk-trace(TracerProvider.js), andBasicTracerProvider.jsbecame a thin re-exportBasicTracerProvider-shim.js. On ≥2.9 there is noBasicTracerProvider.jsfor OneAgent to match, and it has no rule for the new@opentelemetry/sdk-tracepackage — so thegetTracerwrap never attaches and the CDSINTERNALspans are never captured.Exact, empirically confirmed boundary:
@opentelemetry/sdk-trace-baseBasicTracerProvider.jswithgetTracerBasicTracerProvider-shim.js+ split into@opentelemetry/sdk-traceClean A/B on the same app + same OneAgent:
@opentelemetry/sdk-trace-* ^2.8floating to 2.11.0 (post-split): only the HTTP PurePath appears — no CDS spans.CatalogService - tx→CatalogService - READ …→db - READ …→@cap-js/sqlite - prepare/stmt.all SELECT …(scope@cap-js/telemetry,SpanKind.INTERNAL).The HTTP entry point is captured in both cases because that is OneAgent's own HTTP sensor, not OTel.
Conclusion. This is an OneAgent-side gap: OneAgent has not yet been updated for the OTel-JS SDK 2.9 package restructure. The full fix is on the Dynatrace side (OneAgent needs to instrument the current layout —
@opentelemetry/sdk-trace'sTracerProvider.js/Tracer.js/Span.js, or relax the deep-patch version gate to^2.0.0). A support ticket with Dynatrace references this PR.Dependencies are kept at
^2.8(floating to 2.x latest) — pinning the plugin to the last pre-split patch to work around a OneAgent bug would freeze the OTel SDK and is not a maintainable trade-off. Once OneAgent adds support for the current layout, in-process capture works with no further change here.Docs
The temporary pin that works around the current OneAgent gap is posted as a workaround comment on #509 rather than baked into the README, so it can simply be dropped once OneAgent catches up.
Tests
test/tracing-one-agent.test.jsasserts, without a live tenant:undefinedthrows on the first span (why the OneAgent path uses[], not[processor]);@opentelemetry/exporter-trace-otlp-protois not a production dependency (precondition forvia_one_agent);NodeTracerProviderwhose spans are recording (notNonRecordingSpan) and whose creation/end never crashes;