Skip to content

Commit e67d73e

Browse files
authored
Disable http and rpc metrics when advice can not be applied (#10671)
1 parent 042d0e4 commit e67d73e

File tree

8 files changed

+164
-6
lines changed

8 files changed

+164
-6
lines changed

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpClientExperimentalMetrics.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.opentelemetry.context.ContextKey;
1818
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
1919
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
20+
import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil;
2021
import java.util.logging.Logger;
2122

2223
/**
@@ -42,7 +43,8 @@ public final class HttpClientExperimentalMetrics implements OperationListener {
4243
* io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder}.
4344
*/
4445
public static OperationMetrics get() {
45-
return HttpClientExperimentalMetrics::new;
46+
return OperationMetricsUtil.create(
47+
"experimental http client", HttpClientExperimentalMetrics::new);
4648
}
4749

4850
private final LongHistogram requestSize;

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/http/HttpServerExperimentalMetrics.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.opentelemetry.context.ContextKey;
2020
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
2121
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
22+
import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil;
2223
import java.util.logging.Logger;
2324

2425
/**
@@ -46,7 +47,8 @@ public final class HttpServerExperimentalMetrics implements OperationListener {
4647
* io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder}.
4748
*/
4849
public static OperationMetrics get() {
49-
return HttpServerExperimentalMetrics::new;
50+
return OperationMetricsUtil.create(
51+
"experimental http server", HttpServerExperimentalMetrics::new);
5052
}
5153

5254
private final LongUpDownCounter activeRequests;

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcClientMetrics.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.context.ContextKey;
1717
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
1818
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
19+
import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil;
1920
import java.util.concurrent.TimeUnit;
2021
import java.util.logging.Logger;
2122

@@ -51,7 +52,7 @@ private RpcClientMetrics(Meter meter) {
5152
* io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder}.
5253
*/
5354
public static OperationMetrics get() {
54-
return RpcClientMetrics::new;
55+
return OperationMetricsUtil.create("rpc client", RpcClientMetrics::new);
5556
}
5657

5758
@Override

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/rpc/RpcServerMetrics.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.context.ContextKey;
1717
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
1818
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
19+
import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil;
1920
import java.util.concurrent.TimeUnit;
2021
import java.util.logging.Logger;
2122

@@ -51,7 +52,7 @@ private RpcServerMetrics(Meter meter) {
5152
* io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder}.
5253
*/
5354
public static OperationMetrics get() {
54-
return RpcServerMetrics::new;
55+
return OperationMetricsUtil.create("rpc server", RpcServerMetrics::new);
5556
}
5657

5758
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.internal;
7+
8+
import io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
10+
import io.opentelemetry.api.metrics.Meter;
11+
import io.opentelemetry.context.Context;
12+
import io.opentelemetry.extension.incubator.metrics.ExtendedDoubleHistogramBuilder;
13+
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
14+
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
15+
import java.util.function.BiConsumer;
16+
import java.util.function.Function;
17+
import java.util.logging.Level;
18+
import java.util.logging.Logger;
19+
20+
/**
21+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
22+
* any time.
23+
*/
24+
public class OperationMetricsUtil {
25+
private static final Logger logger = Logger.getLogger(OperationMetricsUtil.class.getName());
26+
private static final OperationListener NOOP_OPERATION_LISTENER =
27+
new OperationListener() {
28+
29+
@Override
30+
public Context onStart(Context context, Attributes startAttributes, long startNanos) {
31+
return context;
32+
}
33+
34+
@Override
35+
public void onEnd(Context context, Attributes endAttributes, long endNanos) {}
36+
};
37+
38+
public static OperationMetrics create(
39+
String description, Function<Meter, OperationListener> factory) {
40+
return create(
41+
description,
42+
factory,
43+
(s, histogramBuilder) ->
44+
logger.log(
45+
Level.WARNING,
46+
"Disabling {0} metrics because {1} does not implement {2}. This prevents using "
47+
+ "metrics advice, which could result in {0} having high cardinality attributes.",
48+
new Object[] {
49+
description,
50+
histogramBuilder.getClass().getName(),
51+
ExtendedDoubleHistogramBuilder.class.getName()
52+
}));
53+
}
54+
55+
// visible for testing
56+
static OperationMetrics create(
57+
String description,
58+
Function<Meter, OperationListener> factory,
59+
BiConsumer<String, DoubleHistogramBuilder> warningEmitter) {
60+
return meter -> {
61+
DoubleHistogramBuilder histogramBuilder = meter.histogramBuilder("compatibility-test");
62+
if (!(histogramBuilder instanceof ExtendedDoubleHistogramBuilder)
63+
&& !histogramBuilder.getClass().getName().contains("NoopDoubleHistogram")) {
64+
warningEmitter.accept(description, histogramBuilder);
65+
return NOOP_OPERATION_LISTENER;
66+
}
67+
return factory.apply(meter);
68+
};
69+
}
70+
71+
private OperationMetricsUtil() {}
72+
}

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpClientMetrics.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
1818
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
1919
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
20+
import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil;
2021
import java.util.concurrent.TimeUnit;
2122
import java.util.logging.Logger;
2223

@@ -43,7 +44,7 @@ public final class HttpClientMetrics implements OperationListener {
4344
* @see InstrumenterBuilder#addOperationMetrics(OperationMetrics)
4445
*/
4546
public static OperationMetrics get() {
46-
return HttpClientMetrics::new;
47+
return OperationMetricsUtil.create("http client", HttpClientMetrics::new);
4748
}
4849

4950
private final DoubleHistogram duration;

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpServerMetrics.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
1818
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
1919
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
20+
import io.opentelemetry.instrumentation.api.internal.OperationMetricsUtil;
2021
import java.util.concurrent.TimeUnit;
2122
import java.util.logging.Logger;
2223

@@ -43,7 +44,7 @@ public final class HttpServerMetrics implements OperationListener {
4344
* @see InstrumenterBuilder#addOperationMetrics(OperationMetrics)
4445
*/
4546
public static OperationMetrics get() {
46-
return HttpServerMetrics::new;
47+
return OperationMetricsUtil.create("http server", HttpServerMetrics::new);
4748
}
4849

4950
private final DoubleHistogram duration;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.api.internal;
7+
8+
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
9+
import io.opentelemetry.api.metrics.Meter;
10+
import io.opentelemetry.api.metrics.MeterProvider;
11+
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
12+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
13+
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
14+
import java.lang.reflect.Proxy;
15+
import java.util.concurrent.atomic.AtomicBoolean;
16+
import org.assertj.core.api.Assertions;
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.jupiter.api.extension.RegisterExtension;
19+
20+
class OperationMetricsUtilTest {
21+
@RegisterExtension
22+
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
23+
24+
@Test
25+
void noWarning() {
26+
AtomicBoolean warning = new AtomicBoolean(false);
27+
OperationMetrics operationMetrics =
28+
OperationMetricsUtil.create(
29+
"test metrics", meter -> null, (s, doubleHistogramBuilder) -> warning.set(true));
30+
operationMetrics.create(testing.getOpenTelemetry().getMeter("test"));
31+
32+
Assertions.assertThat(warning).isFalse();
33+
}
34+
35+
@Test
36+
void noWarningWithNoopMetrics() {
37+
AtomicBoolean warning = new AtomicBoolean(false);
38+
OperationMetrics operationMetrics =
39+
OperationMetricsUtil.create(
40+
"test metrics", meter -> null, (s, doubleHistogramBuilder) -> warning.set(true));
41+
operationMetrics.create(MeterProvider.noop().get("test"));
42+
43+
Assertions.assertThat(warning).isFalse();
44+
}
45+
46+
@Test
47+
void warning() {
48+
AtomicBoolean warning = new AtomicBoolean(false);
49+
OperationMetrics operationMetrics =
50+
OperationMetricsUtil.create(
51+
"test metrics", meter -> null, (s, doubleHistogramBuilder) -> warning.set(true));
52+
Meter defaultMeter = MeterProvider.noop().get("test");
53+
Meter meter =
54+
(Meter)
55+
Proxy.newProxyInstance(
56+
Meter.class.getClassLoader(),
57+
new Class<?>[] {Meter.class},
58+
(proxy, method, args) -> {
59+
if ("histogramBuilder".equals(method.getName())) {
60+
// proxy the histogram builder so that the builder instance does not implement
61+
// ExtendedDoubleHistogramBuilder which will trigger the warning
62+
return proxyDoubleHistogramBuilder(defaultMeter);
63+
}
64+
return method.invoke(defaultMeter, args);
65+
});
66+
operationMetrics.create(meter);
67+
68+
Assertions.assertThat(warning).isTrue();
69+
}
70+
71+
private static DoubleHistogramBuilder proxyDoubleHistogramBuilder(Meter meter) {
72+
return (DoubleHistogramBuilder)
73+
Proxy.newProxyInstance(
74+
DoubleHistogramBuilder.class.getClassLoader(),
75+
new Class<?>[] {DoubleHistogramBuilder.class},
76+
(proxy1, method1, args1) -> meter.histogramBuilder((String) args1[0]));
77+
}
78+
}

0 commit comments

Comments
 (0)