Add http.client|server.request|response.size metrics
MODIFY
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetrics.java
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 <a
@@ -43,6 +47,8 @@ public final class HttpClientMetrics implements OperationListener {
}
private final DoubleHistogram duration;
+ private final LongHistogram requestSize;
+ private final LongHistogram responseSize;
private HttpClientMetrics(Meter meter) {
duration =
@@ -51,6 +57,20 @@ public final class HttpClientMetrics implements OperationListener {
.setUnit("ms")
.setDescription("The duration of the outbound HTTP request")
.build();
+ requestSize =
+ meter
+ .histogramBuilder("http.client.request.size")
+ .setUnit("By")
+ .setDescription("The size of HTTP request messages")
+ .ofLongs()
+ .build();
+ responseSize =
+ meter
+ .histogramBuilder("http.client.response.size")
+ .setUnit("By")
+ .setDescription("The size of HTTP response messages")
+ .ofLongs()
+ .build();
}
@Override
@@ -70,10 +90,35 @@ public final class HttpClientMetrics implements OperationListener {
context);
return;
}
+ Attributes durationAndSizeAttributes =
+ applyClientDurationAndSizeView(state.startAttributes(), endAttributes);
duration.record(
- (endNanos - state.startTimeNanos()) / NANOS_PER_MS,
- applyClientDurationView(state.startAttributes(), endAttributes),
- context);
+ (endNanos - state.startTimeNanos()) / NANOS_PER_MS, durationAndSizeAttributes, context);
+ Long requestLength =
+ getAttribute(
+ SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes());
+ if (requestLength != null) {
+ requestSize.record(requestLength, durationAndSizeAttributes);
+ }
+ Long responseLength =
+ getAttribute(
+ SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
+ endAttributes,
+ state.startAttributes());
+ if (responseLength != null) {
+ responseSize.record(responseLength, durationAndSizeAttributes);
+ }
+ }
+
+ @Nullable
+ private static <T> T getAttribute(AttributeKey<T> key, Attributes... attributesList) {
+ for (Attributes attributes : attributesList) {
+ T value = attributes.get(key);
+ if (value != null) {
+ return value;
+ }
+ }
+ return null;
}
@AutoValue

MODIFY
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetrics.java
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 <a
@@ -46,6 +50,8 @@ public final class HttpServerMetrics implements OperationListener {
private final LongUpDownCounter activeRequests;
private final DoubleHistogram duration;
+ private final LongHistogram requestSize;
+ private final LongHistogram responseSize;
private HttpServerMetrics(Meter meter) {
activeRequests =
@@ -61,6 +67,20 @@ public final class HttpServerMetrics implements OperationListener {
.setUnit("ms")
.setDescription("The duration of the inbound HTTP request")
.build();
+ requestSize =
+ meter
+ .histogramBuilder("http.server.request.size")
+ .setUnit("By")
+ .setDescription("The size of HTTP request messages")
+ .ofLongs()
+ .build();
+ responseSize =
+ meter
+ .histogramBuilder("http.server.response.size")
+ .setUnit("By")
+ .setDescription("The size of HTTP response messages")
+ .ofLongs()
+ .build();
}
@Override
@@ -83,10 +103,35 @@ public final class HttpServerMetrics implements OperationListener {
return;
}
activeRequests.add(-1, applyActiveRequestsView(state.startAttributes()), context);
+ Attributes durationAndSizeAttributes =
+ applyServerDurationAndSizeView(state.startAttributes(), endAttributes);
duration.record(
- (endNanos - state.startTimeNanos()) / NANOS_PER_MS,
- applyServerDurationView(state.startAttributes(), endAttributes),
- context);
+ (endNanos - state.startTimeNanos()) / NANOS_PER_MS, durationAndSizeAttributes, context);
+ Long requestLength =
+ getAttribute(
+ SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes());
+ if (requestLength != null) {
+ requestSize.record(requestLength, durationAndSizeAttributes);
+ }
+ Long responseLength =
+ getAttribute(
+ SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
+ endAttributes,
+ state.startAttributes());
+ if (responseLength != null) {
+ responseSize.record(responseLength, durationAndSizeAttributes);
+ }
+ }
+
+ @Nullable
+ private static <T> T getAttribute(AttributeKey<T> key, Attributes... attributesList) {
+ for (Attributes attributes : attributesList) {
+ T value = attributes.get(key);
+ if (value != null) {
+ return value;
+ }
+ }
+ return null;
}
@AutoValue

MODIFY
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java
instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsView.java
@@ -68,14 +68,16 @@ final class TemporaryMetricsView {
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);

MODIFY
instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java
instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpClientMetricsTest.java
@@ -41,6 +41,7 @@ class HttpClientMetricsTest {
.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 @@ class HttpClientMetricsTest {
.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 @@ class HttpClientMetricsTest {
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 @@ class HttpClientMetricsTest {
.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) {

MODIFY
instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java
instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/HttpServerMetricsTest.java
@@ -39,6 +39,7 @@ class HttpServerMetricsTest {
.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 @@ class HttpServerMetricsTest {
.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 @@ class HttpServerMetricsTest {
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 @@ class HttpServerMetricsTest {
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

MODIFY
instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java
instrumentation-api-semconv/src/test/java/io/opentelemetry/instrumentation/api/instrumenter/http/TemporaryMetricsViewTest.java
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 @@ import org.junit.jupiter.api.Test;
class TemporaryMetricsViewTest {
@Test
- void shouldApplyClientDurationView() {
+ void shouldApplyClientDurationAndSizeView() {
Attributes startAttributes =
Attributes.builder()
.put(
@@ -38,7 +38,8 @@ class TemporaryMetricsViewTest {
.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 @@ class TemporaryMetricsViewTest {
}
@Test
- void shouldApplyServerDurationView() {
+ void shouldApplyServerDurationAndSizeView() {
Attributes startAttributes =
Attributes.builder()
.put(SemanticAttributes.HTTP_METHOD, "GET")
@@ -73,7 +74,8 @@ class TemporaryMetricsViewTest {
.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"),

Add http.client|server.request|response.size metrics (#6376)