Skip to content

Commit 666c22e

Browse files
author
Mateusz Rzeszutek
authored
Use http.route in HttpServerMetrics (#5266)
* Use http.route in HttpServerMetrics * remove http.target fallback
1 parent c54a823 commit 666c22e

File tree

5 files changed

+65
-70
lines changed

5 files changed

+65
-70
lines changed

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpRouteHolder.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ private boolean isBetterRoute(String name) {
146146
return name.length() > routeLength;
147147
}
148148

149-
// TODO: use that in HttpServerMetrics
150149
/**
151150
* Returns the {@code http.route} attribute value that's stored in the passed {@code context}, or
152151
* null if it was not set before.

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import io.opentelemetry.instrumentation.api.annotations.UnstableApi;
1919
import io.opentelemetry.instrumentation.api.instrumenter.RequestListener;
2020
import io.opentelemetry.instrumentation.api.instrumenter.RequestMetrics;
21+
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
2122
import java.util.concurrent.TimeUnit;
23+
import java.util.function.Function;
2224
import org.slf4j.Logger;
2325
import org.slf4j.LoggerFactory;
2426

@@ -49,8 +51,14 @@ public static RequestMetrics get() {
4951

5052
private final LongUpDownCounter activeRequests;
5153
private final DoubleHistogram duration;
54+
private final Function<Context, String> httpRouteHolderGetter;
5255

5356
private HttpServerMetrics(Meter meter) {
57+
this(meter, HttpRouteHolder::getRoute);
58+
}
59+
60+
// visible for tests
61+
HttpServerMetrics(Meter meter, Function<Context, String> httpRouteHolderGetter) {
5462
activeRequests =
5563
meter
5664
.upDownCounterBuilder("http.server.active_requests")
@@ -64,6 +72,8 @@ private HttpServerMetrics(Meter meter) {
6472
.setUnit("ms")
6573
.setDescription("The duration of the inbound HTTP request")
6674
.build();
75+
76+
this.httpRouteHolderGetter = httpRouteHolderGetter;
6777
}
6878

6979
@Override
@@ -86,10 +96,19 @@ public void end(Context context, Attributes endAttributes, long endNanos) {
8696
activeRequests.add(-1, applyActiveRequestsView(state.startAttributes()));
8797
duration.record(
8898
(endNanos - state.startTimeNanos()) / NANOS_PER_MS,
89-
applyServerDurationView(state.startAttributes(), endAttributes),
99+
applyServerDurationView(state.startAttributes(), addHttpRoute(context, endAttributes)),
90100
context);
91101
}
92102

103+
// TODO: the http.route should be extracted by the AttributesExtractor, HttpServerMetrics should
104+
// not access the context to get it
105+
private Attributes addHttpRoute(Context context, Attributes endAttributes) {
106+
String route = httpRouteHolderGetter.apply(context);
107+
return route == null
108+
? endAttributes
109+
: endAttributes.toBuilder().put(SemanticAttributes.HTTP_ROUTE, route).build();
110+
}
111+
93112
@AutoValue
94113
abstract static class State {
95114

instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import io.opentelemetry.api.common.AttributeKey;
99
import io.opentelemetry.api.common.Attributes;
1010
import io.opentelemetry.api.common.AttributesBuilder;
11-
import io.opentelemetry.instrumentation.api.config.Config;
1211
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
1312
import java.util.HashSet;
1413
import java.util.Set;
@@ -19,19 +18,9 @@
1918
@SuppressWarnings("rawtypes")
2019
final class TemporaryMetricsView {
2120

22-
// TODO (trask) remove this once http.route is captured consistently
23-
//
24-
// this is not enabled by default because it falls back to http.target (which can be high
25-
// cardinality) when http.route is not available
26-
private static final boolean USE_HTTP_TARGET_FALLBACK =
27-
Config.get()
28-
.getBoolean("otel.instrumentation.metrics.experimental.use-http-target-fallback", false);
29-
3021
private static final Set<AttributeKey> durationAlwaysInclude = buildDurationAlwaysInclude();
3122
private static final Set<AttributeKey> durationClientView = buildDurationClientView();
3223
private static final Set<AttributeKey> durationServerView = buildDurationServerView();
33-
private static final Set<AttributeKey> durationServerFallbackView =
34-
buildDurationServerFallbackView();
3524
private static final Set<AttributeKey> activeRequestsView = buildActiveRequestsView();
3625

3726
private static Set<AttributeKey> buildDurationAlwaysInclude() {
@@ -65,19 +54,6 @@ private static Set<AttributeKey> buildDurationServerView() {
6554
return view;
6655
}
6756

68-
private static Set<AttributeKey> buildDurationServerFallbackView() {
69-
// We pull identifying attributes according to:
70-
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#attribute-alternatives
71-
// With the following caveat:
72-
// - we always rely on http.route + http.host in this repository.
73-
// - we prefer http.route (which is scrubbed) over http.target (which is not scrubbed).
74-
Set<AttributeKey> view = new HashSet<>(durationAlwaysInclude);
75-
view.add(SemanticAttributes.HTTP_SCHEME);
76-
view.add(SemanticAttributes.HTTP_HOST);
77-
view.add(SemanticAttributes.HTTP_TARGET);
78-
return view;
79-
}
80-
8157
private static Set<AttributeKey> buildActiveRequestsView() {
8258
// the list of included metrics is from
8359
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#attributes
@@ -97,21 +73,10 @@ static Attributes applyClientDurationView(Attributes startAttributes, Attributes
9773
return filtered.build();
9874
}
9975

100-
private static <T> boolean containsAttribute(
101-
AttributeKey<T> key, Attributes startAttributes, Attributes endAttributes) {
102-
return startAttributes.get(key) != null || endAttributes.get(key) != null;
103-
}
104-
10576
static Attributes applyServerDurationView(Attributes startAttributes, Attributes endAttributes) {
106-
Set<AttributeKey> fullSet = durationServerView;
107-
// Use http.target when http.route is not available.
108-
if (USE_HTTP_TARGET_FALLBACK
109-
&& !containsAttribute(SemanticAttributes.HTTP_ROUTE, startAttributes, endAttributes)) {
110-
fullSet = durationServerFallbackView;
111-
}
11277
AttributesBuilder filtered = Attributes.builder();
113-
applyView(filtered, startAttributes, fullSet);
114-
applyView(filtered, endAttributes, fullSet);
78+
applyView(filtered, startAttributes, durationServerView);
79+
applyView(filtered, endAttributes, durationServerView);
11580
return filtered.build();
11681
}
11782

instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,49 @@ void collectsMetrics() {
156156
.satisfiesExactly(point -> assertThat(point).hasSum(300 /* millis */)));
157157
}
158158

159+
@Test
160+
void collectsHttpRouteFromContext() {
161+
// given
162+
InMemoryMetricReader metricReader = InMemoryMetricReader.create();
163+
SdkMeterProvider meterProvider =
164+
SdkMeterProvider.builder()
165+
.registerMetricReader(metricReader)
166+
.setMinimumCollectionInterval(Duration.ZERO)
167+
.build();
168+
169+
RequestListener listener = new HttpServerMetrics(meterProvider.get("test"), c -> "/test/{id}");
170+
171+
Attributes requestAttributes =
172+
Attributes.builder().put("http.host", "host").put("http.scheme", "https").build();
173+
174+
Attributes responseAttributes = Attributes.empty();
175+
176+
Context parentContext = Context.root();
177+
178+
// when
179+
Context context = listener.start(parentContext, requestAttributes, nanos(100));
180+
listener.end(context, responseAttributes, nanos(200));
181+
182+
// then
183+
assertThat(metricReader.collectAllMetrics())
184+
.anySatisfy(
185+
metric ->
186+
assertThat(metric)
187+
.hasName("http.server.duration")
188+
.hasUnit("ms")
189+
.hasDoubleHistogram()
190+
.points()
191+
.satisfiesExactly(
192+
point ->
193+
assertThat(point)
194+
.hasSum(100 /* millis */)
195+
.attributes()
196+
.containsOnly(
197+
attributeEntry("http.scheme", "https"),
198+
attributeEntry("http.host", "host"),
199+
attributeEntry("http.route", "/test/{id}"))));
200+
}
201+
159202
private static long nanos(int millis) {
160203
return TimeUnit.MILLISECONDS.toNanos(millis);
161204
}

instrumentation-api/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -109,37 +109,6 @@ public void shouldApplyServerDurationView_withRoute() {
109109
attributeEntry(SemanticAttributes.HTTP_STATUS_CODE.getKey(), 500));
110110
}
111111

112-
@Test
113-
public void shouldApplyServerDurationView_withTarget() {
114-
Attributes startAttributes =
115-
Attributes.builder()
116-
.put(SemanticAttributes.HTTP_METHOD, "GET")
117-
.put(SemanticAttributes.HTTP_URL, "https://somehost/high/cardinality/12345")
118-
.put(SemanticAttributes.HTTP_SCHEME, "https")
119-
.put(SemanticAttributes.HTTP_HOST, "somehost")
120-
.put(SemanticAttributes.HTTP_SERVER_NAME, "somehost")
121-
.put(
122-
SemanticAttributes.HTTP_TARGET, "/somehost/high/cardinality/12345;jsessionId=12145")
123-
.put(SemanticAttributes.NET_HOST_NAME, "somehost")
124-
.put(SemanticAttributes.NET_HOST_PORT, 443)
125-
.build();
126-
127-
Attributes endAttributes =
128-
Attributes.builder()
129-
.put(SemanticAttributes.HTTP_STATUS_CODE, 500)
130-
.put(SemanticAttributes.NET_PEER_NAME, "somehost2")
131-
.put(SemanticAttributes.NET_PEER_IP, "127.0.0.1")
132-
.put(SemanticAttributes.NET_PEER_PORT, 443)
133-
.build();
134-
135-
OpenTelemetryAssertions.assertThat(applyServerDurationView(startAttributes, endAttributes))
136-
.containsOnly(
137-
attributeEntry(SemanticAttributes.HTTP_SCHEME.getKey(), "https"),
138-
attributeEntry(SemanticAttributes.HTTP_HOST.getKey(), "somehost"),
139-
attributeEntry(SemanticAttributes.HTTP_METHOD.getKey(), "GET"),
140-
attributeEntry(SemanticAttributes.HTTP_STATUS_CODE.getKey(), 500));
141-
}
142-
143112
@Test
144113
public void shouldApplyActiveRequestsView() {
145114
Attributes attributes =

0 commit comments

Comments
 (0)