From d23732f47d329f03d885876e649525d9d3b3856b Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Wed, 27 Jul 2022 11:28:11 -0500 Subject: [PATCH] Add http.client|server.request|response.size metrics (#6376) --- .../instrumenter/http/HttpClientMetrics.java | 53 +++++++++++++++++-- .../instrumenter/http/HttpServerMetrics.java | 53 +++++++++++++++++-- .../http/TemporaryMetricsView.java | 6 ++- .../http/HttpClientMetricsTest.java | 51 ++++++++++++++++-- .../http/HttpServerMetricsTest.java | 50 ++++++++++++++++- .../http/TemporaryMetricsViewTest.java | 14 ++--- 6 files changed, 206 insertions(+), 21 deletions(-) diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java index 9323ce8c5c87..ab2aede2a05d 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java @@ -5,19 +5,23 @@ package io.opentelemetry.instrumentation.api.instrumenter.http; -import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationView; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationAndSizeView; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * {@link OperationListener} which keeps track of T getAttribute(AttributeKey key, Attributes... attributesList) { + for (Attributes attributes : attributesList) { + T value = attributes.get(key); + if (value != null) { + return value; + } + } + return null; } @AutoValue diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java index 2c624f9253aa..8eb17691cf26 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java @@ -6,20 +6,24 @@ 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.applyServerDurationView; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView; import static java.util.logging.Level.FINE; import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongHistogram; import io.opentelemetry.api.metrics.LongUpDownCounter; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.context.Context; import io.opentelemetry.context.ContextKey; import io.opentelemetry.instrumentation.api.instrumenter.OperationListener; import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * {@link OperationListener} which keeps track of T getAttribute(AttributeKey key, Attributes... attributesList) { + for (Attributes attributes : attributesList) { + T value = attributes.get(key); + if (value != null) { + return value; + } + } + return null; } @AutoValue diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java index f6432320f5f4..7dbcb9887dee 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java @@ -68,14 +68,16 @@ private static Set buildActiveRequestsView() { return view; } - static Attributes applyClientDurationView(Attributes startAttributes, Attributes endAttributes) { + static Attributes applyClientDurationAndSizeView( + Attributes startAttributes, Attributes endAttributes) { AttributesBuilder filtered = Attributes.builder(); applyView(filtered, startAttributes, durationClientView); applyView(filtered, endAttributes, durationClientView); return filtered.build(); } - static Attributes applyServerDurationView(Attributes startAttributes, Attributes endAttributes) { + static Attributes applyServerDurationAndSizeView( + Attributes startAttributes, Attributes endAttributes) { AttributesBuilder filtered = Attributes.builder(); applyView(filtered, startAttributes, durationServerView); applyView(filtered, endAttributes, durationServerView); diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java index 7a8fa75744a8..fd5491a02fb4 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java @@ -41,6 +41,7 @@ void collectsMetrics() { .put("net.peer.name", "localhost") .put("net.peer.ip", "0.0.0.0") .put("net.peer.port", 1234) + .put("http.request_content_length", 100) .build(); Attributes responseAttributes = @@ -48,6 +49,7 @@ void collectsMetrics() { .put("http.flavor", "2.0") .put("http.server_name", "server") .put("http.status_code", 200) + .put("http.response_content_length", 200) .build(); Context parent = @@ -92,7 +94,39 @@ void collectsMetrics() { exemplar -> exemplar .hasTraceId("ff01020304050600ff0a0b0c0d0e0f00") - .hasSpanId("090a0b0c0d0e0f00"))))); + .hasSpanId("090a0b0c0d0e0f00")))), + metric -> + assertThat(metric) + .hasName("http.client.request.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(100 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, 1234), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200)))), + metric -> + assertThat(metric) + .hasName("http.client.response.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(200 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"), + equalTo(SemanticAttributes.NET_PEER_PORT, 1234), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200))))); listener.onEnd(context2, responseAttributes, nanos(300)); @@ -103,8 +137,19 @@ void collectsMetrics() { .hasName("http.client.duration") .hasHistogramSatisfying( histogram -> - histogram.hasPointsSatisfying( - point -> point.hasSum(300 /* millis */)))); + histogram.hasPointsSatisfying(point -> point.hasSum(300 /* millis */))), + metric -> + assertThat(metric) + .hasName("http.client.request.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(200 /* bytes */))), + metric -> + assertThat(metric) + .hasName("http.client.response.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(400 /* bytes */)))); } private static long nanos(int millis) { diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java index 5ab895909bc0..04442dd1d304 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java @@ -39,6 +39,7 @@ void collectsMetrics() { .put("http.scheme", "https") .put("net.host.name", "localhost") .put("net.host.port", 1234) + .put("http.request_content_length", 100) .build(); Attributes responseAttributes = @@ -46,6 +47,7 @@ void collectsMetrics() { .put("http.flavor", "2.0") .put("http.server_name", "server") .put("http.status_code", 200) + .put("http.response_content_length", 200) .build(); SpanContext spanContext1 = @@ -154,7 +156,39 @@ void collectsMetrics() { exemplar -> exemplar .hasTraceId(spanContext1.getTraceId()) - .hasSpanId(spanContext1.getSpanId()))))); + .hasSpanId(spanContext1.getSpanId())))), + metric -> + assertThat(metric) + .hasName("http.server.request.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(100 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.HTTP_HOST, "host"), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0")))), + metric -> + assertThat(metric) + .hasName("http.server.response.size") + .hasUnit("By") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(200 /* bytes */) + .hasAttributesSatisfying( + equalTo(SemanticAttributes.HTTP_SCHEME, "https"), + equalTo(SemanticAttributes.HTTP_HOST, "host"), + equalTo(SemanticAttributes.HTTP_METHOD, "GET"), + equalTo(SemanticAttributes.HTTP_STATUS_CODE, 200), + equalTo(SemanticAttributes.HTTP_FLAVOR, "2.0"))))); listener.onEnd(context2, responseAttributes, nanos(300)); @@ -178,7 +212,19 @@ void collectsMetrics() { exemplar -> exemplar .hasTraceId(spanContext2.getTraceId()) - .hasSpanId(spanContext2.getSpanId()))))); + .hasSpanId(spanContext2.getSpanId())))), + metric -> + assertThat(metric) + .hasName("http.server.request.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(200 /* bytes */))), + metric -> + assertThat(metric) + .hasName("http.server.response.size") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying(point -> point.hasSum(400 /* bytes */)))); } @Test diff --git a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java index 708e76ffbd31..7f8383fe2e7d 100644 --- a/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java +++ b/instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java @@ -6,8 +6,8 @@ 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.applyClientDurationView; -import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationView; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationAndSizeView; +import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry; import io.opentelemetry.api.common.Attributes; @@ -18,7 +18,7 @@ class TemporaryMetricsViewTest { @Test - void shouldApplyClientDurationView() { + void shouldApplyClientDurationAndSizeView() { Attributes startAttributes = Attributes.builder() .put( @@ -38,7 +38,8 @@ void shouldApplyClientDurationView() { .put(SemanticAttributes.NET_PEER_PORT, 443) .build(); - OpenTelemetryAssertions.assertThat(applyClientDurationView(startAttributes, endAttributes)) + OpenTelemetryAssertions.assertThat( + applyClientDurationAndSizeView(startAttributes, endAttributes)) .containsOnly( attributeEntry(SemanticAttributes.NET_PEER_NAME.getKey(), "somehost2"), attributeEntry(SemanticAttributes.NET_PEER_PORT.getKey(), 443), @@ -47,7 +48,7 @@ void shouldApplyClientDurationView() { } @Test - void shouldApplyServerDurationView() { + void shouldApplyServerDurationAndSizeView() { Attributes startAttributes = Attributes.builder() .put(SemanticAttributes.HTTP_METHOD, "GET") @@ -73,7 +74,8 @@ void shouldApplyServerDurationView() { .put(SemanticAttributes.NET_PEER_PORT, 443) .build(); - OpenTelemetryAssertions.assertThat(applyServerDurationView(startAttributes, endAttributes)) + OpenTelemetryAssertions.assertThat( + applyServerDurationAndSizeView(startAttributes, endAttributes)) .containsOnly( attributeEntry(SemanticAttributes.HTTP_SCHEME.getKey(), "https"), attributeEntry(SemanticAttributes.HTTP_HOST.getKey(), "somehost"),