Fix metrics cardinality (#3972)

* Fix metrics cardinality

* Add test
This commit is contained in:
Trask Stalnaker 2021-08-26 18:45:19 -07:00 committed by GitHub
parent b16331678b
commit fec5ed2290
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 7 deletions

View File

@ -5,6 +5,8 @@
package io.opentelemetry.instrumentation.api.instrumenter.http;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyDurationView;
import com.google.auto.value.AutoValue;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
@ -75,7 +77,8 @@ public final class HttpClientMetrics implements RequestListener {
return;
}
duration.record(
(System.nanoTime() - state.startTimeNanos()) / NANOS_PER_MS, state.startAttributes());
(System.nanoTime() - state.startTimeNanos()) / NANOS_PER_MS,
applyDurationView(state.startAttributes()));
}
@AutoValue

View File

@ -5,6 +5,9 @@
package io.opentelemetry.instrumentation.api.instrumenter.http;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyDurationView;
import com.google.auto.value.AutoValue;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
@ -69,7 +72,7 @@ public final class HttpServerMetrics implements RequestListener {
@Override
public Context start(Context context, Attributes requestAttributes) {
long startTimeNanos = System.nanoTime();
activeRequests.add(1, requestAttributes);
activeRequests.add(1, applyActiveRequestsView(requestAttributes));
return context.with(
HTTP_SERVER_REQUEST_METRICS_STATE,
@ -84,9 +87,10 @@ public final class HttpServerMetrics implements RequestListener {
"No state present when ending context {}. Cannot reset HTTP request metrics.", context);
return;
}
activeRequests.add(-1, state.startAttributes());
activeRequests.add(-1, applyActiveRequestsView(state.startAttributes()));
duration.record(
(System.nanoTime() - state.startTimeNanos()) / NANOS_PER_MS, state.startAttributes());
(System.nanoTime() - state.startTimeNanos()) / NANOS_PER_MS,
applyDurationView(state.startAttributes()));
}
@AutoValue

View File

@ -0,0 +1,77 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter.http;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
// this is temporary, see
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/3962#issuecomment-906606325
@SuppressWarnings("rawtypes")
final class TemporaryMetricsView {
private static final Set<AttributeKey> durationView = buildDurationView();
private static final Set<AttributeKey> activeRequestsView = buildActiveRequestsView();
private static Set<AttributeKey> buildDurationView() {
// the list of included metrics is from
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#attributes
Set<AttributeKey> view = new HashSet<>();
view.add(SemanticAttributes.HTTP_METHOD);
view.add(SemanticAttributes.HTTP_HOST);
view.add(SemanticAttributes.HTTP_SCHEME);
view.add(SemanticAttributes.HTTP_STATUS_CODE);
view.add(SemanticAttributes.HTTP_FLAVOR);
view.add(SemanticAttributes.NET_PEER_NAME);
view.add(SemanticAttributes.NET_PEER_PORT);
view.add(SemanticAttributes.NET_PEER_IP);
view.add(SemanticAttributes.HTTP_SERVER_NAME);
view.add(SemanticAttributes.NET_HOST_NAME);
view.add(SemanticAttributes.NET_HOST_PORT);
return view;
}
private static Set<AttributeKey> buildActiveRequestsView() {
// the list of included metrics is from
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#attributes
Set<AttributeKey> view = new HashSet<>();
view.add(SemanticAttributes.HTTP_METHOD);
view.add(SemanticAttributes.HTTP_HOST);
view.add(SemanticAttributes.HTTP_SCHEME);
view.add(SemanticAttributes.HTTP_FLAVOR);
view.add(SemanticAttributes.HTTP_SERVER_NAME);
return view;
}
static Attributes applyDurationView(Attributes attributes) {
return applyView(attributes, durationView);
}
static Attributes applyActiveRequestsView(Attributes attributes) {
return applyView(attributes, activeRequestsView);
}
@SuppressWarnings("unchecked")
private static Attributes applyView(Attributes attributes, Set<AttributeKey> view) {
AttributesBuilder filtered = Attributes.builder();
attributes.forEach(
(BiConsumer<AttributeKey, Object>)
(key, value) -> {
if (view.contains(key)) {
filtered.put(key, value);
}
});
return filtered.build();
}
private TemporaryMetricsView() {}
}

View File

@ -63,9 +63,7 @@ class HttpServerMetricsTest {
.containsOnly(
attributeEntry("http.host", "host"),
attributeEntry("http.method", "GET"),
attributeEntry("http.scheme", "https"),
attributeEntry("net.host.name", "localhost"),
attributeEntry("net.host.port", 1234L))));
attributeEntry("http.scheme", "https"))));
Context context2 = listener.start(Context.current(), requestAttributes);

View File

@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter.http;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView;
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyDurationView;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import org.junit.jupiter.api.Test;
public class TemporaryMetricsViewTest {
@Test
public void shouldApplyDurationView() {
Attributes attributes =
Attributes.builder()
.put(SemanticAttributes.HTTP_METHOD, "GET")
.put(SemanticAttributes.HTTP_URL, "http://somehost/high/cardinality/12345")
.put(SemanticAttributes.NET_PEER_NAME, "somehost")
.build();
OpenTelemetryAssertions.assertThat(applyDurationView(attributes))
.containsOnly(
attributeEntry("http.method", "GET"), attributeEntry("net.peer.name", "somehost"));
}
@Test
public void shouldApplyActiveRequestsView() {
Attributes attributes =
Attributes.builder()
.put(SemanticAttributes.HTTP_METHOD, "GET")
.put(SemanticAttributes.HTTP_URL, "/high/cardinality/12345")
.put(SemanticAttributes.NET_PEER_NAME, "somehost")
.build();
OpenTelemetryAssertions.assertThat(applyActiveRequestsView(attributes))
.containsOnly(attributeEntry("http.method", "GET"));
}
}