Make HttpClientMetrics report low cardinality metrics (#5319)
This commit is contained in:
parent
edfd842185
commit
ea31ca8be2
|
|
@ -36,8 +36,10 @@ final class TemporaryMetricsView {
|
||||||
private static Set<AttributeKey> buildDurationClientView() {
|
private static Set<AttributeKey> buildDurationClientView() {
|
||||||
// We pull identifying attributes according to:
|
// We pull identifying attributes according to:
|
||||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#attribute-alternatives
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/http-metrics.md#attribute-alternatives
|
||||||
|
// We only pull net.peer.name and net.peer.port because http.url has too high cardinality
|
||||||
Set<AttributeKey> view = new HashSet<>(durationAlwaysInclude);
|
Set<AttributeKey> view = new HashSet<>(durationAlwaysInclude);
|
||||||
view.add(SemanticAttributes.HTTP_URL);
|
view.add(SemanticAttributes.NET_PEER_NAME);
|
||||||
|
view.add(SemanticAttributes.NET_PEER_PORT);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,31 +95,10 @@ final class TemporaryMetricsView {
|
||||||
(BiConsumer<AttributeKey, Object>)
|
(BiConsumer<AttributeKey, Object>)
|
||||||
(key, value) -> {
|
(key, value) -> {
|
||||||
if (view.contains(key)) {
|
if (view.contains(key)) {
|
||||||
// For now, we filter query parameters out of URLs in metrics.
|
filtered.put(key, value);
|
||||||
if (SemanticAttributes.HTTP_URL.equals(key)
|
|
||||||
|| SemanticAttributes.HTTP_TARGET.equals(key)) {
|
|
||||||
filtered.put(key, removeQueryParamFromUrlOrTarget(value.toString()));
|
|
||||||
} else {
|
|
||||||
filtered.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to handle cleaning URLs like http://myServer;jsessionId=1 or targets like
|
|
||||||
// /my/path?queryParam=2
|
|
||||||
private static String removeQueryParamFromUrlOrTarget(String urlOrTarget) {
|
|
||||||
// Note: Maybe not the most robust, but purely to limit cardinality.
|
|
||||||
int idx = -1;
|
|
||||||
for (int i = 0; i < urlOrTarget.length(); ++i) {
|
|
||||||
char ch = urlOrTarget.charAt(i);
|
|
||||||
if (ch == '?' || ch == ';') {
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return idx == -1 ? urlOrTarget : urlOrTarget.substring(0, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TemporaryMetricsView() {}
|
private TemporaryMetricsView() {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,9 @@ class HttpClientMetricsTest {
|
||||||
.put("http.host", "host")
|
.put("http.host", "host")
|
||||||
.put("http.target", "/")
|
.put("http.target", "/")
|
||||||
.put("http.scheme", "https")
|
.put("http.scheme", "https")
|
||||||
.put("net.host.name", "localhost")
|
.put("net.peer.name", "localhost")
|
||||||
.put("net.host.port", 1234)
|
.put("net.peer.ip", "0.0.0.0")
|
||||||
|
.put("net.peer.port", 1234)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Attributes responseAttributes =
|
Attributes responseAttributes =
|
||||||
|
|
@ -87,7 +88,8 @@ class HttpClientMetricsTest {
|
||||||
.hasSum(150 /* millis */)
|
.hasSum(150 /* millis */)
|
||||||
.attributes()
|
.attributes()
|
||||||
.containsOnly(
|
.containsOnly(
|
||||||
attributeEntry("http.url", "https://localhost:1234/"),
|
attributeEntry("net.peer.name", "localhost"),
|
||||||
|
attributeEntry("net.peer.port", 1234),
|
||||||
attributeEntry("http.method", "GET"),
|
attributeEntry("http.method", "GET"),
|
||||||
attributeEntry("http.flavor", "2.0"),
|
attributeEntry("http.flavor", "2.0"),
|
||||||
attributeEntry("http.status_code", 200));
|
attributeEntry("http.status_code", 200));
|
||||||
|
|
|
||||||
|
|
@ -15,37 +15,10 @@ import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions;
|
||||||
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;
|
||||||
|
|
||||||
public class TemporaryMetricsViewTest {
|
class TemporaryMetricsViewTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldApplyClientDurationView_urlIdentity() {
|
void shouldApplyClientDurationView() {
|
||||||
Attributes startAttributes =
|
|
||||||
Attributes.builder()
|
|
||||||
.put(SemanticAttributes.HTTP_URL, "https://somehost/high/cardinality/12345")
|
|
||||||
.put(SemanticAttributes.HTTP_SCHEME, "https")
|
|
||||||
.put(SemanticAttributes.HTTP_METHOD, "GET")
|
|
||||||
.put(SemanticAttributes.HTTP_HOST, "somehost")
|
|
||||||
.put(SemanticAttributes.HTTP_TARGET, "/high/cardinality/12345")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Attributes endAttributes =
|
|
||||||
Attributes.builder()
|
|
||||||
.put(SemanticAttributes.HTTP_STATUS_CODE, 500)
|
|
||||||
.put(SemanticAttributes.NET_PEER_NAME, "somehost2")
|
|
||||||
.put(SemanticAttributes.NET_PEER_IP, "127.0.0.1")
|
|
||||||
.put(SemanticAttributes.NET_PEER_PORT, 443)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
OpenTelemetryAssertions.assertThat(applyClientDurationView(startAttributes, endAttributes))
|
|
||||||
.containsOnly(
|
|
||||||
attributeEntry(
|
|
||||||
SemanticAttributes.HTTP_URL.getKey(), "https://somehost/high/cardinality/12345"),
|
|
||||||
attributeEntry(SemanticAttributes.HTTP_METHOD.getKey(), "GET"),
|
|
||||||
attributeEntry(SemanticAttributes.HTTP_STATUS_CODE.getKey(), 500));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldApplyClientDurationView_urlWithQuery() {
|
|
||||||
Attributes startAttributes =
|
Attributes startAttributes =
|
||||||
Attributes.builder()
|
Attributes.builder()
|
||||||
.put(
|
.put(
|
||||||
|
|
@ -67,14 +40,14 @@ public class TemporaryMetricsViewTest {
|
||||||
|
|
||||||
OpenTelemetryAssertions.assertThat(applyClientDurationView(startAttributes, endAttributes))
|
OpenTelemetryAssertions.assertThat(applyClientDurationView(startAttributes, endAttributes))
|
||||||
.containsOnly(
|
.containsOnly(
|
||||||
attributeEntry(
|
attributeEntry(SemanticAttributes.NET_PEER_NAME.getKey(), "somehost2"),
|
||||||
SemanticAttributes.HTTP_URL.getKey(), "https://somehost/high/cardinality/12345"),
|
attributeEntry(SemanticAttributes.NET_PEER_PORT.getKey(), 443),
|
||||||
attributeEntry(SemanticAttributes.HTTP_METHOD.getKey(), "GET"),
|
attributeEntry(SemanticAttributes.HTTP_METHOD.getKey(), "GET"),
|
||||||
attributeEntry(SemanticAttributes.HTTP_STATUS_CODE.getKey(), 500));
|
attributeEntry(SemanticAttributes.HTTP_STATUS_CODE.getKey(), 500));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldApplyServerDurationView_withRoute() {
|
void shouldApplyServerDurationView() {
|
||||||
Attributes startAttributes =
|
Attributes startAttributes =
|
||||||
Attributes.builder()
|
Attributes.builder()
|
||||||
.put(SemanticAttributes.HTTP_METHOD, "GET")
|
.put(SemanticAttributes.HTTP_METHOD, "GET")
|
||||||
|
|
@ -110,7 +83,7 @@ public class TemporaryMetricsViewTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldApplyActiveRequestsView() {
|
void shouldApplyActiveRequestsView() {
|
||||||
Attributes attributes =
|
Attributes attributes =
|
||||||
Attributes.builder()
|
Attributes.builder()
|
||||||
.put(SemanticAttributes.HTTP_METHOD, "GET")
|
.put(SemanticAttributes.HTTP_METHOD, "GET")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue