Add http.client|server.request|response.size metrics (#6376)
This commit is contained in:
parent
6a24080fe9
commit
c518bf8d7e
|
|
@ -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 <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
|
||||
|
|
|
|||
|
|
@ -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 <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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 @@ 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"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue