Specify `http.(client|server).duration` buckets using advice API (#8435)
This commit is contained in:
parent
d7d629617a
commit
28cf44cef4
|
@ -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")
|
||||||
|
|
|
@ -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() {}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Reference in New Issue