Skip to content

Commit 62fece3

Browse files
committed
Register an OTel attributes reader for each injected/extension class-loader
1 parent c00f676 commit 62fece3

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/metrics/data/OtelMetricStorage.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import java.util.List;
1212
import java.util.Map;
1313
import java.util.Objects;
14+
import java.util.WeakHashMap;
1415
import java.util.concurrent.ConcurrentHashMap;
1516
import java.util.concurrent.TimeUnit;
1617
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
18+
import java.util.function.BiConsumer;
1719
import java.util.function.Function;
1820
import java.util.function.Supplier;
1921
import org.slf4j.Logger;
@@ -33,6 +35,9 @@ public final class OtelMetricStorage {
3335
private static final Attributes CARDINALITY_OVERFLOW =
3436
Attributes.builder().put("otel.metric.overflow", true).build();
3537

38+
private static final Map<ClassLoader, BiConsumer<Object, BiConsumer<String, Object>>>
39+
ATTRIBUTE_READERS = new WeakHashMap<>();
40+
3641
private final OtelInstrumentDescriptor descriptor;
3742
private final boolean resetOnCollect;
3843
private final Function<Object, OtelAggregator> aggregatorSupplier;
@@ -157,13 +162,27 @@ public void collectMetric(OtelMetricVisitor visitor) {
157162
}
158163
}
159164

165+
public static void registerAttributeReader(
166+
ClassLoader cl, BiConsumer<Object, BiConsumer<String, Object>> reader) {
167+
ATTRIBUTE_READERS.put(cl, reader);
168+
}
169+
170+
private static void visitAttributes(Object attributes, OtelMetricVisitor visitor) {
171+
ClassLoader cl = attributes.getClass().getClassLoader();
172+
BiConsumer<Object, BiConsumer<String, Object>> reader = ATTRIBUTE_READERS.get(cl);
173+
if (reader != null) {
174+
reader.accept(attributes, visitor::visitAttribute);
175+
}
176+
}
177+
160178
/** Collect data for CUMULATIVE temporality, keeping aggregators for future writes. */
161179
private void doCollect(OtelMetricVisitor visitor) {
162180
// no need to hold writers back if we are not resetting metrics on collect
163181
currentRecording.aggregators.forEach(
164182
(attributes, aggregator) -> {
165183
if (!aggregator.isEmpty()) {
166-
visitor.visitPoint(attributes, aggregator.collect());
184+
visitAttributes(attributes, visitor);
185+
visitor.visitPoint(aggregator.collect());
167186
}
168187
});
169188
}
@@ -197,7 +216,8 @@ private void doCollectAndReset(OtelMetricVisitor visitor) {
197216
aggregators.forEach(
198217
(attributes, aggregator) -> {
199218
if (!aggregator.isEmpty()) {
200-
visitor.visitPoint(attributes, aggregator.collectAndReset());
219+
visitAttributes(attributes, visitor);
220+
visitor.visitPoint(aggregator.collectAndReset());
201221
}
202222
});
203223

dd-java-agent/agent-otel/otel-bootstrap/src/main/java/datadog/trace/bootstrap/otel/metrics/export/OtelMetricVisitor.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
import datadog.trace.bootstrap.otel.metrics.data.OtelPoint;
44

5-
/** A visitor to visit a metric in an instrumentation scope. */
5+
/**
6+
* A visitor to visit a metric in an instrumentation scope.
7+
*
8+
* <p>Methods must be called in the following order: ( visitAttribute* visitPoint )*
9+
*/
610
public interface OtelMetricVisitor {
11+
/** Visits an attribute of the upcoming data point. */
12+
void visitAttribute(String key, Object value);
13+
714
/** Visits a data point in the metric. */
8-
void visitPoint(Object attributes, OtelPoint point);
15+
void visitPoint(OtelPoint point);
916
}

dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/metrics/OtelMeterProvider.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package datadog.opentelemetry.shim.metrics;
22

33
import datadog.trace.bootstrap.otel.common.OtelInstrumentationScope;
4+
import datadog.trace.bootstrap.otel.metrics.data.OtelMetricStorage;
45
import datadog.trace.util.Strings;
6+
import io.opentelemetry.api.common.Attributes;
57
import io.opentelemetry.api.metrics.Meter;
68
import io.opentelemetry.api.metrics.MeterBuilder;
79
import io.opentelemetry.api.metrics.MeterProvider;
@@ -22,6 +24,15 @@ public final class OtelMeterProvider implements MeterProvider {
2224
/** Meter shims, indexed by instrumentation scope. */
2325
private final Map<OtelInstrumentationScope, OtelMeter> meters = new ConcurrentHashMap<>();
2426

27+
private OtelMeterProvider() {
28+
// register attribute reader for class-loader where this provider is being used/injected
29+
OtelMetricStorage.registerAttributeReader(
30+
Attributes.class.getClassLoader(),
31+
(attributes, consumer) ->
32+
((Attributes) attributes)
33+
.forEach((attribute, value) -> consumer.accept(attribute.getKey(), value)));
34+
}
35+
2536
@Override
2637
public Meter get(String instrumentationScopeName) {
2738
return getMeterShim(instrumentationScopeName, null, null);

dd-java-agent/instrumentation/opentelemetry/opentelemetry-1.47/src/test/groovy/opentelemetry147/metrics/MetricsTest.groovy

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ class MetricsTest extends InstrumentationSpecification {
429429
class MeterReader implements OtelMetricsVisitor, OtelScopedMetricsVisitor, OtelMetricVisitor {
430430
def scopeName
431431
def instrumentName
432+
def attributes = [:]
432433

433434
@Override
434435
OtelScopedMetricsVisitor visitScopedMetrics(OtelInstrumentationScope scope) {
@@ -443,10 +444,16 @@ class MetricsTest extends InstrumentationSpecification {
443444
}
444445

445446
@Override
446-
void visitPoint(Object attributes, OtelPoint point) {
447+
void visitAttribute(String key, Object value) {
448+
attributes.put(key, value)
449+
}
450+
451+
@Override
452+
void visitPoint(OtelPoint point) {
447453
def key = scopeName + ':' + instrumentName
448454
if (!attributes.isEmpty()) {
449-
key = key + '@' + attributes.asMap()
455+
key = key + '@' + attributes
456+
attributes.clear()
450457
}
451458
switch (point.class) {
452459
case OtelLongPoint:

0 commit comments

Comments
 (0)