Skip to content

Support OTLP runtime metrics with OTel-native naming#11318

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 17 commits into
masterfrom
maximo/otlp-runtime-metrics
May 19, 2026
Merged

Support OTLP runtime metrics with OTel-native naming#11318
gh-worker-dd-mergequeue-cf854d[bot] merged 17 commits into
masterfrom
maximo/otlp-runtime-metrics

Conversation

@link04
Copy link
Copy Markdown
Contributor

@link04 link04 commented May 8, 2026

What Does This Do

Adds an OTLP runtime-metrics path that emits JVM runtime metrics with OTel semantic-convention names (jvm.*) through the agent's MeterProvider, instead of the proprietary DogStatsD names (jvm.heap_memory, jvm.thread_count, …).

When the three flags below are set together, JvmOtlpRuntimeMetrics.start() is invoked from JMXFetch.run() and registers 15 instruments backed by java.lang.management MXBean callbacks. They flow through the existing OTLP exporter — no new transport. To avoid double-reporting, JMXFetch switches to jmxfetch-config-no-jvm-defaults.yaml (which sets collect_default_jvm_metrics: false) instead of the default config when OTLP runtime metrics are enabled.

JvmOtlpRuntimeMetrics lives in the agent-jmxfetch module. Starting it from JMXFetch.run() lets it ride the same delayed-start path as the rest of JMXFetch, avoiding the JMX side-effects that would occur if it were started from Agent.installDatadogTracer().

Flag Required value Default
DD_RUNTIME_METRICS_ENABLED true true
DD_METRICS_OTEL_ENABLED true false
DD_METRICS_OTEL_EXPORTER otlp unset

Instruments registered (15 total — Recommended + Development per the OTel JVM semconv):

  • Memoryjvm.memory.used, jvm.memory.committed, jvm.memory.limit, jvm.memory.init, jvm.memory.used_after_last_gc
  • Buffer poolsjvm.buffer.memory.used, jvm.buffer.memory.limit, jvm.buffer.count
  • Threadsjvm.thread.count
  • Class loadingjvm.class.loaded, jvm.class.count, jvm.class.unloaded
  • CPUjvm.cpu.time, jvm.cpu.count, jvm.cpu.recent_utilization

jvm.gc.duration is intentionally deferred. The spec requires a Histogram of per-collection pause durations, but GarbageCollectorMXBean only exposes cumulative collection time. Populating the histogram requires either subscribing to GarbageCollectionNotificationInfo via JMX (blocked by the bootstrap-class-loading constraints in docs/bootstrap_design_guidelines.md) or consuming JFR GarbageCollection events. Tracked as a follow-up.

Related system tests PR enabling tests: DataDog/system-tests#6800

Motivation

Customers running with DD_METRICS_OTEL_EXPORTER=otlp route their telemetry to an OTel collector — there may not be a Datadog Agent on the path, and therefore nothing listening on the DogStatsD socket. Today the tracer's runtime metrics still emit through DogStatsD with proprietary names (jvm.heap_memory, …), so in those deployments runtime metrics silently go nowhere.

This change emits the same runtime metric data as OTLP instruments with OTel semantic-convention names through the OTel MeterProvider, so it travels the same OTLP pipeline the customer already configured. Customers who haven't opted into OTLP metrics see no change — the existing DogStatsD path is untouched.

Additional Notes

  • The DogStatsD runtime-metrics path is unmodified. The two paths can run independently; opting into OTLP doesn't disable DogStatsD.
  • start() is single-shot: an AtomicBoolean CAS guards against re-entry from re-init, and on failure we log and stop (partial registration is worse than a silent retry).
  • Uses only java.lang.management.* plus com.sun.management.OperatingSystemMXBean for CPU. CPU instruments are skipped at registration time on JVMs where the com.sun bean isn't present. No javax.management.* is touched, keeping the constraints in docs/bootstrap_design_guidelines.md intact.
  • agent-jmxfetch depends on otel-bootstrap at build time (compile-only). The OTel API is vendor-repackaged into otel-bootstrap at build time, so it won't conflict with anything in the customer app.
  • OtelRunnableObservable (new, in otel-bootstrap) provides a Runnable-backed OtelObservable for lambda-style registration; it rate-limits exception logging from the callback.
  • jmxfetch-config-no-jvm-defaults.yaml is registered as a GraalVM native-image resource in ResourcesFeatureInstrumentation so AOT/native-image builds can load it.
  • Tests: JvmOtlpRuntimeMetricsTest (JUnit 5, opentelemetry-1.47 module) covers instrument surface, attribute keys (jvm.memory.type=heap|non_heap), positive values for live metrics (jvm.memory.used, jvm.thread.count), and idempotency of repeated start() calls.

@link04 link04 added type: enhancement Enhancements and improvements comp: metrics Metrics inst: opentelemetry OpenTelemetry instrumentation tag: ai generated Largely based on code generated by an AI or LLM labels May 8, 2026
@link04 link04 marked this pull request as ready for review May 9, 2026 11:02
@link04 link04 requested review from a team as code owners May 9, 2026 11:02
@link04 link04 requested review from mcculls and removed request for a team May 9, 2026 11:02
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 953c8710a6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java Outdated
Copy link
Copy Markdown
Contributor

@mcculls mcculls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JvmOtlpRuntimeMetrics needs to be moved to under the agent-jmxfetch module. You can then start it from the JMXFetch class, which means you won't need to change anything in the Agent class.

One thought I did have is that we'd still be sending JMXFetch runtime metrics over DogstatsD, and that this is somewhat duplicating the existing metrics managed there. Is there a plan to address this?

BTW another approach would be to write an implementation of AgentStatsdReporter (say AgentOtlpReporter) which sends metrics data from JMXFetch to the OTel API. We could pass into the embedded JMXFetch service when OTLP is enabled for metrics. This would let you send the existing JMXFetch metrics over OTLP and avoid having 2 different pieces of runtime metrics code to maintain.

(Downside is that a couple of the JMXFetch matrics are different to the OTel metrics, but we might need to converge those anyway.)

@mhlidd
Copy link
Copy Markdown
Contributor

mhlidd commented May 13, 2026

One thought I did have is that we'd still be sending JMXFetch runtime metrics over DogstatsD, and that this is somewhat duplicating the existing metrics managed there. Is there a plan to address this?

Agreed that this is suboptimal. I'll pass in a new .yaml file w/ collect_default_jvm_metrics: false to override the default JMX metrics that are emitted by JMXFetch.

BTW another approach would be to write an implementation of AgentStatsdReporter (say AgentOtlpReporter) which sends metrics data from JMXFetch to the OTel API. We could pass into the embedded JMXFetch service when OTLP is enabled for metrics. This would let you send the existing JMXFetch metrics over OTLP and avoid having 2 different pieces of runtime metrics code to maintain.

(Downside is that a couple of the JMXFetch matrics are different to the OTel metrics, but we might need to converge those anyway.)

@mcculls The problem w/ using JMXFetch is that it is structured to handle DogstatsD metric formats, and to emit the OTel runtime metrics over OTLP, we would still need to convert the results JMXFetch returns to what OTLP expects. This means that we would effectively duplicate the OTel runtime metrics information in a .yaml file and the tracer where we convert the JMXFetch results. IMO this is less optimal than just invoking the OTel Metrics API directly.

@mcculls mcculls self-requested a review May 14, 2026 22:21
@mcculls mcculls dismissed their stale review May 14, 2026 22:24

Stale

@mhlidd mhlidd added this pull request to the merge queue May 18, 2026
@dd-octo-sts
Copy link
Copy Markdown
Contributor

dd-octo-sts Bot commented May 18, 2026

/merge

@gh-worker-devflow-routing-ef8351
Copy link
Copy Markdown

gh-worker-devflow-routing-ef8351 Bot commented May 18, 2026

View all feedbacks in Devflow UI.

2026-05-18 19:21:25 UTC ℹ️ Start processing command /merge


2026-05-18 19:21:30 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-05-18 21:22:06 UTCMergeQueue: The build pipeline has timeout

The merge request has been interrupted because the build 0 took longer than expected. The current limit for the base branch 'master' is 120 minutes.

@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 18, 2026
@mhlidd mhlidd added this pull request to the merge queue May 19, 2026
@dd-octo-sts
Copy link
Copy Markdown
Contributor

dd-octo-sts Bot commented May 19, 2026

/merge

@gh-worker-devflow-routing-ef8351
Copy link
Copy Markdown

gh-worker-devflow-routing-ef8351 Bot commented May 19, 2026

View all feedbacks in Devflow UI.

2026-05-19 13:49:27 UTC ℹ️ Start processing command /merge


2026-05-19 13:49:32 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-05-19 15:49:57 UTCMergeQueue: The build pipeline has timeout

The merge request has been interrupted because the build 0 took longer than expected. The current limit for the base branch 'master' is 120 minutes.

@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 19, 2026
@mhlidd mhlidd added this pull request to the merge queue May 19, 2026
@dd-octo-sts
Copy link
Copy Markdown
Contributor

dd-octo-sts Bot commented May 19, 2026

/merge

@gh-worker-devflow-routing-ef8351
Copy link
Copy Markdown

gh-worker-devflow-routing-ef8351 Bot commented May 19, 2026

View all feedbacks in Devflow UI.

2026-05-19 17:05:59 UTC ℹ️ Start processing command /merge


2026-05-19 17:06:05 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-05-19 18:23:40 UTC ℹ️ MergeQueue: This merge request was merged

@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 19, 2026
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot merged commit 3b1c46e into master May 19, 2026
573 checks passed
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot deleted the maximo/otlp-runtime-metrics branch May 19, 2026 18:23
@github-actions github-actions Bot added this to the 1.63.0 milestone May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: metrics Metrics inst: opentelemetry OpenTelemetry instrumentation tag: ai generated Largely based on code generated by an AI or LLM type: enhancement Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants