Specify `http.(client|server).duration` buckets using advice API (#8435)

This commit is contained in:
Mateusz Rzeszutek 2023-05-16 17:01:18 +02:00 committed by GitHub
parent d7d629617a
commit 28cf44cef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 22 deletions

View File

@ -13,6 +13,7 @@ group = "io.opentelemetry.instrumentation"
dependencies { dependencies {
api("io.opentelemetry:opentelemetry-semconv") api("io.opentelemetry:opentelemetry-semconv")
api(project(":instrumentation-api")) api(project(":instrumentation-api"))
implementation("io.opentelemetry:opentelemetry-extension-incubator")
compileOnly("com.google.auto.value:auto-value-annotations") compileOnly("com.google.auto.value:auto-value-annotations")
annotationProcessor("com.google.auto.value:auto-value") annotationProcessor("com.google.auto.value:auto-value")

View File

@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter.http;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
import io.opentelemetry.extension.incubator.metrics.ExtendedDoubleHistogramBuilder;
import java.util.List;
final class HistogramAdviceUtil {
static final List<Double> DURATION_SECONDS_BUCKETS =
unmodifiableList(
asList(
0.0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5,
10.0));
static void setHttpDurationBuckets(DoubleHistogramBuilder builder) {
if (!(builder instanceof ExtendedDoubleHistogramBuilder)) {
// that shouldn't really happen
return;
}
((ExtendedDoubleHistogramBuilder) builder)
.setAdvice(advice -> advice.setExplicitBucketBoundaries(DURATION_SECONDS_BUCKETS));
}
private HistogramAdviceUtil() {}
}

View File

@ -12,6 +12,7 @@ import com.google.auto.value.AutoValue;
import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.context.Context; import io.opentelemetry.context.Context;
@ -30,7 +31,7 @@ import javax.annotation.Nullable;
*/ */
public final class HttpClientMetrics implements OperationListener { public final class HttpClientMetrics implements OperationListener {
private static final double NANOS_PER_MS = TimeUnit.MILLISECONDS.toNanos(1); private static final double NANOS_PER_S = TimeUnit.SECONDS.toNanos(1);
private static final ContextKey<State> HTTP_CLIENT_REQUEST_METRICS_STATE = private static final ContextKey<State> HTTP_CLIENT_REQUEST_METRICS_STATE =
ContextKey.named("http-client-request-metrics-state"); ContextKey.named("http-client-request-metrics-state");
@ -51,12 +52,13 @@ public final class HttpClientMetrics implements OperationListener {
private final LongHistogram responseSize; private final LongHistogram responseSize;
private HttpClientMetrics(Meter meter) { private HttpClientMetrics(Meter meter) {
duration = DoubleHistogramBuilder durationBuilder =
meter meter
.histogramBuilder("http.client.duration") .histogramBuilder("http.client.duration")
.setUnit("ms") .setUnit("s")
.setDescription("The duration of the outbound HTTP request") .setDescription("The duration of the outbound HTTP request");
.build(); HistogramAdviceUtil.setHttpDurationBuckets(durationBuilder);
duration = durationBuilder.build();
requestSize = requestSize =
meter meter
.histogramBuilder("http.client.request.size") .histogramBuilder("http.client.request.size")
@ -93,7 +95,7 @@ public final class HttpClientMetrics implements OperationListener {
Attributes durationAndSizeAttributes = Attributes durationAndSizeAttributes =
applyClientDurationAndSizeView(state.startAttributes(), endAttributes); applyClientDurationAndSizeView(state.startAttributes(), endAttributes);
duration.record( duration.record(
(endNanos - state.startTimeNanos()) / NANOS_PER_MS, durationAndSizeAttributes, context); (endNanos - state.startTimeNanos()) / NANOS_PER_S, durationAndSizeAttributes, context);
Long requestLength = Long requestLength =
getAttribute( getAttribute(
SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes()); SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes());

View File

@ -13,6 +13,7 @@ import com.google.auto.value.AutoValue;
import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.LongUpDownCounter; import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.Meter;
@ -32,7 +33,7 @@ import javax.annotation.Nullable;
*/ */
public final class HttpServerMetrics implements OperationListener { public final class HttpServerMetrics implements OperationListener {
private static final double NANOS_PER_MS = TimeUnit.MILLISECONDS.toNanos(1); private static final double NANOS_PER_S = TimeUnit.SECONDS.toNanos(1);
private static final ContextKey<State> HTTP_SERVER_REQUEST_METRICS_STATE = private static final ContextKey<State> HTTP_SERVER_REQUEST_METRICS_STATE =
ContextKey.named("http-server-request-metrics-state"); ContextKey.named("http-server-request-metrics-state");
@ -61,12 +62,13 @@ public final class HttpServerMetrics implements OperationListener {
.setDescription("The number of concurrent HTTP requests that are currently in-flight") .setDescription("The number of concurrent HTTP requests that are currently in-flight")
.build(); .build();
duration = DoubleHistogramBuilder durationBuilder =
meter meter
.histogramBuilder("http.server.duration") .histogramBuilder("http.server.duration")
.setUnit("ms") .setUnit("s")
.setDescription("The duration of the inbound HTTP request") .setDescription("The duration of the inbound HTTP request");
.build(); HistogramAdviceUtil.setHttpDurationBuckets(durationBuilder);
duration = durationBuilder.build();
requestSize = requestSize =
meter meter
.histogramBuilder("http.server.request.size") .histogramBuilder("http.server.request.size")
@ -108,7 +110,7 @@ public final class HttpServerMetrics implements OperationListener {
Attributes durationAndSizeAttributes = Attributes durationAndSizeAttributes =
applyServerDurationAndSizeView(state.startAttributes(), endAttributes); applyServerDurationAndSizeView(state.startAttributes(), endAttributes);
duration.record( duration.record(
(endNanos - state.startTimeNanos()) / NANOS_PER_MS, durationAndSizeAttributes, context); (endNanos - state.startTimeNanos()) / NANOS_PER_S, durationAndSizeAttributes, context);
Long requestLength = Long requestLength =
getAttribute( getAttribute(
SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes()); SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes());

View File

@ -24,6 +24,9 @@ import org.junit.jupiter.api.Test;
class HttpClientMetricsTest { class HttpClientMetricsTest {
static final double[] DURATION_BUCKETS =
HistogramAdviceUtil.DURATION_SECONDS_BUCKETS.stream().mapToDouble(d -> d).toArray();
@Test @Test
void collectsMetrics() { void collectsMetrics() {
InMemoryMetricReader metricReader = InMemoryMetricReader.create(); InMemoryMetricReader metricReader = InMemoryMetricReader.create();
@ -79,13 +82,13 @@ class HttpClientMetricsTest {
metric -> metric ->
assertThat(metric) assertThat(metric)
.hasName("http.client.duration") .hasName("http.client.duration")
.hasUnit("ms") .hasUnit("s")
.hasHistogramSatisfying( .hasHistogramSatisfying(
histogram -> histogram ->
histogram.hasPointsSatisfying( histogram.hasPointsSatisfying(
point -> point ->
point point
.hasSum(150 /* millis */) .hasSum(0.15 /* seconds */)
.hasAttributesSatisfying( .hasAttributesSatisfying(
equalTo(SemanticAttributes.HTTP_METHOD, "GET"), equalTo(SemanticAttributes.HTTP_METHOD, "GET"),
equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200),
@ -99,7 +102,8 @@ class HttpClientMetricsTest {
exemplar -> exemplar ->
exemplar exemplar
.hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00")
.hasSpanId("090a0b0c0d0e0f00")))), .hasSpanId("090a0b0c0d0e0f00"))
.hasBucketBoundaries(DURATION_BUCKETS))),
metric -> metric ->
assertThat(metric) assertThat(metric)
.hasName("http.client.request.size") .hasName("http.client.request.size")
@ -158,7 +162,8 @@ class HttpClientMetricsTest {
.hasName("http.client.duration") .hasName("http.client.duration")
.hasHistogramSatisfying( .hasHistogramSatisfying(
histogram -> histogram ->
histogram.hasPointsSatisfying(point -> point.hasSum(300 /* millis */))), histogram.hasPointsSatisfying(
point -> point.hasSum(0.3 /* seconds */))),
metric -> metric ->
assertThat(metric) assertThat(metric)
.hasName("http.client.request.size") .hasName("http.client.request.size")

View File

@ -25,6 +25,9 @@ import org.junit.jupiter.api.Test;
class HttpServerMetricsTest { class HttpServerMetricsTest {
static final double[] DURATION_BUCKETS =
HistogramAdviceUtil.DURATION_SECONDS_BUCKETS.stream().mapToDouble(d -> d).toArray();
@Test @Test
void collectsMetrics() { void collectsMetrics() {
InMemoryMetricReader metricReader = InMemoryMetricReader.create(); InMemoryMetricReader metricReader = InMemoryMetricReader.create();
@ -149,13 +152,13 @@ class HttpServerMetricsTest {
metric -> metric ->
assertThat(metric) assertThat(metric)
.hasName("http.server.duration") .hasName("http.server.duration")
.hasUnit("ms") .hasUnit("s")
.hasHistogramSatisfying( .hasHistogramSatisfying(
histogram -> histogram ->
histogram.hasPointsSatisfying( histogram.hasPointsSatisfying(
point -> point ->
point point
.hasSum(150 /* millis */) .hasSum(0.15 /* seconds */)
.hasAttributesSatisfying( .hasAttributesSatisfying(
equalTo(SemanticAttributes.HTTP_METHOD, "GET"), equalTo(SemanticAttributes.HTTP_METHOD, "GET"),
equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200),
@ -168,7 +171,8 @@ class HttpServerMetricsTest {
exemplar -> exemplar ->
exemplar exemplar
.hasTraceId(spanContext1.getTraceId()) .hasTraceId(spanContext1.getTraceId())
.hasSpanId(spanContext1.getSpanId())))), .hasSpanId(spanContext1.getSpanId()))
.hasBucketBoundaries(DURATION_BUCKETS))),
metric -> metric ->
assertThat(metric) assertThat(metric)
.hasName("http.server.request.size") .hasName("http.server.request.size")
@ -242,7 +246,7 @@ class HttpServerMetricsTest {
histogram.hasPointsSatisfying( histogram.hasPointsSatisfying(
point -> point ->
point point
.hasSum(300 /* millis */) .hasSum(0.3 /* seconds */)
.hasExemplarsSatisfying( .hasExemplarsSatisfying(
exemplar -> exemplar ->
exemplar exemplar
@ -304,13 +308,13 @@ class HttpServerMetricsTest {
metric -> metric ->
assertThat(metric) assertThat(metric)
.hasName("http.server.duration") .hasName("http.server.duration")
.hasUnit("ms") .hasUnit("s")
.hasHistogramSatisfying( .hasHistogramSatisfying(
histogram -> histogram ->
histogram.hasPointsSatisfying( histogram.hasPointsSatisfying(
point -> point ->
point point
.hasSum(100 /* millis */) .hasSum(0.100 /* seconds */)
.hasAttributesSatisfying( .hasAttributesSatisfying(
equalTo(SemanticAttributes.HTTP_SCHEME, "https"), equalTo(SemanticAttributes.HTTP_SCHEME, "https"),
equalTo(SemanticAttributes.NET_HOST_NAME, "host"), equalTo(SemanticAttributes.NET_HOST_NAME, "host"),