Implement new stable HTTP metric semantic conventions (#8705)
This commit is contained in:
parent
0070148b34
commit
5d8e37ad1b
|
@ -5,11 +5,12 @@
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpRequestBodySize;
|
||||||
|
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpResponseBodySize;
|
||||||
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationAndSizeView;
|
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyClientDurationAndSizeView;
|
||||||
import static java.util.logging.Level.FINE;
|
import static java.util.logging.Level.FINE;
|
||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
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.DoubleHistogramBuilder;
|
||||||
|
@ -19,14 +20,12 @@ import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.ContextKey;
|
import io.opentelemetry.context.ContextKey;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link OperationListener} which keeps track of <a
|
* {@link OperationListener} which keeps track of <a
|
||||||
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#http-client">HTTP
|
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/http-metrics.md#http-client">HTTP
|
||||||
* client metrics</a>.
|
* client metrics</a>.
|
||||||
*/
|
*/
|
||||||
public final class HttpClientMetrics implements OperationListener {
|
public final class HttpClientMetrics implements OperationListener {
|
||||||
|
@ -92,35 +91,21 @@ public final class HttpClientMetrics implements OperationListener {
|
||||||
context);
|
context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Attributes durationAndSizeAttributes =
|
Attributes durationAndSizeAttributes =
|
||||||
applyClientDurationAndSizeView(state.startAttributes(), endAttributes);
|
applyClientDurationAndSizeView(state.startAttributes(), endAttributes);
|
||||||
duration.record(
|
duration.record(
|
||||||
(endNanos - state.startTimeNanos()) / NANOS_PER_S, durationAndSizeAttributes, context);
|
(endNanos - state.startTimeNanos()) / NANOS_PER_S, durationAndSizeAttributes, context);
|
||||||
Long requestLength =
|
|
||||||
getAttribute(
|
|
||||||
SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes());
|
|
||||||
if (requestLength != null) {
|
|
||||||
requestSize.record(requestLength, durationAndSizeAttributes, context);
|
|
||||||
}
|
|
||||||
Long responseLength =
|
|
||||||
getAttribute(
|
|
||||||
SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
|
|
||||||
endAttributes,
|
|
||||||
state.startAttributes());
|
|
||||||
if (responseLength != null) {
|
|
||||||
responseSize.record(responseLength, durationAndSizeAttributes, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
Long requestBodySize = getHttpRequestBodySize(endAttributes, state.startAttributes());
|
||||||
private static <T> T getAttribute(AttributeKey<T> key, Attributes... attributesList) {
|
if (requestBodySize != null) {
|
||||||
for (Attributes attributes : attributesList) {
|
requestSize.record(requestBodySize, durationAndSizeAttributes, context);
|
||||||
T value = attributes.get(key);
|
}
|
||||||
if (value != null) {
|
|
||||||
return value;
|
Long responseBodySize = getHttpResponseBodySize(endAttributes, state.startAttributes());
|
||||||
}
|
if (responseBodySize != null) {
|
||||||
|
responseSize.record(responseBodySize, durationAndSizeAttributes, context);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AutoValue
|
@AutoValue
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.AttributeKey;
|
||||||
|
import io.opentelemetry.api.common.Attributes;
|
||||||
|
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
final class HttpMessageBodySizeUtil {
|
||||||
|
|
||||||
|
private static final AttributeKey<Long> HTTP_REQUEST_BODY_SIZE =
|
||||||
|
SemconvStability.emitOldHttpSemconv()
|
||||||
|
? SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH
|
||||||
|
: HttpAttributes.HTTP_REQUEST_BODY_SIZE;
|
||||||
|
|
||||||
|
private static final AttributeKey<Long> HTTP_RESPONSE_BODY_SIZE =
|
||||||
|
SemconvStability.emitOldHttpSemconv()
|
||||||
|
? SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH
|
||||||
|
: HttpAttributes.HTTP_RESPONSE_BODY_SIZE;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
static Long getHttpRequestBodySize(Attributes... attributesList) {
|
||||||
|
return getAttribute(HTTP_REQUEST_BODY_SIZE, attributesList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
static Long getHttpResponseBodySize(Attributes... attributesList) {
|
||||||
|
return getAttribute(HTTP_RESPONSE_BODY_SIZE, attributesList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpMessageBodySizeUtil() {}
|
||||||
|
}
|
|
@ -5,12 +5,13 @@
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpRequestBodySize;
|
||||||
|
import static io.opentelemetry.instrumentation.api.instrumenter.http.HttpMessageBodySizeUtil.getHttpResponseBodySize;
|
||||||
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView;
|
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyActiveRequestsView;
|
||||||
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView;
|
import static io.opentelemetry.instrumentation.api.instrumenter.http.TemporaryMetricsView.applyServerDurationAndSizeView;
|
||||||
import static java.util.logging.Level.FINE;
|
import static java.util.logging.Level.FINE;
|
||||||
|
|
||||||
import com.google.auto.value.AutoValue;
|
import com.google.auto.value.AutoValue;
|
||||||
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.DoubleHistogramBuilder;
|
||||||
|
@ -21,14 +22,12 @@ import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.context.ContextKey;
|
import io.opentelemetry.context.ContextKey;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationMetrics;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link OperationListener} which keeps track of <a
|
* {@link OperationListener} which keeps track of <a
|
||||||
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#http-server">HTTP
|
* href="https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/http-metrics.md#http-server">HTTP
|
||||||
* server metrics</a>.
|
* server metrics</a>.
|
||||||
*/
|
*/
|
||||||
public final class HttpServerMetrics implements OperationListener {
|
public final class HttpServerMetrics implements OperationListener {
|
||||||
|
@ -107,35 +106,21 @@ public final class HttpServerMetrics implements OperationListener {
|
||||||
// it's important to use exactly the same attributes that were used when incrementing the active
|
// it's important to use exactly the same attributes that were used when incrementing the active
|
||||||
// request count (otherwise it will split the timeseries)
|
// request count (otherwise it will split the timeseries)
|
||||||
activeRequests.add(-1, applyActiveRequestsView(state.startAttributes()), context);
|
activeRequests.add(-1, applyActiveRequestsView(state.startAttributes()), context);
|
||||||
|
|
||||||
Attributes durationAndSizeAttributes =
|
Attributes durationAndSizeAttributes =
|
||||||
applyServerDurationAndSizeView(state.startAttributes(), endAttributes);
|
applyServerDurationAndSizeView(state.startAttributes(), endAttributes);
|
||||||
duration.record(
|
duration.record(
|
||||||
(endNanos - state.startTimeNanos()) / NANOS_PER_S, durationAndSizeAttributes, context);
|
(endNanos - state.startTimeNanos()) / NANOS_PER_S, durationAndSizeAttributes, context);
|
||||||
Long requestLength =
|
|
||||||
getAttribute(
|
|
||||||
SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH, endAttributes, state.startAttributes());
|
|
||||||
if (requestLength != null) {
|
|
||||||
requestSize.record(requestLength, durationAndSizeAttributes, context);
|
|
||||||
}
|
|
||||||
Long responseLength =
|
|
||||||
getAttribute(
|
|
||||||
SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
|
|
||||||
endAttributes,
|
|
||||||
state.startAttributes());
|
|
||||||
if (responseLength != null) {
|
|
||||||
responseSize.record(responseLength, durationAndSizeAttributes, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
Long requestBodySize = getHttpRequestBodySize(endAttributes, state.startAttributes());
|
||||||
private static <T> T getAttribute(AttributeKey<T> key, Attributes... attributesList) {
|
if (requestBodySize != null) {
|
||||||
for (Attributes attributes : attributesList) {
|
requestSize.record(requestBodySize, durationAndSizeAttributes, context);
|
||||||
T value = attributes.get(key);
|
}
|
||||||
if (value != null) {
|
|
||||||
return value;
|
Long responseBodySize = getHttpResponseBodySize(endAttributes, state.startAttributes());
|
||||||
}
|
if (responseBodySize != null) {
|
||||||
|
responseSize.record(responseBodySize, durationAndSizeAttributes, context);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AutoValue
|
@AutoValue
|
||||||
|
|
|
@ -9,6 +9,8 @@ import io.opentelemetry.api.common.AttributeKey;
|
||||||
import io.opentelemetry.api.common.Attributes;
|
import io.opentelemetry.api.common.Attributes;
|
||||||
import io.opentelemetry.api.common.AttributesBuilder;
|
import io.opentelemetry.api.common.AttributesBuilder;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.NetAttributes;
|
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.NetAttributes;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -32,6 +34,13 @@ final class TemporaryMetricsView {
|
||||||
view.add(SemanticAttributes.HTTP_STATUS_CODE); // Optional
|
view.add(SemanticAttributes.HTTP_STATUS_CODE); // Optional
|
||||||
view.add(NetAttributes.NET_PROTOCOL_NAME); // Optional
|
view.add(NetAttributes.NET_PROTOCOL_NAME); // Optional
|
||||||
view.add(NetAttributes.NET_PROTOCOL_VERSION); // Optional
|
view.add(NetAttributes.NET_PROTOCOL_VERSION); // Optional
|
||||||
|
// stable semconv
|
||||||
|
view.add(HttpAttributes.HTTP_REQUEST_METHOD);
|
||||||
|
view.add(HttpAttributes.HTTP_RESPONSE_STATUS_CODE);
|
||||||
|
view.add(NetworkAttributes.NETWORK_PROTOCOL_NAME);
|
||||||
|
view.add(NetworkAttributes.NETWORK_PROTOCOL_VERSION);
|
||||||
|
view.add(NetworkAttributes.SERVER_ADDRESS);
|
||||||
|
view.add(NetworkAttributes.SERVER_PORT);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +52,8 @@ final class TemporaryMetricsView {
|
||||||
view.add(SemanticAttributes.NET_PEER_NAME);
|
view.add(SemanticAttributes.NET_PEER_NAME);
|
||||||
view.add(SemanticAttributes.NET_PEER_PORT);
|
view.add(SemanticAttributes.NET_PEER_PORT);
|
||||||
view.add(SemanticAttributes.NET_SOCK_PEER_ADDR);
|
view.add(SemanticAttributes.NET_SOCK_PEER_ADDR);
|
||||||
|
// stable semconv
|
||||||
|
view.add(NetworkAttributes.SERVER_SOCKET_ADDRESS);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +68,8 @@ final class TemporaryMetricsView {
|
||||||
view.add(SemanticAttributes.NET_HOST_NAME);
|
view.add(SemanticAttributes.NET_HOST_NAME);
|
||||||
view.add(SemanticAttributes.NET_HOST_PORT);
|
view.add(SemanticAttributes.NET_HOST_PORT);
|
||||||
view.add(SemanticAttributes.HTTP_ROUTE);
|
view.add(SemanticAttributes.HTTP_ROUTE);
|
||||||
|
// stable semconv
|
||||||
|
view.add(UrlAttributes.URL_SCHEME);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +81,11 @@ final class TemporaryMetricsView {
|
||||||
view.add(SemanticAttributes.HTTP_SCHEME);
|
view.add(SemanticAttributes.HTTP_SCHEME);
|
||||||
view.add(SemanticAttributes.NET_HOST_NAME);
|
view.add(SemanticAttributes.NET_HOST_NAME);
|
||||||
view.add(SemanticAttributes.NET_HOST_PORT);
|
view.add(SemanticAttributes.NET_HOST_PORT);
|
||||||
|
// stable semconv
|
||||||
|
view.add(HttpAttributes.HTTP_REQUEST_METHOD);
|
||||||
|
view.add(NetworkAttributes.SERVER_ADDRESS);
|
||||||
|
view.add(NetworkAttributes.SERVER_PORT);
|
||||||
|
view.add(UrlAttributes.URL_SCHEME);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,5 +40,14 @@ public final class NetworkAttributes {
|
||||||
|
|
||||||
public static final AttributeKey<Long> SERVER_SOCKET_PORT = longKey("server.socket.port");
|
public static final AttributeKey<Long> SERVER_SOCKET_PORT = longKey("server.socket.port");
|
||||||
|
|
||||||
|
public static final AttributeKey<String> CLIENT_ADDRESS = stringKey("client.address");
|
||||||
|
|
||||||
|
public static final AttributeKey<Long> CLIENT_PORT = longKey("client.port");
|
||||||
|
|
||||||
|
public static final AttributeKey<String> CLIENT_SOCKET_ADDRESS =
|
||||||
|
stringKey("client.socket.address");
|
||||||
|
|
||||||
|
public static final AttributeKey<Long> CLIENT_SOCKET_PORT = longKey("client.socket.port");
|
||||||
|
|
||||||
private NetworkAttributes() {}
|
private NetworkAttributes() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ import static org.assertj.core.api.Assertions.entry;
|
||||||
|
|
||||||
import io.opentelemetry.api.common.Attributes;
|
import io.opentelemetry.api.common.Attributes;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.NetAttributes;
|
import io.opentelemetry.instrumentation.api.instrumenter.net.internal.NetAttributes;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes;
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -56,6 +58,43 @@ class TemporaryMetricsViewTest {
|
||||||
entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1.2.3.4"));
|
entry(SemanticAttributes.NET_SOCK_PEER_ADDR, "1.2.3.4"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldApplyClientDurationAndSizeView_stableSemconv() {
|
||||||
|
Attributes startAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(
|
||||||
|
UrlAttributes.URL_FULL, "https://somehost/high/cardinality/12345?jsessionId=121454")
|
||||||
|
.put(HttpAttributes.HTTP_REQUEST_METHOD, "GET")
|
||||||
|
.put(UrlAttributes.URL_SCHEME, "https")
|
||||||
|
.put(UrlAttributes.URL_PATH, "/high/cardinality/12345")
|
||||||
|
.put(UrlAttributes.URL_QUERY, "jsessionId=121454")
|
||||||
|
.put(NetworkAttributes.SERVER_ADDRESS, "somehost2")
|
||||||
|
.put(NetworkAttributes.SERVER_PORT, 443)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Attributes endAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 500)
|
||||||
|
.put(NetworkAttributes.NETWORK_TRANSPORT, "tcp")
|
||||||
|
.put(NetworkAttributes.NETWORK_TYPE, "ipv4")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_NAME, "http")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_ADDRESS, "1.2.3.4")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_DOMAIN, "somehost20")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_PORT, 8080)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(applyClientDurationAndSizeView(startAttributes, endAttributes))
|
||||||
|
.containsOnly(
|
||||||
|
entry(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
entry(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 500L),
|
||||||
|
entry(NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
entry(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1"),
|
||||||
|
entry(NetworkAttributes.SERVER_ADDRESS, "somehost2"),
|
||||||
|
entry(NetworkAttributes.SERVER_PORT, 443L),
|
||||||
|
entry(NetworkAttributes.SERVER_SOCKET_ADDRESS, "1.2.3.4"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldApplyServerDurationAndSizeView() {
|
void shouldApplyServerDurationAndSizeView() {
|
||||||
Attributes startAttributes =
|
Attributes startAttributes =
|
||||||
|
@ -98,6 +137,48 @@ class TemporaryMetricsViewTest {
|
||||||
entry(SemanticAttributes.HTTP_ROUTE, "/somehost/high/{name}/{id}"));
|
entry(SemanticAttributes.HTTP_ROUTE, "/somehost/high/{name}/{id}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldApplyServerDurationAndSizeView_stableSemconv() {
|
||||||
|
Attributes startAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(HttpAttributes.HTTP_REQUEST_METHOD, "GET")
|
||||||
|
.put(
|
||||||
|
UrlAttributes.URL_FULL, "https://somehost/high/cardinality/12345?jsessionId=121454")
|
||||||
|
.put(UrlAttributes.URL_SCHEME, "https")
|
||||||
|
.put(UrlAttributes.URL_PATH, "/high/cardinality/12345")
|
||||||
|
.put(UrlAttributes.URL_QUERY, "jsessionId=121454")
|
||||||
|
.put(NetworkAttributes.SERVER_ADDRESS, "somehost")
|
||||||
|
.put(NetworkAttributes.SERVER_PORT, 443)
|
||||||
|
.put(NetworkAttributes.CLIENT_ADDRESS, "somehost2")
|
||||||
|
.put(NetworkAttributes.CLIENT_PORT, 443)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Attributes endAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(SemanticAttributes.HTTP_ROUTE, "/somehost/high/{name}/{id}")
|
||||||
|
.put(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 500)
|
||||||
|
.put(NetworkAttributes.NETWORK_TRANSPORT, "tcp")
|
||||||
|
.put(NetworkAttributes.NETWORK_TYPE, "ipv4")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_NAME, "http")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_ADDRESS, "4.3.2.1")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_PORT, 9090)
|
||||||
|
.put(NetworkAttributes.CLIENT_SOCKET_ADDRESS, "1.2.3.4")
|
||||||
|
.put(NetworkAttributes.CLIENT_SOCKET_PORT, 8080)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(applyServerDurationAndSizeView(startAttributes, endAttributes))
|
||||||
|
.containsOnly(
|
||||||
|
entry(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
entry(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 500L),
|
||||||
|
entry(SemanticAttributes.HTTP_ROUTE, "/somehost/high/{name}/{id}"),
|
||||||
|
entry(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
entry(NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
entry(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1"),
|
||||||
|
entry(NetworkAttributes.SERVER_ADDRESS, "somehost"),
|
||||||
|
entry(NetworkAttributes.SERVER_PORT, 443L));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldApplyActiveRequestsView() {
|
void shouldApplyActiveRequestsView() {
|
||||||
Attributes attributes =
|
Attributes attributes =
|
||||||
|
@ -127,4 +208,34 @@ class TemporaryMetricsViewTest {
|
||||||
entry(SemanticAttributes.NET_HOST_NAME, "somehost"),
|
entry(SemanticAttributes.NET_HOST_NAME, "somehost"),
|
||||||
entry(SemanticAttributes.NET_HOST_PORT, 443L));
|
entry(SemanticAttributes.NET_HOST_PORT, 443L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldApplyActiveRequestsView_stableSemconv() {
|
||||||
|
Attributes attributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(HttpAttributes.HTTP_REQUEST_METHOD, "GET")
|
||||||
|
.put(
|
||||||
|
UrlAttributes.URL_FULL, "https://somehost/high/cardinality/12345?jsessionId=121454")
|
||||||
|
.put(UrlAttributes.URL_SCHEME, "https")
|
||||||
|
.put(UrlAttributes.URL_PATH, "/high/cardinality/12345")
|
||||||
|
.put(UrlAttributes.URL_QUERY, "jsessionId=121454")
|
||||||
|
.put(NetworkAttributes.NETWORK_TRANSPORT, "tcp")
|
||||||
|
.put(NetworkAttributes.NETWORK_TYPE, "ipv4")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_NAME, "http")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "1.1")
|
||||||
|
.put(NetworkAttributes.SERVER_ADDRESS, "somehost")
|
||||||
|
.put(NetworkAttributes.SERVER_PORT, 443)
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_ADDRESS, "4.3.2.1")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_PORT, 9090)
|
||||||
|
.put(NetworkAttributes.CLIENT_SOCKET_ADDRESS, "1.2.3.4")
|
||||||
|
.put(NetworkAttributes.CLIENT_SOCKET_PORT, 8080)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertThat(applyActiveRequestsView(attributes))
|
||||||
|
.containsOnly(
|
||||||
|
entry(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
entry(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
entry(NetworkAttributes.SERVER_ADDRESS, "somehost"),
|
||||||
|
entry(NetworkAttributes.SERVER_PORT, 443L));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
||||||
|
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.Attributes;
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.SpanContext;
|
||||||
|
import io.opentelemetry.api.trace.TraceFlags;
|
||||||
|
import io.opentelemetry.api.trace.TraceState;
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes;
|
||||||
|
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class HttpClientMetricsStableSemconvTest {
|
||||||
|
|
||||||
|
static final double[] DURATION_BUCKETS =
|
||||||
|
HistogramAdviceUtil.DURATION_SECONDS_BUCKETS.stream().mapToDouble(d -> d).toArray();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void collectsMetrics() {
|
||||||
|
InMemoryMetricReader metricReader = InMemoryMetricReader.create();
|
||||||
|
SdkMeterProvider meterProvider =
|
||||||
|
SdkMeterProvider.builder().registerMetricReader(metricReader).build();
|
||||||
|
|
||||||
|
OperationListener listener = HttpClientMetrics.get().create(meterProvider.get("test"));
|
||||||
|
|
||||||
|
Attributes requestAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(HttpAttributes.HTTP_REQUEST_METHOD, "GET")
|
||||||
|
.put(UrlAttributes.URL_FULL, "https://localhost:1234/")
|
||||||
|
.put(UrlAttributes.URL_SCHEME, "https")
|
||||||
|
.put(UrlAttributes.URL_PATH, "/")
|
||||||
|
.put(UrlAttributes.URL_QUERY, "q=a")
|
||||||
|
.put(NetworkAttributes.SERVER_ADDRESS, "localhost")
|
||||||
|
.put(NetworkAttributes.SERVER_PORT, 1234)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Attributes responseAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200)
|
||||||
|
.put(HttpAttributes.HTTP_REQUEST_BODY_SIZE, 100)
|
||||||
|
.put(HttpAttributes.HTTP_RESPONSE_BODY_SIZE, 200)
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_NAME, "http")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_ADDRESS, "1.2.3.4")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_DOMAIN, "somehost20")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_PORT, 8080)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Context parent =
|
||||||
|
Context.root()
|
||||||
|
.with(
|
||||||
|
Span.wrap(
|
||||||
|
SpanContext.create(
|
||||||
|
"ff01020304050600ff0a0b0c0d0e0f00",
|
||||||
|
"090a0b0c0d0e0f00",
|
||||||
|
TraceFlags.getSampled(),
|
||||||
|
TraceState.getDefault())));
|
||||||
|
|
||||||
|
Context context1 = listener.onStart(parent, requestAttributes, nanos(100));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics()).isEmpty();
|
||||||
|
|
||||||
|
Context context2 = listener.onStart(Context.root(), requestAttributes, nanos(150));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics()).isEmpty();
|
||||||
|
|
||||||
|
listener.onEnd(context1, responseAttributes, nanos(250));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfiesExactlyInAnyOrder(
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.client.duration")
|
||||||
|
.hasUnit("s")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(0.15 /* seconds */)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.SERVER_SOCKET_ADDRESS, "1.2.3.4"))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId("ff01020304050600ff0a0b0c0d0e0f00")
|
||||||
|
.hasSpanId("090a0b0c0d0e0f00"))
|
||||||
|
.hasBucketBoundaries(DURATION_BUCKETS))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.client.request.size")
|
||||||
|
.hasUnit("By")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(100 /* bytes */)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.SERVER_SOCKET_ADDRESS, "1.2.3.4"))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId("ff01020304050600ff0a0b0c0d0e0f00")
|
||||||
|
.hasSpanId("090a0b0c0d0e0f00")))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.client.response.size")
|
||||||
|
.hasUnit("By")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(200 /* bytes */)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.SERVER_SOCKET_ADDRESS, "1.2.3.4"))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId("ff01020304050600ff0a0b0c0d0e0f00")
|
||||||
|
.hasSpanId("090a0b0c0d0e0f00")))));
|
||||||
|
|
||||||
|
listener.onEnd(context2, responseAttributes, nanos(300));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfiesExactlyInAnyOrder(
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.client.duration")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point -> point.hasSum(0.3 /* seconds */))),
|
||||||
|
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) {
|
||||||
|
return TimeUnit.MILLISECONDS.toNanos(millis);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,339 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.instrumenter.http;
|
||||||
|
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.Attributes;
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.SpanContext;
|
||||||
|
import io.opentelemetry.api.trace.TraceFlags;
|
||||||
|
import io.opentelemetry.api.trace.TraceState;
|
||||||
|
import io.opentelemetry.context.Context;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.OperationListener;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
|
||||||
|
import io.opentelemetry.instrumentation.api.instrumenter.url.internal.UrlAttributes;
|
||||||
|
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class HttpServerMetricsStableSemconvTest {
|
||||||
|
|
||||||
|
static final double[] DURATION_BUCKETS =
|
||||||
|
HistogramAdviceUtil.DURATION_SECONDS_BUCKETS.stream().mapToDouble(d -> d).toArray();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void collectsMetrics() {
|
||||||
|
InMemoryMetricReader metricReader = InMemoryMetricReader.create();
|
||||||
|
SdkMeterProvider meterProvider =
|
||||||
|
SdkMeterProvider.builder().registerMetricReader(metricReader).build();
|
||||||
|
|
||||||
|
OperationListener listener = HttpServerMetrics.get().create(meterProvider.get("test"));
|
||||||
|
|
||||||
|
Attributes requestAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(HttpAttributes.HTTP_REQUEST_METHOD, "GET")
|
||||||
|
.put(UrlAttributes.URL_SCHEME, "https")
|
||||||
|
.put(UrlAttributes.URL_PATH, "/")
|
||||||
|
.put(UrlAttributes.URL_QUERY, "q=a")
|
||||||
|
.put(NetworkAttributes.NETWORK_TRANSPORT, "tcp")
|
||||||
|
.put(NetworkAttributes.NETWORK_TYPE, "ipv4")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_NAME, "http")
|
||||||
|
.put(NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0")
|
||||||
|
.put(NetworkAttributes.SERVER_ADDRESS, "localhost")
|
||||||
|
.put(NetworkAttributes.SERVER_PORT, 1234)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Attributes responseAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200)
|
||||||
|
.put(HttpAttributes.HTTP_REQUEST_BODY_SIZE, 100)
|
||||||
|
.put(HttpAttributes.HTTP_RESPONSE_BODY_SIZE, 200)
|
||||||
|
.put(NetworkAttributes.CLIENT_SOCKET_ADDRESS, "1.2.3.4")
|
||||||
|
.put(NetworkAttributes.CLIENT_SOCKET_PORT, 8080)
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_ADDRESS, "4.3.2.1")
|
||||||
|
.put(NetworkAttributes.SERVER_SOCKET_PORT, 9090)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
SpanContext spanContext1 =
|
||||||
|
SpanContext.create(
|
||||||
|
"ff01020304050600ff0a0b0c0d0e0f00",
|
||||||
|
"090a0b0c0d0e0f00",
|
||||||
|
TraceFlags.getSampled(),
|
||||||
|
TraceState.getDefault());
|
||||||
|
SpanContext spanContext2 =
|
||||||
|
SpanContext.create(
|
||||||
|
"123456789abcdef00000000000999999",
|
||||||
|
"abcde00000054321",
|
||||||
|
TraceFlags.getSampled(),
|
||||||
|
TraceState.getDefault());
|
||||||
|
|
||||||
|
Context parent1 = Context.root().with(Span.wrap(spanContext1));
|
||||||
|
Context context1 = listener.onStart(parent1, requestAttributes, nanos(100));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfiesExactlyInAnyOrder(
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.active_requests")
|
||||||
|
.hasDescription(
|
||||||
|
"The number of concurrent HTTP requests that are currently in-flight")
|
||||||
|
.hasUnit("{requests}")
|
||||||
|
.hasLongSumSatisfying(
|
||||||
|
sum ->
|
||||||
|
sum.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasValue(1)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234L))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext1.getTraceId())
|
||||||
|
.hasSpanId(spanContext1.getSpanId())))));
|
||||||
|
|
||||||
|
Context parent2 = Context.root().with(Span.wrap(spanContext2));
|
||||||
|
Context context2 = listener.onStart(parent2, requestAttributes, nanos(150));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfiesExactlyInAnyOrder(
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.active_requests")
|
||||||
|
.hasLongSumSatisfying(
|
||||||
|
sum ->
|
||||||
|
sum.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasValue(2)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234L))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext2.getTraceId())
|
||||||
|
.hasSpanId(spanContext2.getSpanId())))));
|
||||||
|
|
||||||
|
listener.onEnd(context1, responseAttributes, nanos(250));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfiesExactlyInAnyOrder(
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.active_requests")
|
||||||
|
.hasLongSumSatisfying(
|
||||||
|
sum ->
|
||||||
|
sum.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasValue(1)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234L))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext1.getTraceId())
|
||||||
|
.hasSpanId(spanContext1.getSpanId())))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.duration")
|
||||||
|
.hasUnit("s")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(0.15 /* seconds */)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0"),
|
||||||
|
equalTo(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234L))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext1.getTraceId())
|
||||||
|
.hasSpanId(spanContext1.getSpanId()))
|
||||||
|
.hasBucketBoundaries(DURATION_BUCKETS))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.request.size")
|
||||||
|
.hasUnit("By")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(100 /* bytes */)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0"),
|
||||||
|
equalTo(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234L))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext1.getTraceId())
|
||||||
|
.hasSpanId(spanContext1.getSpanId())))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.response.size")
|
||||||
|
.hasUnit("By")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(200 /* bytes */)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(HttpAttributes.HTTP_REQUEST_METHOD, "GET"),
|
||||||
|
equalTo(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_NAME, "http"),
|
||||||
|
equalTo(
|
||||||
|
NetworkAttributes.NETWORK_PROTOCOL_VERSION, "2.0"),
|
||||||
|
equalTo(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "localhost"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_PORT, 1234L))
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext1.getTraceId())
|
||||||
|
.hasSpanId(spanContext1.getSpanId())))));
|
||||||
|
|
||||||
|
listener.onEnd(context2, responseAttributes, nanos(300));
|
||||||
|
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfiesExactlyInAnyOrder(
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.active_requests")
|
||||||
|
.hasLongSumSatisfying(
|
||||||
|
sum ->
|
||||||
|
sum.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasValue(0)
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext2.getTraceId())
|
||||||
|
.hasSpanId(spanContext2.getSpanId())))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.duration")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(0.3 /* seconds */)
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext2.getTraceId())
|
||||||
|
.hasSpanId(spanContext2.getSpanId())))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.request.size")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(200 /* bytes */)
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext2.getTraceId())
|
||||||
|
.hasSpanId(spanContext2.getSpanId())))),
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.response.size")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(400 /* bytes */)
|
||||||
|
.hasExemplarsSatisfying(
|
||||||
|
exemplar ->
|
||||||
|
exemplar
|
||||||
|
.hasTraceId(spanContext2.getTraceId())
|
||||||
|
.hasSpanId(spanContext2.getSpanId())))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void collectsHttpRouteFromEndAttributes() {
|
||||||
|
// given
|
||||||
|
InMemoryMetricReader metricReader = InMemoryMetricReader.create();
|
||||||
|
SdkMeterProvider meterProvider =
|
||||||
|
SdkMeterProvider.builder().registerMetricReader(metricReader).build();
|
||||||
|
|
||||||
|
OperationListener listener = HttpServerMetrics.get().create(meterProvider.get("test"));
|
||||||
|
|
||||||
|
Attributes requestAttributes =
|
||||||
|
Attributes.builder()
|
||||||
|
.put(NetworkAttributes.SERVER_ADDRESS, "host")
|
||||||
|
.put(UrlAttributes.URL_SCHEME, "https")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Attributes responseAttributes =
|
||||||
|
Attributes.builder().put(SemanticAttributes.HTTP_ROUTE, "/test/{id}").build();
|
||||||
|
|
||||||
|
Context parentContext = Context.root();
|
||||||
|
|
||||||
|
// when
|
||||||
|
Context context = listener.onStart(parentContext, requestAttributes, nanos(100));
|
||||||
|
listener.onEnd(context, responseAttributes, nanos(200));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.anySatisfy(
|
||||||
|
metric ->
|
||||||
|
assertThat(metric)
|
||||||
|
.hasName("http.server.duration")
|
||||||
|
.hasUnit("s")
|
||||||
|
.hasHistogramSatisfying(
|
||||||
|
histogram ->
|
||||||
|
histogram.hasPointsSatisfying(
|
||||||
|
point ->
|
||||||
|
point
|
||||||
|
.hasSum(0.100 /* seconds */)
|
||||||
|
.hasAttributesSatisfying(
|
||||||
|
equalTo(UrlAttributes.URL_SCHEME, "https"),
|
||||||
|
equalTo(NetworkAttributes.SERVER_ADDRESS, "host"),
|
||||||
|
equalTo(
|
||||||
|
SemanticAttributes.HTTP_ROUTE, "/test/{id}")))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long nanos(int millis) {
|
||||||
|
return TimeUnit.MILLISECONDS.toNanos(millis);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue