From 4525c09d6926e1181b0c8e1ed90bf13926400f93 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Mon, 19 Jan 2026 12:14:12 +0000 Subject: [PATCH 01/14] Build instrument descriptors --- .../shim/OtelInstrumentationScope.java | 11 ++++ .../shim/metrics/OtelDoubleCounter.java | 8 ++- .../shim/metrics/OtelDoubleGauge.java | 8 ++- .../shim/metrics/OtelDoubleHistogram.java | 8 ++- .../shim/metrics/OtelDoubleUpDownCounter.java | 8 ++- .../shim/metrics/OtelInstrument.java | 30 +++++++++ .../shim/metrics/OtelInstrumentBuilder.java | 5 ++ .../metrics/OtelInstrumentDescriptor.java | 66 +++++++++++++++++++ .../shim/metrics/OtelLongCounter.java | 8 ++- .../shim/metrics/OtelLongGauge.java | 8 ++- .../shim/metrics/OtelLongHistogram.java | 8 ++- .../shim/metrics/OtelLongUpDownCounter.java | 8 ++- 12 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java index 407fd0edbd0..823c6f40ea8 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java @@ -36,4 +36,15 @@ public int hashCode() { result = 31 * result + Objects.hashCode(schemaUrl); return result; } + + @Override + public String toString() { + // use same property names as OTel in toString + return "OtelInstrumentationScope{" + + "name='" + + scopeName + + (scopeVersion != null ? "', version='" + scopeVersion : "") + + (schemaUrl != null ? "', schemaUrl='" + schemaUrl : "") + + "'}"; + } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java index c1584ed24c4..b5c26d59d8a 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java @@ -15,7 +15,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelDoubleCounter implements DoubleCounter { +final class OtelDoubleCounter extends OtelInstrument implements DoubleCounter { + + OtelDoubleCounter(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void add(double value) { @@ -53,7 +57,7 @@ public DoubleCounterBuilder setUnit(String unit) { @Override public DoubleCounter build() { - return new OtelDoubleCounter(); + return new OtelDoubleCounter(instrumentBuilder.toDescriptor()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java index 1929175be72..48e4f6b5049 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java @@ -16,7 +16,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelDoubleGauge implements DoubleGauge { +final class OtelDoubleGauge extends OtelInstrument implements DoubleGauge { + + OtelDoubleGauge(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void set(double value) { @@ -59,7 +63,7 @@ public LongGaugeBuilder ofLongs() { @Override public DoubleGauge build() { - return new OtelDoubleGauge(); + return new OtelDoubleGauge(instrumentBuilder.toDescriptor()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index bbc3612c5ec..a7b39ceb07f 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -12,7 +12,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelDoubleHistogram implements DoubleHistogram { +final class OtelDoubleHistogram extends OtelInstrument implements DoubleHistogram { + + OtelDoubleHistogram(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void record(double value) { @@ -61,7 +65,7 @@ public LongHistogramBuilder ofLongs() { @Override public DoubleHistogram build() { - return new OtelDoubleHistogram(); + return new OtelDoubleHistogram(instrumentBuilder.toDescriptor()); } } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java index 32b68931659..38a91a210b7 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java @@ -15,7 +15,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelDoubleUpDownCounter implements DoubleUpDownCounter { +final class OtelDoubleUpDownCounter extends OtelInstrument implements DoubleUpDownCounter { + + OtelDoubleUpDownCounter(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void add(double value) { @@ -53,7 +57,7 @@ public DoubleUpDownCounterBuilder setUnit(String unit) { @Override public DoubleUpDownCounter build() { - return new OtelDoubleUpDownCounter(); + return new OtelDoubleUpDownCounter(instrumentBuilder.toDescriptor()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java new file mode 100644 index 00000000000..33d2eeb48ee --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java @@ -0,0 +1,30 @@ +package datadog.opentelemetry.shim.metrics; + +/** Ensure all instruments implement the same equivalency. */ +abstract class OtelInstrument { + private final OtelInstrumentDescriptor descriptor; + + OtelInstrument(OtelInstrumentDescriptor descriptor) { + this.descriptor = descriptor; + } + + @Override + public final boolean equals(Object o) { + if (!(o instanceof OtelInstrument)) { + return false; + } + + OtelInstrument that = (OtelInstrument) o; + return descriptor.equals(that.descriptor); + } + + @Override + public final int hashCode() { + return descriptor.hashCode(); + } + + @Override + public final String toString() { + return getClass().getSimpleName() + "{descriptor=" + descriptor + '}'; + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java index 494cefea84f..870226f9c00 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java @@ -80,4 +80,9 @@ void setDescription(String description) { void setUnit(String unit) { this.unit = unit; } + + OtelInstrumentDescriptor toDescriptor() { + return new OtelInstrumentDescriptor( + instrumentName, instrumentType, longValues, description, unit); + } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java new file mode 100644 index 00000000000..89c92c37d10 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java @@ -0,0 +1,66 @@ +package datadog.opentelemetry.shim.metrics; + +import java.util.Locale; +import java.util.Objects; +import javax.annotation.Nullable; + +/** Uniquely describes an instrument for the Meter that created it. */ +final class OtelInstrumentDescriptor { + private final String instrumentName; + private final OtelInstrumentType instrumentType; + private final boolean longValues; + @Nullable private final String description; + @Nullable private final String unit; + + OtelInstrumentDescriptor( + String instrumentName, + OtelInstrumentType instrumentType, + boolean longValues, + @Nullable String description, + @Nullable String unit) { + this.instrumentName = instrumentName; + this.instrumentType = instrumentType; + this.longValues = longValues; + this.description = description; + this.unit = unit; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof OtelInstrumentDescriptor)) { + return false; + } + + OtelInstrumentDescriptor that = (OtelInstrumentDescriptor) o; + return instrumentName.equalsIgnoreCase(that.instrumentName) + && instrumentType == that.instrumentType + && longValues == that.longValues + && Objects.equals(description, that.description) + && Objects.equals(unit, that.unit); + } + + @Override + public int hashCode() { + int result = instrumentName.toLowerCase(Locale.ROOT).hashCode(); + result = 31 * result + instrumentType.hashCode(); + result = 31 * result + Boolean.hashCode(longValues); + result = 31 * result + Objects.hashCode(description); + result = 31 * result + Objects.hashCode(unit); + return result; + } + + @Override + public String toString() { + // use same property names as OTel in toString + return "OtelInstrumentDescriptor{" + + "name='" + + instrumentName + + (description != null ? ", description='" + description : "") + + (unit != null ? "', unit='" + unit : "") + + "', type=" + + instrumentType + + ", valueType=" + + (longValues ? "LONG" : "DOUBLE") + + "}"; + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java index c387c5b8104..0334656fb5f 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java @@ -16,7 +16,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelLongCounter implements LongCounter { +final class OtelLongCounter extends OtelInstrument implements LongCounter { + + OtelLongCounter(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void add(long value) { @@ -59,7 +63,7 @@ public DoubleCounterBuilder ofDoubles() { @Override public LongCounter build() { - return new OtelLongCounter(); + return new OtelLongCounter(instrumentBuilder.toDescriptor()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java index 97f708d49e5..0cf7c4b6c1e 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java @@ -15,7 +15,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelLongGauge implements LongGauge { +final class OtelLongGauge extends OtelInstrument implements LongGauge { + + OtelLongGauge(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void set(long value) { @@ -53,7 +57,7 @@ public LongGaugeBuilder setUnit(String unit) { @Override public LongGauge build() { - return new OtelLongGauge(); + return new OtelLongGauge(instrumentBuilder.toDescriptor()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index c005bc10806..2baa3d238f8 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -11,7 +11,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelLongHistogram implements LongHistogram { +final class OtelLongHistogram extends OtelInstrument implements LongHistogram { + + OtelLongHistogram(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void record(long value) { @@ -55,7 +59,7 @@ public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketB @Override public LongHistogram build() { - return new OtelLongHistogram(); + return new OtelLongHistogram(instrumentBuilder.toDescriptor()); } } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java index d53eb468592..17233d17fe3 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java @@ -16,7 +16,11 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault -final class OtelLongUpDownCounter implements LongUpDownCounter { +final class OtelLongUpDownCounter extends OtelInstrument implements LongUpDownCounter { + + OtelLongUpDownCounter(OtelInstrumentDescriptor descriptor) { + super(descriptor); + } @Override public void add(long value) { @@ -59,7 +63,7 @@ public DoubleUpDownCounterBuilder ofDoubles() { @Override public LongUpDownCounter build() { - return new OtelLongUpDownCounter(); + return new OtelLongUpDownCounter(instrumentBuilder.toDescriptor()); } @Override From 7c3a232f3e46f0ca5bd710af217c6d86c25c6e0f Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Mon, 19 Jan 2026 16:09:31 +0000 Subject: [PATCH 02/14] Fill in default arguments --- .../datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java | 4 ++-- .../datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java | 4 ++-- .../opentelemetry/shim/metrics/OtelDoubleHistogram.java | 4 ++-- .../opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java | 4 ++-- .../datadog/opentelemetry/shim/metrics/OtelLongCounter.java | 4 ++-- .../datadog/opentelemetry/shim/metrics/OtelLongGauge.java | 4 ++-- .../datadog/opentelemetry/shim/metrics/OtelLongHistogram.java | 4 ++-- .../opentelemetry/shim/metrics/OtelLongUpDownCounter.java | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java index b5c26d59d8a..3a689cddb2c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java @@ -23,12 +23,12 @@ final class OtelDoubleCounter extends OtelInstrument implements DoubleCounter { @Override public void add(double value) { - // FIXME: implement recording + add(value, Attributes.empty()); } @Override public void add(double value, Attributes attributes) { - // FIXME: implement recording + add(value, attributes, Context.current()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java index 48e4f6b5049..f080a040842 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java @@ -24,12 +24,12 @@ final class OtelDoubleGauge extends OtelInstrument implements DoubleGauge { @Override public void set(double value) { - // FIXME: implement recording + set(value, Attributes.empty()); } @Override public void set(double value, Attributes attributes) { - // FIXME: implement recording + set(value, attributes, Context.current()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index a7b39ceb07f..1233b24d861 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -20,12 +20,12 @@ final class OtelDoubleHistogram extends OtelInstrument implements DoubleHistogra @Override public void record(double value) { - // FIXME: implement recording + record(value, Attributes.empty()); } @Override public void record(double value, Attributes attributes) { - // FIXME: implement recording + record(value, attributes, Context.current()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java index 38a91a210b7..d9c16cdb98a 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java @@ -23,12 +23,12 @@ final class OtelDoubleUpDownCounter extends OtelInstrument implements DoubleUpDo @Override public void add(double value) { - // FIXME: implement recording + add(value, Attributes.empty()); } @Override public void add(double value, Attributes attributes) { - // FIXME: implement recording + add(value, attributes, Context.current()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java index 0334656fb5f..fae616225c9 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java @@ -24,12 +24,12 @@ final class OtelLongCounter extends OtelInstrument implements LongCounter { @Override public void add(long value) { - // FIXME: implement recording + add(value, Attributes.empty()); } @Override public void add(long value, Attributes attributes) { - // FIXME: implement recording + add(value, attributes, Context.current()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java index 0cf7c4b6c1e..e812e5b3c6e 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java @@ -23,12 +23,12 @@ final class OtelLongGauge extends OtelInstrument implements LongGauge { @Override public void set(long value) { - // FIXME: implement recording + set(value, Attributes.empty()); } @Override public void set(long value, Attributes attributes) { - // FIXME: implement recording + set(value, attributes, Context.current()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index 2baa3d238f8..6a42e29d806 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -19,12 +19,12 @@ final class OtelLongHistogram extends OtelInstrument implements LongHistogram { @Override public void record(long value) { - // FIXME: implement recording + record(value, Attributes.empty()); } @Override public void record(long value, Attributes attributes) { - // FIXME: implement recording + record(value, attributes, Context.current()); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java index 17233d17fe3..521c4388c5c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java @@ -24,12 +24,12 @@ final class OtelLongUpDownCounter extends OtelInstrument implements LongUpDownCo @Override public void add(long value) { - // FIXME: implement recording + add(value, Attributes.empty()); } @Override public void add(long value, Attributes attributes) { - // FIXME: implement recording + add(value, attributes, Context.current()); } @Override From 10d70b3c5fe8cab1b56d95759fda12d4cbc0e587 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Mon, 19 Jan 2026 16:39:36 +0000 Subject: [PATCH 03/14] Disallow negative values in counters/histograms --- .../shim/OtelInstrumentationScope.java | 14 ++++++++++++++ .../shim/metrics/OtelDoubleCounter.java | 14 +++++++++++++- .../shim/metrics/OtelDoubleHistogram.java | 15 ++++++++++++++- .../shim/metrics/OtelInstrument.java | 4 ++++ .../shim/metrics/OtelInstrumentDescriptor.java | 14 ++++++++++++++ .../shim/metrics/OtelLongCounter.java | 14 +++++++++++++- .../shim/metrics/OtelLongHistogram.java | 14 +++++++++++++- 7 files changed, 85 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java index 823c6f40ea8..68869e54ff9 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/OtelInstrumentationScope.java @@ -17,6 +17,20 @@ public OtelInstrumentationScope( this.schemaUrl = schemaUrl; } + public String getName() { + return scopeName; + } + + @Nullable + public String getVersion() { + return scopeVersion; + } + + @Nullable + public String getSchemaUrl() { + return schemaUrl; + } + @Override public boolean equals(Object o) { if (!(o instanceof OtelInstrumentationScope)) { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java index 3a689cddb2c..6bfef6aa105 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java @@ -5,17 +5,22 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleCounter; import io.opentelemetry.api.metrics.DoubleCounterBuilder; import io.opentelemetry.api.metrics.ObservableDoubleCounter; import io.opentelemetry.api.metrics.ObservableDoubleMeasurement; import io.opentelemetry.context.Context; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelDoubleCounter extends OtelInstrument implements DoubleCounter { + private static final RatelimitedLogger log = + new RatelimitedLogger(LoggerFactory.getLogger(OtelDoubleCounter.class), 5, TimeUnit.MINUTES); OtelDoubleCounter(OtelInstrumentDescriptor descriptor) { super(descriptor); @@ -33,7 +38,14 @@ public void add(double value, Attributes attributes) { @Override public void add(double value, Attributes attributes, Context context) { - // FIXME: implement recording + if (value < 0) { + log.warn( + "Counters can only increase. Instrument " + + getDescriptor().getName() + + " has recorded a negative value."); + } else { + // FIXME: implement recording + } } static final class Builder implements DoubleCounterBuilder { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index 1233b24d861..1d2ecab78c1 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -3,16 +3,22 @@ import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofDoubles; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; +import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; import io.opentelemetry.api.metrics.LongHistogramBuilder; import io.opentelemetry.context.Context; import java.util.List; +import java.util.concurrent.TimeUnit; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelDoubleHistogram extends OtelInstrument implements DoubleHistogram { + private static final RatelimitedLogger log = + new RatelimitedLogger( + LoggerFactory.getLogger(OtelDoubleHistogram.class), 5, TimeUnit.MINUTES); OtelDoubleHistogram(OtelInstrumentDescriptor descriptor) { super(descriptor); @@ -30,7 +36,14 @@ public void record(double value, Attributes attributes) { @Override public void record(double value, Attributes attributes, Context context) { - // FIXME: implement recording + if (value < 0) { + log.warn( + "Histograms can only record non-negative values. Instrument " + + getDescriptor().getName() + + " has recorded a negative value."); + } else { + // FIXME: implement recording + } } static final class Builder implements DoubleHistogramBuilder { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java index 33d2eeb48ee..adc65038c02 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java @@ -8,6 +8,10 @@ abstract class OtelInstrument { this.descriptor = descriptor; } + public final OtelInstrumentDescriptor getDescriptor() { + return descriptor; + } + @Override public final boolean equals(Object o) { if (!(o instanceof OtelInstrument)) { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java index 89c92c37d10..d1922bc25f3 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java @@ -25,6 +25,20 @@ final class OtelInstrumentDescriptor { this.unit = unit; } + public String getName() { + return instrumentName; + } + + @Nullable + public String getDescription() { + return description; + } + + @Nullable + public String getUnit() { + return unit; + } + @Override public boolean equals(Object o) { if (!(o instanceof OtelInstrumentDescriptor)) { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java index fae616225c9..d14196fbf47 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java @@ -5,6 +5,7 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleCounterBuilder; import io.opentelemetry.api.metrics.LongCounter; @@ -12,11 +13,15 @@ import io.opentelemetry.api.metrics.ObservableLongCounter; import io.opentelemetry.api.metrics.ObservableLongMeasurement; import io.opentelemetry.context.Context; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelLongCounter extends OtelInstrument implements LongCounter { + private static final RatelimitedLogger log = + new RatelimitedLogger(LoggerFactory.getLogger(OtelLongCounter.class), 5, TimeUnit.MINUTES); OtelLongCounter(OtelInstrumentDescriptor descriptor) { super(descriptor); @@ -34,7 +39,14 @@ public void add(long value, Attributes attributes) { @Override public void add(long value, Attributes attributes, Context context) { - // FIXME: implement recording + if (value < 0) { + log.warn( + "Counters can only increase. Instrument " + + getDescriptor().getName() + + " has recorded a negative value."); + } else { + // FIXME: implement recording + } } static final class Builder implements LongCounterBuilder { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index 6a42e29d806..0c0c53920f8 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -3,15 +3,20 @@ import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofLongs; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; +import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.LongHistogramBuilder; import io.opentelemetry.context.Context; import java.util.List; +import java.util.concurrent.TimeUnit; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelLongHistogram extends OtelInstrument implements LongHistogram { + private static final RatelimitedLogger log = + new RatelimitedLogger(LoggerFactory.getLogger(OtelLongHistogram.class), 5, TimeUnit.MINUTES); OtelLongHistogram(OtelInstrumentDescriptor descriptor) { super(descriptor); @@ -29,7 +34,14 @@ public void record(long value, Attributes attributes) { @Override public void record(long value, Attributes attributes, Context context) { - // FIXME: implement recording + if (value < 0) { + log.warn( + "Histograms can only record non-negative values. Instrument " + + getDescriptor().getName() + + " has recorded a negative value."); + } else { + // FIXME: implement recording + } } static final class Builder implements LongHistogramBuilder { From 3c0341ef7261cbc74d71f065ad87cd2929234792 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Tue, 20 Jan 2026 22:51:30 +0000 Subject: [PATCH 04/14] Validate and capture explicit bucket boundaries --- .../shim/metrics/OtelDoubleHistogram.java | 51 +++++++++++++++++-- .../shim/metrics/OtelLongHistogram.java | 27 ++++++++-- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index 1d2ecab78c1..932eb7a15f6 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -2,6 +2,8 @@ import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofDoubles; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; @@ -9,8 +11,11 @@ import io.opentelemetry.api.metrics.DoubleHistogramBuilder; import io.opentelemetry.api.metrics.LongHistogramBuilder; import io.opentelemetry.context.Context; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.slf4j.LoggerFactory; @@ -20,8 +25,11 @@ final class OtelDoubleHistogram extends OtelInstrument implements DoubleHistogra new RatelimitedLogger( LoggerFactory.getLogger(OtelDoubleHistogram.class), 5, TimeUnit.MINUTES); - OtelDoubleHistogram(OtelInstrumentDescriptor descriptor) { + @Nullable private final List bucketBoundaries; + + OtelDoubleHistogram(OtelInstrumentDescriptor descriptor, List bucketBoundaries) { super(descriptor); + this.bucketBoundaries = bucketBoundaries; } @Override @@ -49,6 +57,8 @@ public void record(double value, Attributes attributes, Context context) { static final class Builder implements DoubleHistogramBuilder { private final OtelInstrumentBuilder instrumentBuilder; + private List bucketBoundaries; + Builder(OtelMeter meter, String instrumentName) { this.instrumentBuilder = ofDoubles(meter, instrumentName, HISTOGRAM); } @@ -67,18 +77,51 @@ public DoubleHistogramBuilder setUnit(String unit) { @Override public DoubleHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketBoundaries) { - // FIXME: implement boundary advice + try { + Objects.requireNonNull(bucketBoundaries, "bucketBoundaries must not be null"); + this.bucketBoundaries = + validateBoundaries(unmodifiableList(new ArrayList<>(bucketBoundaries))); + } catch (NullPointerException | IllegalArgumentException e) { + log.warn("Error setting explicit bucket boundaries advice: " + e.getMessage()); + } return this; } @Override public LongHistogramBuilder ofLongs() { - return new OtelLongHistogram.Builder(instrumentBuilder); + return new OtelLongHistogram.Builder(instrumentBuilder, bucketBoundaries); } @Override public DoubleHistogram build() { - return new OtelDoubleHistogram(instrumentBuilder.toDescriptor()); + return new OtelDoubleHistogram(instrumentBuilder.toDescriptor(), bucketBoundaries); + } + + static List validateBoundaries(List boundaries) { + if (boundaries.isEmpty()) { + return emptyList(); + } + if (boundaries.get(0) == Double.NEGATIVE_INFINITY) { + throw new IllegalArgumentException("invalid bucket boundary: -Inf"); + } + if (boundaries.get(boundaries.size() - 1) == Double.POSITIVE_INFINITY) { + throw new IllegalArgumentException("invalid bucket boundary: +Inf"); + } + Double previousBoundary = null; + for (Double boundary : boundaries) { + if (boundary.isNaN()) { + throw new IllegalArgumentException("invalid bucket boundary: NaN"); + } + if (previousBoundary != null && previousBoundary >= boundary) { + throw new IllegalArgumentException( + "Bucket boundaries must be in increasing order: " + + previousBoundary + + " >= " + + boundary); + } + previousBoundary = boundary; + } + return boundaries; } } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index 0c0c53920f8..d162b284e6c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -1,7 +1,10 @@ package datadog.opentelemetry.shim.metrics; +import static datadog.opentelemetry.shim.metrics.OtelDoubleHistogram.Builder.validateBoundaries; import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofLongs; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; +import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.toList; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; @@ -9,7 +12,9 @@ import io.opentelemetry.api.metrics.LongHistogramBuilder; import io.opentelemetry.context.Context; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.slf4j.LoggerFactory; @@ -18,8 +23,11 @@ final class OtelLongHistogram extends OtelInstrument implements LongHistogram { private static final RatelimitedLogger log = new RatelimitedLogger(LoggerFactory.getLogger(OtelLongHistogram.class), 5, TimeUnit.MINUTES); - OtelLongHistogram(OtelInstrumentDescriptor descriptor) { + @Nullable private final List bucketBoundaries; + + OtelLongHistogram(OtelInstrumentDescriptor descriptor, List bucketBoundaries) { super(descriptor); + this.bucketBoundaries = bucketBoundaries; } @Override @@ -47,8 +55,11 @@ public void record(long value, Attributes attributes, Context context) { static final class Builder implements LongHistogramBuilder { private final OtelInstrumentBuilder instrumentBuilder; - Builder(OtelInstrumentBuilder builder) { + private List bucketBoundaries; + + Builder(OtelInstrumentBuilder builder, List bucketBoundaries) { this.instrumentBuilder = ofLongs(builder, HISTOGRAM); + this.bucketBoundaries = bucketBoundaries; } @Override @@ -65,13 +76,21 @@ public LongHistogramBuilder setUnit(String unit) { @Override public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketBoundaries) { - // FIXME: implement boundary advice + try { + Objects.requireNonNull(bucketBoundaries, "bucketBoundaries must not be null"); + this.bucketBoundaries = + validateBoundaries( + unmodifiableList( + bucketBoundaries.stream().map(Long::doubleValue).collect(toList()))); + } catch (NullPointerException | IllegalArgumentException e) { + log.warn("Error setting explicit bucket boundaries advice: " + e.getMessage()); + } return this; } @Override public LongHistogram build() { - return new OtelLongHistogram(instrumentBuilder.toDescriptor()); + return new OtelLongHistogram(instrumentBuilder.toDescriptor(), bucketBoundaries); } } } From f64d81e0df4d0663709e03434fe62b1e01609fcb Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Wed, 21 Jan 2026 00:21:12 +0000 Subject: [PATCH 05/14] Update OTel metrics helper list --- .../OpenTelemetryMetricsInstrumentation.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java index 41aa14afc7e..b547d85eb49 100644 --- a/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java +++ b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java @@ -65,6 +65,26 @@ public String[] helperClassNames() { "datadog.opentelemetry.shim.metrics.OtelMeter", "datadog.opentelemetry.shim.metrics.OtelMeterBuilder", "datadog.opentelemetry.shim.metrics.OtelMeterProvider", + "datadog.opentelemetry.shim.metrics.OtelInstrumentType", + "datadog.opentelemetry.shim.metrics.OtelInstrumentDescriptor", + "datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder", + "datadog.opentelemetry.shim.metrics.OtelInstrument", + "datadog.opentelemetry.shim.metrics.OtelDoubleCounter", + "datadog.opentelemetry.shim.metrics.OtelDoubleCounter$Builder", + "datadog.opentelemetry.shim.metrics.OtelDoubleGauge", + "datadog.opentelemetry.shim.metrics.OtelDoubleGauge$Builder", + "datadog.opentelemetry.shim.metrics.OtelDoubleHistogram", + "datadog.opentelemetry.shim.metrics.OtelDoubleHistogram$Builder", + "datadog.opentelemetry.shim.metrics.OtelDoubleUpDownCounter", + "datadog.opentelemetry.shim.metrics.OtelDoubleUpDownCounter$Builder", + "datadog.opentelemetry.shim.metrics.OtelLongCounter", + "datadog.opentelemetry.shim.metrics.OtelLongCounter$Builder", + "datadog.opentelemetry.shim.metrics.OtelLongGauge", + "datadog.opentelemetry.shim.metrics.OtelLongGauge$Builder", + "datadog.opentelemetry.shim.metrics.OtelLongHistogram", + "datadog.opentelemetry.shim.metrics.OtelLongHistogram$Builder", + "datadog.opentelemetry.shim.metrics.OtelLongUpDownCounter", + "datadog.opentelemetry.shim.metrics.OtelLongUpDownCounter$Builder", }; } From 9cf99aef0f0e97255021470edb60a33c30a7abc6 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Wed, 28 Jan 2026 20:01:23 +0000 Subject: [PATCH 06/14] Context is unused by our metric mapping --- .../opentelemetry/shim/metrics/OtelDoubleCounter.java | 10 +++++----- .../opentelemetry/shim/metrics/OtelDoubleGauge.java | 6 +++--- .../shim/metrics/OtelDoubleHistogram.java | 10 +++++----- .../shim/metrics/OtelDoubleUpDownCounter.java | 6 +++--- .../opentelemetry/shim/metrics/OtelLongCounter.java | 10 +++++----- .../opentelemetry/shim/metrics/OtelLongGauge.java | 6 +++--- .../opentelemetry/shim/metrics/OtelLongHistogram.java | 10 +++++----- .../shim/metrics/OtelLongUpDownCounter.java | 6 +++--- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java index 6bfef6aa105..ae963953888 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java @@ -33,11 +33,6 @@ public void add(double value) { @Override public void add(double value, Attributes attributes) { - add(value, attributes, Context.current()); - } - - @Override - public void add(double value, Attributes attributes, Context context) { if (value < 0) { log.warn( "Counters can only increase. Instrument " @@ -48,6 +43,11 @@ public void add(double value, Attributes attributes, Context context) { } } + @Override + public void add(double value, Attributes attributes, Context unused) { + add(value, attributes); + } + static final class Builder implements DoubleCounterBuilder { private final OtelInstrumentBuilder instrumentBuilder; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java index f080a040842..6d0b1622e3e 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java @@ -29,12 +29,12 @@ public void set(double value) { @Override public void set(double value, Attributes attributes) { - set(value, attributes, Context.current()); + // FIXME: implement recording } @Override - public void set(double value, Attributes attributes, Context context) { - // FIXME: implement recording + public void set(double value, Attributes attributes, Context unused) { + set(value, attributes); } static final class Builder implements DoubleGaugeBuilder { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index 932eb7a15f6..18a07221a02 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -39,11 +39,6 @@ public void record(double value) { @Override public void record(double value, Attributes attributes) { - record(value, attributes, Context.current()); - } - - @Override - public void record(double value, Attributes attributes, Context context) { if (value < 0) { log.warn( "Histograms can only record non-negative values. Instrument " @@ -54,6 +49,11 @@ public void record(double value, Attributes attributes, Context context) { } } + @Override + public void record(double value, Attributes attributes, Context unused) { + record(value, attributes); + } + static final class Builder implements DoubleHistogramBuilder { private final OtelInstrumentBuilder instrumentBuilder; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java index d9c16cdb98a..c169bc9c567 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java @@ -28,12 +28,12 @@ public void add(double value) { @Override public void add(double value, Attributes attributes) { - add(value, attributes, Context.current()); + // FIXME: implement recording } @Override - public void add(double value, Attributes attributes, Context context) { - // FIXME: implement recording + public void add(double value, Attributes attributes, Context unused) { + add(value, attributes); } static final class Builder implements DoubleUpDownCounterBuilder { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java index d14196fbf47..5033f83bbef 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java @@ -34,11 +34,6 @@ public void add(long value) { @Override public void add(long value, Attributes attributes) { - add(value, attributes, Context.current()); - } - - @Override - public void add(long value, Attributes attributes, Context context) { if (value < 0) { log.warn( "Counters can only increase. Instrument " @@ -49,6 +44,11 @@ public void add(long value, Attributes attributes, Context context) { } } + @Override + public void add(long value, Attributes attributes, Context unused) { + add(value, attributes); + } + static final class Builder implements LongCounterBuilder { private final OtelInstrumentBuilder instrumentBuilder; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java index e812e5b3c6e..94bef4bc75a 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java @@ -28,12 +28,12 @@ public void set(long value) { @Override public void set(long value, Attributes attributes) { - set(value, attributes, Context.current()); + // FIXME: implement recording } @Override - public void set(long value, Attributes attributes, Context context) { - // FIXME: implement recording + public void set(long value, Attributes attributes, Context unused) { + set(value, attributes); } static final class Builder implements LongGaugeBuilder { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index d162b284e6c..fbfa29b620f 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -37,11 +37,6 @@ public void record(long value) { @Override public void record(long value, Attributes attributes) { - record(value, attributes, Context.current()); - } - - @Override - public void record(long value, Attributes attributes, Context context) { if (value < 0) { log.warn( "Histograms can only record non-negative values. Instrument " @@ -52,6 +47,11 @@ public void record(long value, Attributes attributes, Context context) { } } + @Override + public void record(long value, Attributes attributes, Context unused) { + record(value, attributes); + } + static final class Builder implements LongHistogramBuilder { private final OtelInstrumentBuilder instrumentBuilder; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java index 521c4388c5c..5af18be4996 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java @@ -29,12 +29,12 @@ public void add(long value) { @Override public void add(long value, Attributes attributes) { - add(value, attributes, Context.current()); + // FIXME: implement recording } @Override - public void add(long value, Attributes attributes, Context context) { - // FIXME: implement recording + public void add(long value, Attributes attributes, Context unused) { + add(value, attributes); } static final class Builder implements LongUpDownCounterBuilder { From 806f7e44249a61a1fafbf9b445f61f0b97d0f208 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Wed, 28 Jan 2026 17:19:23 +0000 Subject: [PATCH 07/14] Implement OTel metrics storage and aggregation --- .../agent-otel/otel-shim/build.gradle | 1 + .../shim/metrics/OtelDoubleCounter.java | 19 +- .../shim/metrics/OtelDoubleGauge.java | 5 +- .../shim/metrics/OtelDoubleHistogram.java | 31 +-- .../shim/metrics/OtelDoubleUpDownCounter.java | 5 +- .../shim/metrics/OtelInstrumentBuilder.java | 2 +- .../metrics/OtelInstrumentDescriptor.java | 10 +- .../shim/metrics/OtelInstrumentType.java | 8 +- .../shim/metrics/OtelLongCounter.java | 19 +- .../shim/metrics/OtelLongGauge.java | 5 +- .../shim/metrics/OtelLongHistogram.java | 23 +- .../shim/metrics/OtelLongUpDownCounter.java | 5 +- .../shim/metrics/data/OtelAggregator.java | 39 ++++ .../shim/metrics/data/OtelDoublePoint.java | 9 + .../shim/metrics/data/OtelDoubleSum.java | 17 ++ .../shim/metrics/data/OtelDoubleValue.java | 15 ++ .../shim/metrics/data/OtelHistogramPoint.java | 18 ++ .../metrics/data/OtelHistogramSketch.java | 56 +++++ .../shim/metrics/data/OtelLongPoint.java | 9 + .../shim/metrics/data/OtelLongSum.java | 17 ++ .../shim/metrics/data/OtelLongValue.java | 15 ++ .../shim/metrics/data/OtelMetricStorage.java | 203 ++++++++++++++++++ .../shim/metrics/data/OtelPoint.java | 3 + .../OpenTelemetryMetricsInstrumentation.java | 12 ++ 24 files changed, 501 insertions(+), 45 deletions(-) create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoublePoint.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramPoint.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongPoint.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelPoint.java diff --git a/dd-java-agent/agent-otel/otel-shim/build.gradle b/dd-java-agent/agent-otel/otel-shim/build.gradle index 428f19de65d..a077e01236f 100644 --- a/dd-java-agent/agent-otel/otel-shim/build.gradle +++ b/dd-java-agent/agent-otel/otel-shim/build.gradle @@ -7,5 +7,6 @@ dependencies { // minimum OpenTelemetry API version this shim is compatible with compileOnly group: 'io.opentelemetry', name: 'opentelemetry-api', version: '1.47.0' + implementation project(':products:metrics:metrics-api') implementation project(':internal-api') } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java index ae963953888..1ffc55d19f2 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java @@ -5,6 +5,7 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleCounter; @@ -15,15 +16,20 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelDoubleCounter extends OtelInstrument implements DoubleCounter { - private static final RatelimitedLogger log = - new RatelimitedLogger(LoggerFactory.getLogger(OtelDoubleCounter.class), 5, TimeUnit.MINUTES); + private static final Logger LOGGER = LoggerFactory.getLogger(OtelDoubleCounter.class); + private static final RatelimitedLogger RATELIMITED_LOGGER = + new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); + + private final OtelMetricStorage storage; OtelDoubleCounter(OtelInstrumentDescriptor descriptor) { super(descriptor); + this.storage = OtelMetricStorage.newDoubleSumStorage(descriptor); } @Override @@ -34,12 +40,11 @@ public void add(double value) { @Override public void add(double value, Attributes attributes) { if (value < 0) { - log.warn( - "Counters can only increase. Instrument " - + getDescriptor().getName() - + " has recorded a negative value."); + RATELIMITED_LOGGER.warn( + "Counters can only increase. Instrument {} has recorded a negative value.", + getDescriptor().getName()); } else { - // FIXME: implement recording + storage.recordDouble(value, attributes); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java index 6d0b1622e3e..8e3e231c898 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java @@ -5,6 +5,7 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleGauge; import io.opentelemetry.api.metrics.DoubleGaugeBuilder; @@ -17,9 +18,11 @@ @ParametersAreNonnullByDefault final class OtelDoubleGauge extends OtelInstrument implements DoubleGauge { + private final OtelMetricStorage storage; OtelDoubleGauge(OtelInstrumentDescriptor descriptor) { super(descriptor); + this.storage = OtelMetricStorage.newDoubleValueStorage(descriptor); } @Override @@ -29,7 +32,7 @@ public void set(double value) { @Override public void set(double value, Attributes attributes) { - // FIXME: implement recording + storage.recordDouble(value, attributes); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index 18a07221a02..a153e6ec58e 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -2,9 +2,11 @@ import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofDoubles; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; @@ -15,21 +17,21 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelDoubleHistogram extends OtelInstrument implements DoubleHistogram { - private static final RatelimitedLogger log = - new RatelimitedLogger( - LoggerFactory.getLogger(OtelDoubleHistogram.class), 5, TimeUnit.MINUTES); + private static final Logger LOGGER = LoggerFactory.getLogger(OtelDoubleHistogram.class); + private static final RatelimitedLogger RATELIMITED_LOGGER = + new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - @Nullable private final List bucketBoundaries; + private final OtelMetricStorage storage; OtelDoubleHistogram(OtelInstrumentDescriptor descriptor, List bucketBoundaries) { super(descriptor); - this.bucketBoundaries = bucketBoundaries; + this.storage = OtelMetricStorage.newHistogramStorage(descriptor, bucketBoundaries); } @Override @@ -40,12 +42,11 @@ public void record(double value) { @Override public void record(double value, Attributes attributes) { if (value < 0) { - log.warn( - "Histograms can only record non-negative values. Instrument " - + getDescriptor().getName() - + " has recorded a negative value."); + RATELIMITED_LOGGER.warn( + "Histograms can only record non-negative values. Instrument {} has recorded a negative value.", + getDescriptor().getName()); } else { - // FIXME: implement recording + storage.recordDouble(value, attributes); } } @@ -55,12 +56,18 @@ public void record(double value, Attributes attributes, Context unused) { } static final class Builder implements DoubleHistogramBuilder { + private static final List DEFAULT_BOUNDARIES = + asList( + 0d, 5d, 10d, 25d, 50d, 75d, 100d, 250d, 500d, 750d, 1_000d, 2_500d, 5_000d, 7_500d, + 10_000d); + private final OtelInstrumentBuilder instrumentBuilder; private List bucketBoundaries; Builder(OtelMeter meter, String instrumentName) { this.instrumentBuilder = ofDoubles(meter, instrumentName, HISTOGRAM); + this.bucketBoundaries = DEFAULT_BOUNDARIES; } @Override @@ -82,7 +89,7 @@ public DoubleHistogramBuilder setExplicitBucketBoundariesAdvice(List buc this.bucketBoundaries = validateBoundaries(unmodifiableList(new ArrayList<>(bucketBoundaries))); } catch (NullPointerException | IllegalArgumentException e) { - log.warn("Error setting explicit bucket boundaries advice: " + e.getMessage()); + LOGGER.warn("Error setting explicit bucket boundaries advice: {}", e.getMessage()); } return this; } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java index c169bc9c567..fb60edfc119 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java @@ -5,6 +5,7 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleUpDownCounter; import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder; @@ -16,9 +17,11 @@ @ParametersAreNonnullByDefault final class OtelDoubleUpDownCounter extends OtelInstrument implements DoubleUpDownCounter { + private final OtelMetricStorage storage; OtelDoubleUpDownCounter(OtelInstrumentDescriptor descriptor) { super(descriptor); + this.storage = OtelMetricStorage.newDoubleSumStorage(descriptor); } @Override @@ -28,7 +31,7 @@ public void add(double value) { @Override public void add(double value, Attributes attributes) { - // FIXME: implement recording + storage.recordDouble(value, attributes); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java index 870226f9c00..dbd6a7122aa 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java @@ -3,7 +3,6 @@ import javax.annotation.Nullable; final class OtelInstrumentBuilder { - private final OtelMeter meter; private final String instrumentName; private final OtelInstrumentType instrumentType; @@ -81,6 +80,7 @@ void setUnit(String unit) { this.unit = unit; } + /** Captures the instrument details built so far as a descriptor. */ OtelInstrumentDescriptor toDescriptor() { return new OtelInstrumentDescriptor( instrumentName, instrumentType, longValues, description, unit); diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java index d1922bc25f3..c467d8a677d 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java @@ -5,7 +5,7 @@ import javax.annotation.Nullable; /** Uniquely describes an instrument for the Meter that created it. */ -final class OtelInstrumentDescriptor { +public final class OtelInstrumentDescriptor { private final String instrumentName; private final OtelInstrumentType instrumentType; private final boolean longValues; @@ -29,6 +29,14 @@ public String getName() { return instrumentName; } + public OtelInstrumentType getType() { + return instrumentType; + } + + public boolean hasLongValues() { + return longValues; + } + @Nullable public String getDescription() { return description; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java index 338b842ed87..4423655d302 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java @@ -1,8 +1,12 @@ package datadog.opentelemetry.shim.metrics; -enum OtelInstrumentType { +public enum OtelInstrumentType { + // same order as opentelemetry-java COUNTER, UP_DOWN_COUNTER, HISTOGRAM, - GAUGE + OBSERVABLE_COUNTER, + OBSERVABLE_UP_DOWN_COUNTER, + OBSERVABLE_GAUGE, + GAUGE, } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java index 5033f83bbef..65a08504151 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java @@ -5,6 +5,7 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleCounterBuilder; @@ -16,15 +17,20 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelLongCounter extends OtelInstrument implements LongCounter { - private static final RatelimitedLogger log = - new RatelimitedLogger(LoggerFactory.getLogger(OtelLongCounter.class), 5, TimeUnit.MINUTES); + private static final Logger LOGGER = LoggerFactory.getLogger(OtelLongCounter.class); + private static final RatelimitedLogger RATELIMITED_LOGGER = + new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); + + private final OtelMetricStorage storage; OtelLongCounter(OtelInstrumentDescriptor descriptor) { super(descriptor); + this.storage = OtelMetricStorage.newLongSumStorage(descriptor); } @Override @@ -35,12 +41,11 @@ public void add(long value) { @Override public void add(long value, Attributes attributes) { if (value < 0) { - log.warn( - "Counters can only increase. Instrument " - + getDescriptor().getName() - + " has recorded a negative value."); + RATELIMITED_LOGGER.warn( + "Counters can only increase. Instrument {} has recorded a negative value.", + getDescriptor().getName()); } else { - // FIXME: implement recording + storage.recordLong(value, attributes); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java index 94bef4bc75a..32ff596b30a 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java @@ -5,6 +5,7 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongGauge; import io.opentelemetry.api.metrics.LongGaugeBuilder; @@ -16,9 +17,11 @@ @ParametersAreNonnullByDefault final class OtelLongGauge extends OtelInstrument implements LongGauge { + private final OtelMetricStorage storage; OtelLongGauge(OtelInstrumentDescriptor descriptor) { super(descriptor); + this.storage = OtelMetricStorage.newLongValueStorage(descriptor); } @Override @@ -28,7 +31,7 @@ public void set(long value) { @Override public void set(long value, Attributes attributes) { - // FIXME: implement recording + storage.recordLong(value, attributes); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index fbfa29b620f..4da01973850 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -6,6 +6,7 @@ import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongHistogram; @@ -14,20 +15,21 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; -import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ParametersAreNonnullByDefault final class OtelLongHistogram extends OtelInstrument implements LongHistogram { - private static final RatelimitedLogger log = - new RatelimitedLogger(LoggerFactory.getLogger(OtelLongHistogram.class), 5, TimeUnit.MINUTES); + private static final Logger LOGGER = LoggerFactory.getLogger(OtelLongHistogram.class); + private static final RatelimitedLogger RATELIMITED_LOGGER = + new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - @Nullable private final List bucketBoundaries; + private final OtelMetricStorage storage; OtelLongHistogram(OtelInstrumentDescriptor descriptor, List bucketBoundaries) { super(descriptor); - this.bucketBoundaries = bucketBoundaries; + this.storage = OtelMetricStorage.newHistogramStorage(descriptor, bucketBoundaries); } @Override @@ -38,12 +40,11 @@ public void record(long value) { @Override public void record(long value, Attributes attributes) { if (value < 0) { - log.warn( - "Histograms can only record non-negative values. Instrument " - + getDescriptor().getName() - + " has recorded a negative value."); + RATELIMITED_LOGGER.warn( + "Histograms can only record non-negative values. Instrument {} has recorded a negative value.", + getDescriptor().getName()); } else { - // FIXME: implement recording + storage.recordLong(value, attributes); } } @@ -83,7 +84,7 @@ public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketB unmodifiableList( bucketBoundaries.stream().map(Long::doubleValue).collect(toList()))); } catch (NullPointerException | IllegalArgumentException e) { - log.warn("Error setting explicit bucket boundaries advice: " + e.getMessage()); + LOGGER.warn("Error setting explicit bucket boundaries advice: {}", e.getMessage()); } return this; } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java index 5af18be4996..c02cda0213c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java @@ -5,6 +5,7 @@ import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_INSTRUMENT_NAME; import static datadog.opentelemetry.shim.metrics.OtelMeter.NOOP_METER; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder; import io.opentelemetry.api.metrics.LongUpDownCounter; @@ -17,9 +18,11 @@ @ParametersAreNonnullByDefault final class OtelLongUpDownCounter extends OtelInstrument implements LongUpDownCounter { + private final OtelMetricStorage storage; OtelLongUpDownCounter(OtelInstrumentDescriptor descriptor) { super(descriptor); + this.storage = OtelMetricStorage.newLongSumStorage(descriptor); } @Override @@ -29,7 +32,7 @@ public void add(long value) { @Override public void add(long value, Attributes attributes) { - // FIXME: implement recording + storage.recordLong(value, attributes); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java new file mode 100644 index 00000000000..ef566b83c65 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java @@ -0,0 +1,39 @@ +package datadog.opentelemetry.shim.metrics.data; + +public abstract class OtelAggregator { + private volatile boolean empty = true; + + public final boolean isEmpty() { + return empty; + } + + public final void recordDouble(double value) { + doRecordDouble(value); + empty = false; + } + + public final void recordLong(long value) { + doRecordLong(value); + empty = false; + } + + public final OtelPoint collect() { + return doCollect(false); + } + + public final OtelPoint collectAndReset() { + OtelPoint point = doCollect(true); + empty = true; + return point; + } + + protected void doRecordDouble(double value) { + throw new UnsupportedOperationException(); + } + + protected void doRecordLong(long value) { + throw new UnsupportedOperationException(); + } + + protected abstract OtelPoint doCollect(boolean reset); +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoublePoint.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoublePoint.java new file mode 100644 index 00000000000..0404543b538 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoublePoint.java @@ -0,0 +1,9 @@ +package datadog.opentelemetry.shim.metrics.data; + +public final class OtelDoublePoint extends OtelPoint { + public final double value; + + OtelDoublePoint(double value) { + this.value = value; + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java new file mode 100644 index 00000000000..53dd37b4082 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java @@ -0,0 +1,17 @@ +package datadog.opentelemetry.shim.metrics.data; + +import java.util.concurrent.atomic.DoubleAdder; + +final class OtelDoubleSum extends OtelAggregator { + private final DoubleAdder total = new DoubleAdder(); + + @Override + protected void doRecordDouble(double value) { + total.add(value); + } + + @Override + protected OtelPoint doCollect(boolean reset) { + return new OtelDoublePoint(reset ? total.sumThenReset() : total.sum()); + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java new file mode 100644 index 00000000000..6ae48a4dd0b --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java @@ -0,0 +1,15 @@ +package datadog.opentelemetry.shim.metrics.data; + +final class OtelDoubleValue extends OtelAggregator { + private volatile double value; + + @Override + protected void doRecordDouble(double value) { + this.value = value; + } + + @Override + protected OtelPoint doCollect(boolean reset) { + return new OtelDoublePoint(value); + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramPoint.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramPoint.java new file mode 100644 index 00000000000..efa6cdd3c7a --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramPoint.java @@ -0,0 +1,18 @@ +package datadog.opentelemetry.shim.metrics.data; + +import java.util.List; + +public final class OtelHistogramPoint extends OtelPoint { + public final double count; + public final List bucketBoundaries; + public final List bucketCounts; + public final double sum; + + OtelHistogramPoint( + double count, List bucketBoundaries, List bucketCounts, double sum) { + this.count = count; + this.bucketBoundaries = bucketBoundaries; + this.bucketCounts = bucketCounts; + this.sum = sum; + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java new file mode 100644 index 00000000000..b60986b733f --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java @@ -0,0 +1,56 @@ +package datadog.opentelemetry.shim.metrics.data; + +import datadog.metrics.api.Histogram; +import java.util.List; + +final class OtelHistogramSketch extends OtelAggregator { + private final Histogram histogram; + private volatile double totalSum; + + OtelHistogramSketch(List bucketBoundaries) { + this.histogram = Histogram.newHistogram(bucketBoundaries); + } + + @Override + protected void doRecordDouble(double value) { + synchronized (histogram) { + histogram.accept(value); + totalSum += value; + } + } + + @Override + protected void doRecordLong(long value) { + doRecordDouble(fixedPrecision(value)); + } + + @Override + protected OtelPoint doCollect(boolean reset) { + double count; + List binBoundaries; + List binCounts; + double sum; + synchronized (histogram) { + count = histogram.getCount(); + binBoundaries = histogram.getBinBoundaries(); + binCounts = histogram.getBinCounts(); + sum = totalSum; + if (reset) { + histogram.clear(); + totalSum = 0; + } + } + return new OtelHistogramPoint(count, binBoundaries, binCounts, sum); + } + + /** Truncate IEEE-754 floating-point value to 10 bits precision. */ + private static double fixedPrecision(long value) { + long bits = Double.doubleToRawLongBits(value); + // the mask include 1 bit sign 11 bits exponent (0xfff) + // then we filter the mantissa to 10bits (0xff8) (9 bits as it has implicit value of 1) + // 10 bits precision (any value will be +/- 1/1024) + // https://en.wikipedia.org/wiki/Double-precision_floating-point_format + bits &= 0xfffff80000000000L; + return Double.longBitsToDouble(bits); + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongPoint.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongPoint.java new file mode 100644 index 00000000000..453f60eab31 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongPoint.java @@ -0,0 +1,9 @@ +package datadog.opentelemetry.shim.metrics.data; + +public final class OtelLongPoint extends OtelPoint { + public final long value; + + OtelLongPoint(long value) { + this.value = value; + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java new file mode 100644 index 00000000000..3f8a9c59514 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java @@ -0,0 +1,17 @@ +package datadog.opentelemetry.shim.metrics.data; + +import java.util.concurrent.atomic.LongAdder; + +final class OtelLongSum extends OtelAggregator { + private final LongAdder total = new LongAdder(); + + @Override + protected void doRecordLong(long value) { + total.add(value); + } + + @Override + protected OtelPoint doCollect(boolean reset) { + return new OtelLongPoint(reset ? total.sumThenReset() : total.sum()); + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java new file mode 100644 index 00000000000..3b16fb52e20 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java @@ -0,0 +1,15 @@ +package datadog.opentelemetry.shim.metrics.data; + +final class OtelLongValue extends OtelAggregator { + private volatile long value; + + @Override + protected void doRecordLong(long value) { + this.value = value; + } + + @Override + protected OtelPoint doCollect(boolean reset) { + return new OtelLongPoint(value); + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java new file mode 100644 index 00000000000..c9805d16e31 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java @@ -0,0 +1,203 @@ +package datadog.opentelemetry.shim.metrics.data; + +import datadog.opentelemetry.shim.metrics.OtelInstrumentDescriptor; +import datadog.trace.api.Config; +import datadog.trace.api.config.OtlpConfig; +import datadog.trace.relocate.api.RatelimitedLogger; +import io.opentelemetry.api.common.Attributes; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** Stores and aggregates metrics data for a given instrument. */ +public final class OtelMetricStorage { + private static final Logger LOGGER = LoggerFactory.getLogger(OtelMetricStorage.class); + private static final RatelimitedLogger RATELIMITED_LOGGER = + new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); + + private static final int DEFAULT_MAX_CARDINALITY = 2_000; + + private static final Attributes CARDINALITY_OVERFLOW = + Attributes.builder().put("otel.metric.overflow", true).build(); + + private static final boolean RESET_ON_COLLECT = + Config.get().getOtlpMetricsTemporalityPreference() == OtlpConfig.Temporality.DELTA; + + private final OtelInstrumentDescriptor descriptor; + private final Function aggregatorSupplier; + private volatile Recording currentRecording; + + // only used with DELTA temporality + private Recording previousRecording; + + private OtelMetricStorage( + OtelInstrumentDescriptor descriptor, Supplier aggregatorSupplier) { + this.descriptor = descriptor; + this.aggregatorSupplier = unused -> aggregatorSupplier.get(); + this.currentRecording = new Recording(); + if (RESET_ON_COLLECT) { + this.previousRecording = new Recording(); + } + } + + public static OtelMetricStorage newDoubleSumStorage(OtelInstrumentDescriptor descriptor) { + return new OtelMetricStorage(descriptor, OtelDoubleSum::new); + } + + public static OtelMetricStorage newDoubleValueStorage(OtelInstrumentDescriptor descriptor) { + return new OtelMetricStorage(descriptor, OtelDoubleValue::new); + } + + public static OtelMetricStorage newLongSumStorage(OtelInstrumentDescriptor descriptor) { + return new OtelMetricStorage(descriptor, OtelLongSum::new); + } + + public static OtelMetricStorage newLongValueStorage(OtelInstrumentDescriptor descriptor) { + return new OtelMetricStorage(descriptor, OtelLongValue::new); + } + + public static OtelMetricStorage newHistogramStorage( + OtelInstrumentDescriptor descriptor, List bucketBoundaries) { + return new OtelMetricStorage(descriptor, () -> new OtelHistogramSketch(bucketBoundaries)); + } + + public void recordLong(long value, Attributes attributes) { + Recording recording = acquireRecordingForWrite(); + try { + aggregator(recording.aggregators, attributes).recordLong(value); + } finally { + releaseRecordingAfterWrite(recording); + } + } + + public void recordDouble(double value, Attributes attributes) { + Recording recording = acquireRecordingForWrite(); + try { + aggregator(recording.aggregators, attributes).recordDouble(value); + } finally { + releaseRecordingAfterWrite(recording); + } + } + + private OtelAggregator aggregator( + Map aggregators, Attributes attributes) { + Objects.requireNonNull(attributes, "attributes"); + OtelAggregator aggregator = aggregators.get(attributes); + if (null != aggregator) { + return aggregator; + } + if (aggregators.size() >= DEFAULT_MAX_CARDINALITY) { + RATELIMITED_LOGGER.warn( + "Instrument {} has exceeded the maximum allowed cardinality ({}).", + descriptor.getName(), + DEFAULT_MAX_CARDINALITY); + attributes = CARDINALITY_OVERFLOW; // write data to overflow + } + return aggregators.computeIfAbsent(attributes, aggregatorSupplier); + } + + public void collect(BiConsumer consumer) { + if (RESET_ON_COLLECT) { + doCollectAndReset(consumer); + } else { + doCollect(consumer); + } + } + + /** Collect data for CUMULATIVE temporality, keeping aggregators for future writes. */ + private void doCollect(BiConsumer consumer) { + // no need to hold writers back if we are not resetting metrics on collect + currentRecording.aggregators.forEach((k, v) -> consumer.accept(k, v.collect())); + } + + /** + * Collect data for DELTA temporality, resetting aggregators for future writes. + * + *

Each collect request toggles between two groups of aggregators: current / previous. + */ + private void doCollectAndReset(BiConsumer consumer) { + + // capture _current_ recording for collection, its aggregators will be reset at the end + final Recording recording = currentRecording; + + // publish fresh recording for new writers, using aggregators from _previous_ recording + currentRecording = new Recording(previousRecording); + + // notify writers that the captured recording is about to be reset + ACTIVITY.addAndGet(recording, RESET_PENDING); + while (recording.activity > 1) { + Thread.yield(); // other threads are still writing to this recording + } + + Map aggregators = recording.aggregators; + + // avoid churn: only remove empty aggregators if we're over cardinality + if (aggregators.size() >= DEFAULT_MAX_CARDINALITY) { + aggregators.values().removeIf(OtelAggregator::isEmpty); + } + + aggregators.forEach( + (k, v) -> { + if (!v.isEmpty()) { + consumer.accept(k, v.collectAndReset()); + } + }); + + previousRecording = recording; + } + + private Recording acquireRecordingForWrite() { + if (RESET_ON_COLLECT) { + // busy loop to limit impact on caller + while (true) { + final Recording recording = currentRecording; + // atomically notify collector of write activity and check state + if ((ACTIVITY.addAndGet(recording, WRITER) & RESET_PENDING) == 0) { + return recording; + } else { + // reset pending: rollback and check again for a fresh recording + ACTIVITY.addAndGet(recording, -WRITER); + } + } + } else { + return currentRecording; + } + } + + private void releaseRecordingAfterWrite(Recording recording) { + if (RESET_ON_COLLECT) { + ACTIVITY.addAndGet(recording, -WRITER); + } + } + + static final AtomicIntegerFieldUpdater ACTIVITY = + AtomicIntegerFieldUpdater.newUpdater(Recording.class, "activity"); + + // first activity bit indicates if this recording is about to be reset + private static final int RESET_PENDING = 1; + + // the other activity bits indicate how many threads are writing to it + private static final int WRITER = 2; + + static final class Recording { + final Map aggregators; + + transient volatile int activity; + + Recording() { + this.aggregators = new ConcurrentHashMap<>(); + } + + Recording(Recording previous) { + this.aggregators = previous.aggregators; + } + } +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelPoint.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelPoint.java new file mode 100644 index 00000000000..1c0a8afd070 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelPoint.java @@ -0,0 +1,3 @@ +package datadog.opentelemetry.shim.metrics.data; + +public abstract class OtelPoint {} diff --git a/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java index b547d85eb49..e20a52adcb0 100644 --- a/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java +++ b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java @@ -69,6 +69,18 @@ public String[] helperClassNames() { "datadog.opentelemetry.shim.metrics.OtelInstrumentDescriptor", "datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder", "datadog.opentelemetry.shim.metrics.OtelInstrument", + "datadog.opentelemetry.shim.metrics.data.OtelPoint", + "datadog.opentelemetry.shim.metrics.data.OtelAggregator", + "datadog.opentelemetry.shim.metrics.data.OtelDoublePoint", + "datadog.opentelemetry.shim.metrics.data.OtelDoubleSum", + "datadog.opentelemetry.shim.metrics.data.OtelDoubleValue", + "datadog.opentelemetry.shim.metrics.data.OtelHistogramPoint", + "datadog.opentelemetry.shim.metrics.data.OtelHistogramSketch", + "datadog.opentelemetry.shim.metrics.data.OtelLongPoint", + "datadog.opentelemetry.shim.metrics.data.OtelLongSum", + "datadog.opentelemetry.shim.metrics.data.OtelLongValue", + "datadog.opentelemetry.shim.metrics.data.OtelMetricStorage", + "datadog.opentelemetry.shim.metrics.data.OtelMetricStorage$Recording", "datadog.opentelemetry.shim.metrics.OtelDoubleCounter", "datadog.opentelemetry.shim.metrics.OtelDoubleCounter$Builder", "datadog.opentelemetry.shim.metrics.OtelDoubleGauge", From 0e362ed2c9d9793c4f42aaa105edd32218f06022 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 5 Feb 2026 17:48:27 +0000 Subject: [PATCH 08/14] Track metrics storage per-meter --- .../shim/metrics/OtelDoubleCounter.java | 11 ++++------ .../shim/metrics/OtelDoubleGauge.java | 8 +++----- .../shim/metrics/OtelDoubleHistogram.java | 14 +++++-------- .../shim/metrics/OtelDoubleUpDownCounter.java | 8 +++----- .../shim/metrics/OtelInstrument.java | 18 ++++++++--------- .../shim/metrics/OtelInstrumentBuilder.java | 11 ++++++---- .../shim/metrics/OtelLongCounter.java | 11 ++++------ .../shim/metrics/OtelLongGauge.java | 8 +++----- .../shim/metrics/OtelLongHistogram.java | 14 +++++-------- .../shim/metrics/OtelLongUpDownCounter.java | 8 +++----- .../opentelemetry/shim/metrics/OtelMeter.java | 20 +++++++++++++++++++ .../shim/metrics/OtelMeterProvider.java | 7 +++++++ .../shim/metrics/data/OtelAggregator.java | 18 ++++++++--------- .../shim/metrics/data/OtelDoubleSum.java | 4 ++-- .../shim/metrics/data/OtelDoubleValue.java | 4 ++-- .../metrics/data/OtelHistogramSketch.java | 6 +++--- .../shim/metrics/data/OtelLongSum.java | 4 ++-- .../shim/metrics/data/OtelLongValue.java | 4 ++-- .../shim/metrics/data/OtelMetricStorage.java | 4 ++++ 19 files changed, 96 insertions(+), 86 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java index 1ffc55d19f2..f5e2653062f 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java @@ -25,11 +25,8 @@ final class OtelDoubleCounter extends OtelInstrument implements DoubleCounter { private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - private final OtelMetricStorage storage; - - OtelDoubleCounter(OtelInstrumentDescriptor descriptor) { - super(descriptor); - this.storage = OtelMetricStorage.newDoubleSumStorage(descriptor); + OtelDoubleCounter(OtelInstrumentBuilder builder) { + super(builder.build(OtelMetricStorage::newDoubleSumStorage)); } @Override @@ -42,7 +39,7 @@ public void add(double value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Counters can only increase. Instrument {} has recorded a negative value.", - getDescriptor().getName()); + storage.getDescriptor().getName()); } else { storage.recordDouble(value, attributes); } @@ -74,7 +71,7 @@ public DoubleCounterBuilder setUnit(String unit) { @Override public DoubleCounter build() { - return new OtelDoubleCounter(instrumentBuilder.toDescriptor()); + return new OtelDoubleCounter(instrumentBuilder); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java index 8e3e231c898..f4ef8be2555 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java @@ -18,11 +18,9 @@ @ParametersAreNonnullByDefault final class OtelDoubleGauge extends OtelInstrument implements DoubleGauge { - private final OtelMetricStorage storage; - OtelDoubleGauge(OtelInstrumentDescriptor descriptor) { - super(descriptor); - this.storage = OtelMetricStorage.newDoubleValueStorage(descriptor); + OtelDoubleGauge(OtelInstrumentBuilder builder) { + super(builder.build(OtelMetricStorage::newDoubleValueStorage)); } @Override @@ -66,7 +64,7 @@ public LongGaugeBuilder ofLongs() { @Override public DoubleGauge build() { - return new OtelDoubleGauge(instrumentBuilder.toDescriptor()); + return new OtelDoubleGauge(instrumentBuilder); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index a153e6ec58e..6a7164e3b6e 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -2,11 +2,11 @@ import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofDoubles; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; +import static datadog.opentelemetry.shim.metrics.data.OtelMetricStorage.newHistogramStorage; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; -import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; @@ -27,11 +27,8 @@ final class OtelDoubleHistogram extends OtelInstrument implements DoubleHistogra private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - private final OtelMetricStorage storage; - - OtelDoubleHistogram(OtelInstrumentDescriptor descriptor, List bucketBoundaries) { - super(descriptor); - this.storage = OtelMetricStorage.newHistogramStorage(descriptor, bucketBoundaries); + OtelDoubleHistogram(OtelInstrumentBuilder builder, List bucketBoundaries) { + super(builder.build(descriptor -> newHistogramStorage(descriptor, bucketBoundaries))); } @Override @@ -44,7 +41,7 @@ public void record(double value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Histograms can only record non-negative values. Instrument {} has recorded a negative value.", - getDescriptor().getName()); + storage.getDescriptor().getName()); } else { storage.recordDouble(value, attributes); } @@ -62,7 +59,6 @@ static final class Builder implements DoubleHistogramBuilder { 10_000d); private final OtelInstrumentBuilder instrumentBuilder; - private List bucketBoundaries; Builder(OtelMeter meter, String instrumentName) { @@ -101,7 +97,7 @@ public LongHistogramBuilder ofLongs() { @Override public DoubleHistogram build() { - return new OtelDoubleHistogram(instrumentBuilder.toDescriptor(), bucketBoundaries); + return new OtelDoubleHistogram(instrumentBuilder, bucketBoundaries); } static List validateBoundaries(List boundaries) { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java index fb60edfc119..0ca6c284a3c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java @@ -17,11 +17,9 @@ @ParametersAreNonnullByDefault final class OtelDoubleUpDownCounter extends OtelInstrument implements DoubleUpDownCounter { - private final OtelMetricStorage storage; - OtelDoubleUpDownCounter(OtelInstrumentDescriptor descriptor) { - super(descriptor); - this.storage = OtelMetricStorage.newDoubleSumStorage(descriptor); + OtelDoubleUpDownCounter(OtelInstrumentBuilder builder) { + super(builder.build(OtelMetricStorage::newDoubleSumStorage)); } @Override @@ -60,7 +58,7 @@ public DoubleUpDownCounterBuilder setUnit(String unit) { @Override public DoubleUpDownCounter build() { - return new OtelDoubleUpDownCounter(instrumentBuilder.toDescriptor()); + return new OtelDoubleUpDownCounter(instrumentBuilder); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java index adc65038c02..3b7e727550d 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrument.java @@ -1,15 +1,13 @@ package datadog.opentelemetry.shim.metrics; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; + /** Ensure all instruments implement the same equivalency. */ abstract class OtelInstrument { - private final OtelInstrumentDescriptor descriptor; - - OtelInstrument(OtelInstrumentDescriptor descriptor) { - this.descriptor = descriptor; - } + final OtelMetricStorage storage; - public final OtelInstrumentDescriptor getDescriptor() { - return descriptor; + OtelInstrument(OtelMetricStorage storage) { + this.storage = storage; } @Override @@ -19,16 +17,16 @@ public final boolean equals(Object o) { } OtelInstrument that = (OtelInstrument) o; - return descriptor.equals(that.descriptor); + return storage.getDescriptor().equals(that.storage.getDescriptor()); } @Override public final int hashCode() { - return descriptor.hashCode(); + return storage.getDescriptor().hashCode(); } @Override public final String toString() { - return getClass().getSimpleName() + "{descriptor=" + descriptor + '}'; + return getClass().getSimpleName() + "{descriptor=" + storage.getDescriptor() + '}'; } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java index dbd6a7122aa..b8fc6c29827 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java @@ -1,5 +1,7 @@ package datadog.opentelemetry.shim.metrics; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; +import java.util.function.Function; import javax.annotation.Nullable; final class OtelInstrumentBuilder { @@ -80,9 +82,10 @@ void setUnit(String unit) { this.unit = unit; } - /** Captures the instrument details built so far as a descriptor. */ - OtelInstrumentDescriptor toDescriptor() { - return new OtelInstrumentDescriptor( - instrumentName, instrumentType, longValues, description, unit); + OtelMetricStorage build(Function storageFactory) { + OtelInstrumentDescriptor descriptor = + new OtelInstrumentDescriptor(instrumentName, instrumentType, longValues, description, unit); + + return meter.registerStorage(descriptor, storageFactory); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java index 65a08504151..bd33c758962 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java @@ -26,11 +26,8 @@ final class OtelLongCounter extends OtelInstrument implements LongCounter { private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - private final OtelMetricStorage storage; - - OtelLongCounter(OtelInstrumentDescriptor descriptor) { - super(descriptor); - this.storage = OtelMetricStorage.newLongSumStorage(descriptor); + OtelLongCounter(OtelInstrumentBuilder builder) { + super(builder.build(OtelMetricStorage::newLongSumStorage)); } @Override @@ -43,7 +40,7 @@ public void add(long value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Counters can only increase. Instrument {} has recorded a negative value.", - getDescriptor().getName()); + storage.getDescriptor().getName()); } else { storage.recordLong(value, attributes); } @@ -80,7 +77,7 @@ public DoubleCounterBuilder ofDoubles() { @Override public LongCounter build() { - return new OtelLongCounter(instrumentBuilder.toDescriptor()); + return new OtelLongCounter(instrumentBuilder); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java index 32ff596b30a..0219606e11e 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java @@ -17,11 +17,9 @@ @ParametersAreNonnullByDefault final class OtelLongGauge extends OtelInstrument implements LongGauge { - private final OtelMetricStorage storage; - OtelLongGauge(OtelInstrumentDescriptor descriptor) { - super(descriptor); - this.storage = OtelMetricStorage.newLongValueStorage(descriptor); + OtelLongGauge(OtelInstrumentBuilder builder) { + super(builder.build(OtelMetricStorage::newLongValueStorage)); } @Override @@ -60,7 +58,7 @@ public LongGaugeBuilder setUnit(String unit) { @Override public LongGauge build() { - return new OtelLongGauge(instrumentBuilder.toDescriptor()); + return new OtelLongGauge(instrumentBuilder); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index 4da01973850..7ad74901609 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -3,10 +3,10 @@ import static datadog.opentelemetry.shim.metrics.OtelDoubleHistogram.Builder.validateBoundaries; import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofLongs; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; +import static datadog.opentelemetry.shim.metrics.data.OtelMetricStorage.newHistogramStorage; import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; -import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongHistogram; @@ -25,11 +25,8 @@ final class OtelLongHistogram extends OtelInstrument implements LongHistogram { private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - private final OtelMetricStorage storage; - - OtelLongHistogram(OtelInstrumentDescriptor descriptor, List bucketBoundaries) { - super(descriptor); - this.storage = OtelMetricStorage.newHistogramStorage(descriptor, bucketBoundaries); + OtelLongHistogram(OtelInstrumentBuilder builder, List bucketBoundaries) { + super(builder.build(descriptor -> newHistogramStorage(descriptor, bucketBoundaries))); } @Override @@ -42,7 +39,7 @@ public void record(long value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Histograms can only record non-negative values. Instrument {} has recorded a negative value.", - getDescriptor().getName()); + storage.getDescriptor().getName()); } else { storage.recordLong(value, attributes); } @@ -55,7 +52,6 @@ public void record(long value, Attributes attributes, Context unused) { static final class Builder implements LongHistogramBuilder { private final OtelInstrumentBuilder instrumentBuilder; - private List bucketBoundaries; Builder(OtelInstrumentBuilder builder, List bucketBoundaries) { @@ -91,7 +87,7 @@ public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketB @Override public LongHistogram build() { - return new OtelLongHistogram(instrumentBuilder.toDescriptor(), bucketBoundaries); + return new OtelLongHistogram(instrumentBuilder, bucketBoundaries); } } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java index c02cda0213c..61cf77a400c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java @@ -18,11 +18,9 @@ @ParametersAreNonnullByDefault final class OtelLongUpDownCounter extends OtelInstrument implements LongUpDownCounter { - private final OtelMetricStorage storage; - OtelLongUpDownCounter(OtelInstrumentDescriptor descriptor) { - super(descriptor); - this.storage = OtelMetricStorage.newLongSumStorage(descriptor); + OtelLongUpDownCounter(OtelInstrumentBuilder builder) { + super(builder.build(OtelMetricStorage::newLongSumStorage)); } @Override @@ -66,7 +64,7 @@ public DoubleUpDownCounterBuilder ofDoubles() { @Override public LongUpDownCounter build() { - return new OtelLongUpDownCounter(instrumentBuilder.toDescriptor()); + return new OtelLongUpDownCounter(instrumentBuilder); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java index 39345aef96e..214da5b0e70 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java @@ -1,6 +1,9 @@ package datadog.opentelemetry.shim.metrics; import datadog.opentelemetry.shim.OtelInstrumentationScope; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; +import datadog.opentelemetry.shim.metrics.data.OtelPoint; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.BatchCallback; import io.opentelemetry.api.metrics.DoubleGaugeBuilder; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; @@ -9,6 +12,10 @@ import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.api.metrics.ObservableMeasurement; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.regex.Pattern; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; @@ -27,6 +34,9 @@ final class OtelMeter implements Meter { private final OtelInstrumentationScope instrumentationScope; + private final Map storage = + new ConcurrentHashMap<>(); + OtelMeter(OtelInstrumentationScope instrumentationScope) { this.instrumentationScope = instrumentationScope; } @@ -77,6 +87,16 @@ public String toString() { return "OtelMeter{instrumentationScope=" + instrumentationScope + "}"; } + OtelMetricStorage registerStorage( + OtelInstrumentDescriptor descriptor, + Function storageFactory) { + return storage.computeIfAbsent(descriptor, storageFactory); + } + + void collect(BiConsumer consumer) { + storage.values().forEach(storage -> storage.collect(consumer)); + } + private static boolean validInstrumentName(@Nullable String instrumentName) { if (instrumentName != null && VALID_INSTRUMENT_NAME_PATTERN.matcher(instrumentName).matches()) { return true; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java index fa2b641bbdb..0ad9af986bb 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java @@ -1,12 +1,15 @@ package datadog.opentelemetry.shim.metrics; import datadog.opentelemetry.shim.OtelInstrumentationScope; +import datadog.opentelemetry.shim.metrics.data.OtelPoint; import datadog.trace.util.Strings; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterBuilder; import io.opentelemetry.api.metrics.MeterProvider; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.slf4j.Logger; @@ -32,6 +35,10 @@ public MeterBuilder meterBuilder(String instrumentationScopeName) { return new OtelMeterBuilder(this, instrumentationScopeName); } + public void collect(BiConsumer consumer) { + meters.values().forEach(meter -> meter.collect(consumer)); + } + OtelMeter getMeterShim( String instrumentationScopeName, @Nullable String instrumentationScopeVersion, diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java index ef566b83c65..d3bbc0a1250 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java @@ -1,39 +1,39 @@ package datadog.opentelemetry.shim.metrics.data; -public abstract class OtelAggregator { +abstract class OtelAggregator { private volatile boolean empty = true; - public final boolean isEmpty() { + final boolean isEmpty() { return empty; } - public final void recordDouble(double value) { + final void recordDouble(double value) { doRecordDouble(value); empty = false; } - public final void recordLong(long value) { + final void recordLong(long value) { doRecordLong(value); empty = false; } - public final OtelPoint collect() { + final OtelPoint collect() { return doCollect(false); } - public final OtelPoint collectAndReset() { + final OtelPoint collectAndReset() { OtelPoint point = doCollect(true); empty = true; return point; } - protected void doRecordDouble(double value) { + void doRecordDouble(double value) { throw new UnsupportedOperationException(); } - protected void doRecordLong(long value) { + void doRecordLong(long value) { throw new UnsupportedOperationException(); } - protected abstract OtelPoint doCollect(boolean reset); + abstract OtelPoint doCollect(boolean reset); } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java index 53dd37b4082..d1dd1ae6a62 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleSum.java @@ -6,12 +6,12 @@ final class OtelDoubleSum extends OtelAggregator { private final DoubleAdder total = new DoubleAdder(); @Override - protected void doRecordDouble(double value) { + void doRecordDouble(double value) { total.add(value); } @Override - protected OtelPoint doCollect(boolean reset) { + OtelPoint doCollect(boolean reset) { return new OtelDoublePoint(reset ? total.sumThenReset() : total.sum()); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java index 6ae48a4dd0b..9bf5f8e6b98 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelDoubleValue.java @@ -4,12 +4,12 @@ final class OtelDoubleValue extends OtelAggregator { private volatile double value; @Override - protected void doRecordDouble(double value) { + void doRecordDouble(double value) { this.value = value; } @Override - protected OtelPoint doCollect(boolean reset) { + OtelPoint doCollect(boolean reset) { return new OtelDoublePoint(value); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java index b60986b733f..7fc0f182bac 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelHistogramSketch.java @@ -12,7 +12,7 @@ final class OtelHistogramSketch extends OtelAggregator { } @Override - protected void doRecordDouble(double value) { + void doRecordDouble(double value) { synchronized (histogram) { histogram.accept(value); totalSum += value; @@ -20,12 +20,12 @@ protected void doRecordDouble(double value) { } @Override - protected void doRecordLong(long value) { + void doRecordLong(long value) { doRecordDouble(fixedPrecision(value)); } @Override - protected OtelPoint doCollect(boolean reset) { + OtelPoint doCollect(boolean reset) { double count; List binBoundaries; List binCounts; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java index 3f8a9c59514..91da9c054bb 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongSum.java @@ -6,12 +6,12 @@ final class OtelLongSum extends OtelAggregator { private final LongAdder total = new LongAdder(); @Override - protected void doRecordLong(long value) { + void doRecordLong(long value) { total.add(value); } @Override - protected OtelPoint doCollect(boolean reset) { + OtelPoint doCollect(boolean reset) { return new OtelLongPoint(reset ? total.sumThenReset() : total.sum()); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java index 3b16fb52e20..31e1719a89c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelLongValue.java @@ -4,12 +4,12 @@ final class OtelLongValue extends OtelAggregator { private volatile long value; @Override - protected void doRecordLong(long value) { + void doRecordLong(long value) { this.value = value; } @Override - protected OtelPoint doCollect(boolean reset) { + OtelPoint doCollect(boolean reset) { return new OtelLongPoint(value); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java index c9805d16e31..89b90cf5409 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java @@ -69,6 +69,10 @@ public static OtelMetricStorage newHistogramStorage( return new OtelMetricStorage(descriptor, () -> new OtelHistogramSketch(bucketBoundaries)); } + public OtelInstrumentDescriptor getDescriptor() { + return descriptor; + } + public void recordLong(long value, Attributes attributes) { Recording recording = acquireRecordingForWrite(); try { From ed5f068cd061e4f68bdc02f3a1796bc28313a7a5 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 5 Feb 2026 20:47:22 +0000 Subject: [PATCH 09/14] Introduce visitor API for exporting metrics --- .../opentelemetry/shim/metrics/OtelMeter.java | 8 +++--- .../shim/metrics/OtelMeterProvider.java | 8 +++--- .../shim/metrics/data/OtelMetricStorage.java | 25 +++++++++++-------- .../metrics/export/OtelInstrumentVisitor.java | 9 +++++++ .../shim/metrics/export/OtelMeterVisitor.java | 8 ++++++ .../metrics/export/OtelMetricVisitor.java | 8 ++++++ .../OpenTelemetryMetricsInstrumentation.java | 3 +++ 7 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelInstrumentVisitor.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMeterVisitor.java create mode 100644 dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMetricVisitor.java diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java index 214da5b0e70..6213d9084f4 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeter.java @@ -2,8 +2,7 @@ import datadog.opentelemetry.shim.OtelInstrumentationScope; import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; -import datadog.opentelemetry.shim.metrics.data.OtelPoint; -import io.opentelemetry.api.common.Attributes; +import datadog.opentelemetry.shim.metrics.export.OtelMeterVisitor; import io.opentelemetry.api.metrics.BatchCallback; import io.opentelemetry.api.metrics.DoubleGaugeBuilder; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; @@ -14,7 +13,6 @@ import io.opentelemetry.api.metrics.ObservableMeasurement; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; import java.util.function.Function; import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -93,8 +91,8 @@ OtelMetricStorage registerStorage( return storage.computeIfAbsent(descriptor, storageFactory); } - void collect(BiConsumer consumer) { - storage.values().forEach(storage -> storage.collect(consumer)); + void collect(OtelMeterVisitor visitor) { + storage.forEach((descriptor, storage) -> storage.collect(visitor.visitInstrument(descriptor))); } private static boolean validInstrumentName(@Nullable String instrumentName) { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java index 0ad9af986bb..c133eb9368d 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java @@ -1,15 +1,13 @@ package datadog.opentelemetry.shim.metrics; import datadog.opentelemetry.shim.OtelInstrumentationScope; -import datadog.opentelemetry.shim.metrics.data.OtelPoint; +import datadog.opentelemetry.shim.metrics.export.OtelMetricVisitor; import datadog.trace.util.Strings; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterBuilder; import io.opentelemetry.api.metrics.MeterProvider; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import org.slf4j.Logger; @@ -35,8 +33,8 @@ public MeterBuilder meterBuilder(String instrumentationScopeName) { return new OtelMeterBuilder(this, instrumentationScopeName); } - public void collect(BiConsumer consumer) { - meters.values().forEach(meter -> meter.collect(consumer)); + public void collect(OtelMetricVisitor visitor) { + meters.forEach((scope, meter) -> meter.collect(visitor.visitMeter(scope))); } OtelMeter getMeterShim( diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java index 89b90cf5409..0312b27cb6e 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java @@ -1,6 +1,7 @@ package datadog.opentelemetry.shim.metrics.data; import datadog.opentelemetry.shim.metrics.OtelInstrumentDescriptor; +import datadog.opentelemetry.shim.metrics.export.OtelInstrumentVisitor; import datadog.trace.api.Config; import datadog.trace.api.config.OtlpConfig; import datadog.trace.relocate.api.RatelimitedLogger; @@ -11,7 +12,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; import org.slf4j.Logger; @@ -108,18 +108,23 @@ private OtelAggregator aggregator( return aggregators.computeIfAbsent(attributes, aggregatorSupplier); } - public void collect(BiConsumer consumer) { + public void collect(OtelInstrumentVisitor visitor) { if (RESET_ON_COLLECT) { - doCollectAndReset(consumer); + doCollectAndReset(visitor); } else { - doCollect(consumer); + doCollect(visitor); } } /** Collect data for CUMULATIVE temporality, keeping aggregators for future writes. */ - private void doCollect(BiConsumer consumer) { + private void doCollect(OtelInstrumentVisitor visitor) { // no need to hold writers back if we are not resetting metrics on collect - currentRecording.aggregators.forEach((k, v) -> consumer.accept(k, v.collect())); + currentRecording.aggregators.forEach( + (attributes, aggregator) -> { + if (!aggregator.isEmpty()) { + visitor.visitPoint(attributes, aggregator.collect()); + } + }); } /** @@ -127,7 +132,7 @@ private void doCollect(BiConsumer consumer) { * *

Each collect request toggles between two groups of aggregators: current / previous. */ - private void doCollectAndReset(BiConsumer consumer) { + private void doCollectAndReset(OtelInstrumentVisitor visitor) { // capture _current_ recording for collection, its aggregators will be reset at the end final Recording recording = currentRecording; @@ -149,9 +154,9 @@ private void doCollectAndReset(BiConsumer consumer) { } aggregators.forEach( - (k, v) -> { - if (!v.isEmpty()) { - consumer.accept(k, v.collectAndReset()); + (attributes, aggregator) -> { + if (!aggregator.isEmpty()) { + visitor.visitPoint(attributes, aggregator.collectAndReset()); } }); diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelInstrumentVisitor.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelInstrumentVisitor.java new file mode 100644 index 00000000000..6067761e9aa --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelInstrumentVisitor.java @@ -0,0 +1,9 @@ +package datadog.opentelemetry.shim.metrics.export; + +import datadog.opentelemetry.shim.metrics.data.OtelPoint; +import io.opentelemetry.api.common.Attributes; + +public interface OtelInstrumentVisitor { + /** Visits a data point collected by the instrument. */ + void visitPoint(Attributes attributes, OtelPoint point); +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMeterVisitor.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMeterVisitor.java new file mode 100644 index 00000000000..311d2666a0f --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMeterVisitor.java @@ -0,0 +1,8 @@ +package datadog.opentelemetry.shim.metrics.export; + +import datadog.opentelemetry.shim.metrics.OtelInstrumentDescriptor; + +public interface OtelMeterVisitor { + /** Visits an instrument created by the meter. */ + OtelInstrumentVisitor visitInstrument(OtelInstrumentDescriptor descriptor); +} diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMetricVisitor.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMetricVisitor.java new file mode 100644 index 00000000000..ac2a5ac2e16 --- /dev/null +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/export/OtelMetricVisitor.java @@ -0,0 +1,8 @@ +package datadog.opentelemetry.shim.metrics.export; + +import datadog.opentelemetry.shim.OtelInstrumentationScope; + +public interface OtelMetricVisitor { + /** Visits a meter created by the OpenTelemetry API. */ + OtelMeterVisitor visitMeter(OtelInstrumentationScope scope); +} diff --git a/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java index e20a52adcb0..61d959cc249 100644 --- a/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java +++ b/dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/main/java/datadog/trace/instrumentation/opentelemetry147/OpenTelemetryMetricsInstrumentation.java @@ -81,6 +81,9 @@ public String[] helperClassNames() { "datadog.opentelemetry.shim.metrics.data.OtelLongValue", "datadog.opentelemetry.shim.metrics.data.OtelMetricStorage", "datadog.opentelemetry.shim.metrics.data.OtelMetricStorage$Recording", + "datadog.opentelemetry.shim.metrics.export.OtelInstrumentVisitor", + "datadog.opentelemetry.shim.metrics.export.OtelMeterVisitor", + "datadog.opentelemetry.shim.metrics.export.OtelMetricVisitor", "datadog.opentelemetry.shim.metrics.OtelDoubleCounter", "datadog.opentelemetry.shim.metrics.OtelDoubleCounter$Builder", "datadog.opentelemetry.shim.metrics.OtelDoubleGauge", From 2dc0b03c467895292dd558e131ff6c25198939a4 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 5 Feb 2026 22:01:04 +0000 Subject: [PATCH 10/14] Suppress warning about catching NPE in setExplicitBucketBoundariesAdvice - we do this to match OTel behaviour --- .../opentelemetry/shim/metrics/OtelDoubleHistogram.java | 4 +++- .../datadog/opentelemetry/shim/metrics/OtelLongHistogram.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index 6a7164e3b6e..4ef8195489d 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -8,6 +8,7 @@ import static java.util.Collections.unmodifiableList; import datadog.trace.relocate.api.RatelimitedLogger; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.DoubleHistogramBuilder; @@ -79,12 +80,13 @@ public DoubleHistogramBuilder setUnit(String unit) { } @Override + @SuppressFBWarnings("DCN") // match OTel in catching and logging NPE public DoubleHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketBoundaries) { try { Objects.requireNonNull(bucketBoundaries, "bucketBoundaries must not be null"); this.bucketBoundaries = validateBoundaries(unmodifiableList(new ArrayList<>(bucketBoundaries))); - } catch (NullPointerException | IllegalArgumentException e) { + } catch (IllegalArgumentException | NullPointerException e) { LOGGER.warn("Error setting explicit bucket boundaries advice: {}", e.getMessage()); } return this; diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index 7ad74901609..20362efaf47 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -8,6 +8,7 @@ import static java.util.stream.Collectors.toList; import datadog.trace.relocate.api.RatelimitedLogger; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.LongHistogramBuilder; @@ -72,6 +73,7 @@ public LongHistogramBuilder setUnit(String unit) { } @Override + @SuppressFBWarnings("DCN") // match OTel in catching and logging NPE public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketBoundaries) { try { Objects.requireNonNull(bucketBoundaries, "bucketBoundaries must not be null"); @@ -79,7 +81,7 @@ public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketB validateBoundaries( unmodifiableList( bucketBoundaries.stream().map(Long::doubleValue).collect(toList()))); - } catch (NullPointerException | IllegalArgumentException e) { + } catch (IllegalArgumentException | NullPointerException e) { LOGGER.warn("Error setting explicit bucket boundaries advice: {}", e.getMessage()); } return this; From 082c35848bb9a63d29a71db1b8c5c4b93d3260e0 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 5 Feb 2026 22:05:00 +0000 Subject: [PATCH 11/14] Spotless --- .../opentelemetry/shim/metrics/OtelInstrumentBuilder.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java index b8fc6c29827..858eeeb9ceb 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java @@ -83,9 +83,8 @@ void setUnit(String unit) { } OtelMetricStorage build(Function storageFactory) { - OtelInstrumentDescriptor descriptor = - new OtelInstrumentDescriptor(instrumentName, instrumentType, longValues, description, unit); - - return meter.registerStorage(descriptor, storageFactory); + return meter.registerStorage( + new OtelInstrumentDescriptor(instrumentName, instrumentType, longValues, description, unit), + storageFactory); } } From fb6ee6ba0387d1caa59e3cf09fea30ef1c1468ac Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 5 Feb 2026 22:20:31 +0000 Subject: [PATCH 12/14] Cleanup --- .../shim/metrics/OtelDoubleCounter.java | 21 ++++++------ .../shim/metrics/OtelDoubleGauge.java | 20 ++++++------ .../shim/metrics/OtelDoubleHistogram.java | 24 ++++++++------ .../shim/metrics/OtelDoubleUpDownCounter.java | 20 ++++++------ .../shim/metrics/OtelInstrumentBuilder.java | 32 ++++++------------- .../shim/metrics/OtelLongCounter.java | 21 ++++++------ .../shim/metrics/OtelLongGauge.java | 20 ++++++------ .../shim/metrics/OtelLongHistogram.java | 24 ++++++++------ .../shim/metrics/OtelLongUpDownCounter.java | 20 ++++++------ .../shim/metrics/data/OtelMetricStorage.java | 4 +++ 10 files changed, 112 insertions(+), 94 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java index f5e2653062f..3ad72a0689c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleCounter.java @@ -25,8 +25,8 @@ final class OtelDoubleCounter extends OtelInstrument implements DoubleCounter { private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - OtelDoubleCounter(OtelInstrumentBuilder builder) { - super(builder.build(OtelMetricStorage::newDoubleSumStorage)); + OtelDoubleCounter(OtelMetricStorage storage) { + super(storage); } @Override @@ -39,7 +39,7 @@ public void add(double value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Counters can only increase. Instrument {} has recorded a negative value.", - storage.getDescriptor().getName()); + storage.getInstrumentName()); } else { storage.recordDouble(value, attributes); } @@ -51,27 +51,30 @@ public void add(double value, Attributes attributes, Context unused) { } static final class Builder implements DoubleCounterBuilder { - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; - Builder(OtelInstrumentBuilder builder) { - this.instrumentBuilder = ofDoubles(builder, COUNTER); + Builder(OtelMeter meter, OtelInstrumentBuilder builder) { + this.meter = meter; + this.builder = ofDoubles(builder, COUNTER); } @Override public DoubleCounterBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public DoubleCounterBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @Override public DoubleCounter build() { - return new OtelDoubleCounter(instrumentBuilder); + return new OtelDoubleCounter( + meter.registerStorage(builder.descriptor(), OtelMetricStorage::newDoubleSumStorage)); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java index f4ef8be2555..e680d69d611 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleGauge.java @@ -18,9 +18,8 @@ @ParametersAreNonnullByDefault final class OtelDoubleGauge extends OtelInstrument implements DoubleGauge { - - OtelDoubleGauge(OtelInstrumentBuilder builder) { - super(builder.build(OtelMetricStorage::newDoubleValueStorage)); + OtelDoubleGauge(OtelMetricStorage storage) { + super(storage); } @Override @@ -39,32 +38,35 @@ public void set(double value, Attributes attributes, Context unused) { } static final class Builder implements DoubleGaugeBuilder { - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; Builder(OtelMeter meter, String instrumentName) { - this.instrumentBuilder = ofDoubles(meter, instrumentName, GAUGE); + this.meter = meter; + this.builder = ofDoubles(instrumentName, GAUGE); } @Override public DoubleGaugeBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public DoubleGaugeBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @Override public LongGaugeBuilder ofLongs() { - return new OtelLongGauge.Builder(instrumentBuilder); + return new OtelLongGauge.Builder(meter, builder); } @Override public DoubleGauge build() { - return new OtelDoubleGauge(instrumentBuilder); + return new OtelDoubleGauge( + meter.registerStorage(builder.descriptor(), OtelMetricStorage::newDoubleValueStorage)); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index 4ef8195489d..6c74b28ed24 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -7,6 +7,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.opentelemetry.api.common.Attributes; @@ -28,8 +29,8 @@ final class OtelDoubleHistogram extends OtelInstrument implements DoubleHistogra private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - OtelDoubleHistogram(OtelInstrumentBuilder builder, List bucketBoundaries) { - super(builder.build(descriptor -> newHistogramStorage(descriptor, bucketBoundaries))); + OtelDoubleHistogram(OtelMetricStorage storage) { + super(storage); } @Override @@ -42,7 +43,7 @@ public void record(double value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Histograms can only record non-negative values. Instrument {} has recorded a negative value.", - storage.getDescriptor().getName()); + storage.getInstrumentName()); } else { storage.recordDouble(value, attributes); } @@ -59,23 +60,25 @@ static final class Builder implements DoubleHistogramBuilder { 0d, 5d, 10d, 25d, 50d, 75d, 100d, 250d, 500d, 750d, 1_000d, 2_500d, 5_000d, 7_500d, 10_000d); - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; private List bucketBoundaries; Builder(OtelMeter meter, String instrumentName) { - this.instrumentBuilder = ofDoubles(meter, instrumentName, HISTOGRAM); + this.meter = meter; + this.builder = ofDoubles(instrumentName, HISTOGRAM); this.bucketBoundaries = DEFAULT_BOUNDARIES; } @Override public DoubleHistogramBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public DoubleHistogramBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @@ -94,12 +97,15 @@ public DoubleHistogramBuilder setExplicitBucketBoundariesAdvice(List buc @Override public LongHistogramBuilder ofLongs() { - return new OtelLongHistogram.Builder(instrumentBuilder, bucketBoundaries); + return new OtelLongHistogram.Builder(meter, builder, bucketBoundaries); } @Override public DoubleHistogram build() { - return new OtelDoubleHistogram(instrumentBuilder, bucketBoundaries); + return new OtelDoubleHistogram( + meter.registerStorage( + builder.descriptor(), + descriptor -> newHistogramStorage(descriptor, bucketBoundaries))); } static List validateBoundaries(List boundaries) { diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java index 0ca6c284a3c..6e0d2159ee0 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleUpDownCounter.java @@ -17,9 +17,8 @@ @ParametersAreNonnullByDefault final class OtelDoubleUpDownCounter extends OtelInstrument implements DoubleUpDownCounter { - - OtelDoubleUpDownCounter(OtelInstrumentBuilder builder) { - super(builder.build(OtelMetricStorage::newDoubleSumStorage)); + OtelDoubleUpDownCounter(OtelMetricStorage storage) { + super(storage); } @Override @@ -38,27 +37,30 @@ public void add(double value, Attributes attributes, Context unused) { } static final class Builder implements DoubleUpDownCounterBuilder { - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; - Builder(OtelInstrumentBuilder builder) { - this.instrumentBuilder = ofDoubles(builder, UP_DOWN_COUNTER); + Builder(OtelMeter meter, OtelInstrumentBuilder builder) { + this.meter = meter; + this.builder = ofDoubles(builder, UP_DOWN_COUNTER); } @Override public DoubleUpDownCounterBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public DoubleUpDownCounterBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @Override public DoubleUpDownCounter build() { - return new OtelDoubleUpDownCounter(instrumentBuilder); + return new OtelDoubleUpDownCounter( + meter.registerStorage(builder.descriptor(), OtelMetricStorage::newDoubleSumStorage)); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java index 858eeeb9ceb..4ee187fa654 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentBuilder.java @@ -1,11 +1,8 @@ package datadog.opentelemetry.shim.metrics; -import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; -import java.util.function.Function; import javax.annotation.Nullable; final class OtelInstrumentBuilder { - private final OtelMeter meter; private final String instrumentName; private final OtelInstrumentType instrumentType; private final boolean longValues; @@ -16,14 +13,12 @@ final class OtelInstrumentBuilder { /** * Starts building an instrument of long values with the given name and type. * - * @param meter the owning mete * @param instrumentName the name of the instrument * @param instrumentType the type of the instrument * @return new instrument builder */ - static OtelInstrumentBuilder ofLongs( - OtelMeter meter, String instrumentName, OtelInstrumentType instrumentType) { - return new OtelInstrumentBuilder(meter, instrumentName, instrumentType, true); + static OtelInstrumentBuilder ofLongs(String instrumentName, OtelInstrumentType instrumentType) { + return new OtelInstrumentBuilder(instrumentName, instrumentType, true); } /** @@ -35,20 +30,18 @@ static OtelInstrumentBuilder ofLongs( */ static OtelInstrumentBuilder ofLongs( OtelInstrumentBuilder builder, OtelInstrumentType instrumentType) { - return new OtelInstrumentBuilder(builder.meter, builder.instrumentName, instrumentType, true); + return new OtelInstrumentBuilder(builder.instrumentName, instrumentType, true); } /** * Starts building an instrument of double values with the given name and type. * - * @param meter the owning mete * @param instrumentName the name of the instrument * @param instrumentType the type of the instrument * @return new instrument builder */ - static OtelInstrumentBuilder ofDoubles( - OtelMeter meter, String instrumentName, OtelInstrumentType instrumentType) { - return new OtelInstrumentBuilder(meter, instrumentName, instrumentType, false); + static OtelInstrumentBuilder ofDoubles(String instrumentName, OtelInstrumentType instrumentType) { + return new OtelInstrumentBuilder(instrumentName, instrumentType, false); } /** @@ -60,15 +53,11 @@ static OtelInstrumentBuilder ofDoubles( */ static OtelInstrumentBuilder ofDoubles( OtelInstrumentBuilder builder, OtelInstrumentType instrumentType) { - return new OtelInstrumentBuilder(builder.meter, builder.instrumentName, instrumentType, false); + return new OtelInstrumentBuilder(builder.instrumentName, instrumentType, false); } private OtelInstrumentBuilder( - OtelMeter meter, - String instrumentName, - OtelInstrumentType instrumentType, - boolean longValues) { - this.meter = meter; + String instrumentName, OtelInstrumentType instrumentType, boolean longValues) { this.instrumentName = instrumentName; this.instrumentType = instrumentType; this.longValues = longValues; @@ -82,9 +71,8 @@ void setUnit(String unit) { this.unit = unit; } - OtelMetricStorage build(Function storageFactory) { - return meter.registerStorage( - new OtelInstrumentDescriptor(instrumentName, instrumentType, longValues, description, unit), - storageFactory); + OtelInstrumentDescriptor descriptor() { + return new OtelInstrumentDescriptor( + instrumentName, instrumentType, longValues, description, unit); } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java index bd33c758962..8c6d77fcfa6 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongCounter.java @@ -26,8 +26,8 @@ final class OtelLongCounter extends OtelInstrument implements LongCounter { private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - OtelLongCounter(OtelInstrumentBuilder builder) { - super(builder.build(OtelMetricStorage::newLongSumStorage)); + OtelLongCounter(OtelMetricStorage storage) { + super(storage); } @Override @@ -40,7 +40,7 @@ public void add(long value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Counters can only increase. Instrument {} has recorded a negative value.", - storage.getDescriptor().getName()); + storage.getInstrumentName()); } else { storage.recordLong(value, attributes); } @@ -52,32 +52,35 @@ public void add(long value, Attributes attributes, Context unused) { } static final class Builder implements LongCounterBuilder { - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; Builder(OtelMeter meter, String instrumentName) { - this.instrumentBuilder = ofLongs(meter, instrumentName, COUNTER); + this.meter = meter; + this.builder = ofLongs(instrumentName, COUNTER); } @Override public LongCounterBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public LongCounterBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @Override public DoubleCounterBuilder ofDoubles() { - return new OtelDoubleCounter.Builder(instrumentBuilder); + return new OtelDoubleCounter.Builder(meter, builder); } @Override public LongCounter build() { - return new OtelLongCounter(instrumentBuilder); + return new OtelLongCounter( + meter.registerStorage(builder.descriptor(), OtelMetricStorage::newLongSumStorage)); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java index 0219606e11e..2b0057a92fd 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongGauge.java @@ -17,9 +17,8 @@ @ParametersAreNonnullByDefault final class OtelLongGauge extends OtelInstrument implements LongGauge { - - OtelLongGauge(OtelInstrumentBuilder builder) { - super(builder.build(OtelMetricStorage::newLongValueStorage)); + OtelLongGauge(OtelMetricStorage storage) { + super(storage); } @Override @@ -38,27 +37,30 @@ public void set(long value, Attributes attributes, Context unused) { } static final class Builder implements LongGaugeBuilder { - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; - Builder(OtelInstrumentBuilder builder) { - this.instrumentBuilder = ofLongs(builder, GAUGE); + Builder(OtelMeter meter, OtelInstrumentBuilder builder) { + this.meter = meter; + this.builder = ofLongs(builder, GAUGE); } @Override public LongGaugeBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public LongGaugeBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @Override public LongGauge build() { - return new OtelLongGauge(instrumentBuilder); + return new OtelLongGauge( + meter.registerStorage(builder.descriptor(), OtelMetricStorage::newLongValueStorage)); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index 20362efaf47..fb061565650 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -7,6 +7,7 @@ import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; +import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import io.opentelemetry.api.common.Attributes; @@ -26,8 +27,8 @@ final class OtelLongHistogram extends OtelInstrument implements LongHistogram { private static final RatelimitedLogger RATELIMITED_LOGGER = new RatelimitedLogger(LOGGER, 5, TimeUnit.MINUTES); - OtelLongHistogram(OtelInstrumentBuilder builder, List bucketBoundaries) { - super(builder.build(descriptor -> newHistogramStorage(descriptor, bucketBoundaries))); + OtelLongHistogram(OtelMetricStorage storage) { + super(storage); } @Override @@ -40,7 +41,7 @@ public void record(long value, Attributes attributes) { if (value < 0) { RATELIMITED_LOGGER.warn( "Histograms can only record non-negative values. Instrument {} has recorded a negative value.", - storage.getDescriptor().getName()); + storage.getInstrumentName()); } else { storage.recordLong(value, attributes); } @@ -52,23 +53,25 @@ public void record(long value, Attributes attributes, Context unused) { } static final class Builder implements LongHistogramBuilder { - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; private List bucketBoundaries; - Builder(OtelInstrumentBuilder builder, List bucketBoundaries) { - this.instrumentBuilder = ofLongs(builder, HISTOGRAM); + Builder(OtelMeter meter, OtelInstrumentBuilder builder, List bucketBoundaries) { + this.meter = meter; + this.builder = ofLongs(builder, HISTOGRAM); this.bucketBoundaries = bucketBoundaries; } @Override public LongHistogramBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public LongHistogramBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @@ -89,7 +92,10 @@ public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketB @Override public LongHistogram build() { - return new OtelLongHistogram(instrumentBuilder, bucketBoundaries); + return new OtelLongHistogram( + meter.registerStorage( + builder.descriptor(), + descriptor -> newHistogramStorage(descriptor, bucketBoundaries))); } } } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java index 61cf77a400c..52b07b0392c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongUpDownCounter.java @@ -18,9 +18,8 @@ @ParametersAreNonnullByDefault final class OtelLongUpDownCounter extends OtelInstrument implements LongUpDownCounter { - - OtelLongUpDownCounter(OtelInstrumentBuilder builder) { - super(builder.build(OtelMetricStorage::newLongSumStorage)); + OtelLongUpDownCounter(OtelMetricStorage storage) { + super(storage); } @Override @@ -39,32 +38,35 @@ public void add(long value, Attributes attributes, Context unused) { } static final class Builder implements LongUpDownCounterBuilder { - private final OtelInstrumentBuilder instrumentBuilder; + private final OtelMeter meter; + private final OtelInstrumentBuilder builder; Builder(OtelMeter meter, String instrumentName) { - this.instrumentBuilder = ofLongs(meter, instrumentName, UP_DOWN_COUNTER); + this.meter = meter; + this.builder = ofLongs(instrumentName, UP_DOWN_COUNTER); } @Override public LongUpDownCounterBuilder setDescription(String description) { - instrumentBuilder.setDescription(description); + builder.setDescription(description); return this; } @Override public LongUpDownCounterBuilder setUnit(String unit) { - instrumentBuilder.setUnit(unit); + builder.setUnit(unit); return this; } @Override public DoubleUpDownCounterBuilder ofDoubles() { - return new OtelDoubleUpDownCounter.Builder(instrumentBuilder); + return new OtelDoubleUpDownCounter.Builder(meter, builder); } @Override public LongUpDownCounter build() { - return new OtelLongUpDownCounter(instrumentBuilder); + return new OtelLongUpDownCounter( + meter.registerStorage(builder.descriptor(), OtelMetricStorage::newLongSumStorage)); } @Override diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java index 0312b27cb6e..39b6edd9fd6 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelMetricStorage.java @@ -69,6 +69,10 @@ public static OtelMetricStorage newHistogramStorage( return new OtelMetricStorage(descriptor, () -> new OtelHistogramSketch(bucketBoundaries)); } + public String getInstrumentName() { + return descriptor.getName(); + } + public OtelInstrumentDescriptor getDescriptor() { return descriptor; } From 492a33e6eb00ce7ddad44e5246d4bc586263c548 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Thu, 5 Feb 2026 23:45:22 +0000 Subject: [PATCH 13/14] javadoc --- .../datadog/opentelemetry/shim/metrics/data/OtelAggregator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java index d3bbc0a1250..282186ef36d 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/data/OtelAggregator.java @@ -1,5 +1,6 @@ package datadog.opentelemetry.shim.metrics.data; +/** Common behaviour shared across all aggregators. */ abstract class OtelAggregator { private volatile boolean empty = true; From 3f305b4c07f4c647fcf2e80f7b79604f194c8a66 Mon Sep 17 00:00:00 2001 From: Stuart McCulloch Date: Fri, 6 Feb 2026 10:11:18 +0000 Subject: [PATCH 14/14] Review feedback --- .../opentelemetry/shim/metrics/OtelDoubleHistogram.java | 4 +--- .../opentelemetry/shim/metrics/OtelInstrumentDescriptor.java | 2 +- .../opentelemetry/shim/metrics/OtelInstrumentType.java | 2 +- .../opentelemetry/shim/metrics/OtelLongHistogram.java | 5 +---- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java index 6c74b28ed24..f3b54923c75 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelDoubleHistogram.java @@ -5,7 +5,6 @@ import static datadog.opentelemetry.shim.metrics.data.OtelMetricStorage.newHistogramStorage; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableList; import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; import datadog.trace.relocate.api.RatelimitedLogger; @@ -87,8 +86,7 @@ public DoubleHistogramBuilder setUnit(String unit) { public DoubleHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketBoundaries) { try { Objects.requireNonNull(bucketBoundaries, "bucketBoundaries must not be null"); - this.bucketBoundaries = - validateBoundaries(unmodifiableList(new ArrayList<>(bucketBoundaries))); + this.bucketBoundaries = validateBoundaries(new ArrayList<>(bucketBoundaries)); } catch (IllegalArgumentException | NullPointerException e) { LOGGER.warn("Error setting explicit bucket boundaries advice: {}", e.getMessage()); } diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java index c467d8a677d..2ba90bc1a0c 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentDescriptor.java @@ -77,7 +77,7 @@ public String toString() { return "OtelInstrumentDescriptor{" + "name='" + instrumentName - + (description != null ? ", description='" + description : "") + + (description != null ? "', description='" + description : "") + (unit != null ? "', unit='" + unit : "") + "', type=" + instrumentType diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java index 4423655d302..95cc54d5f71 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelInstrumentType.java @@ -1,7 +1,7 @@ package datadog.opentelemetry.shim.metrics; public enum OtelInstrumentType { - // same order as opentelemetry-java + // same order as io.opentelemetry.sdk.metrics.InstrumentType COUNTER, UP_DOWN_COUNTER, HISTOGRAM, diff --git a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java index fb061565650..730134ba4d4 100644 --- a/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java +++ b/dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelLongHistogram.java @@ -4,7 +4,6 @@ import static datadog.opentelemetry.shim.metrics.OtelInstrumentBuilder.ofLongs; import static datadog.opentelemetry.shim.metrics.OtelInstrumentType.HISTOGRAM; import static datadog.opentelemetry.shim.metrics.data.OtelMetricStorage.newHistogramStorage; -import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; import datadog.opentelemetry.shim.metrics.data.OtelMetricStorage; @@ -81,9 +80,7 @@ public LongHistogramBuilder setExplicitBucketBoundariesAdvice(List bucketB try { Objects.requireNonNull(bucketBoundaries, "bucketBoundaries must not be null"); this.bucketBoundaries = - validateBoundaries( - unmodifiableList( - bucketBoundaries.stream().map(Long::doubleValue).collect(toList()))); + validateBoundaries(bucketBoundaries.stream().map(Long::doubleValue).collect(toList())); } catch (IllegalArgumentException | NullPointerException e) { LOGGER.warn("Error setting explicit bucket boundaries advice: {}", e.getMessage()); }