Redact query string values for http client spans (#13114)
Co-authored-by: Steve Rao <raozihao.rzh@alibaba-inc.com> Co-authored-by: Lauri Tulmin <tulmin@gmail.com> Co-authored-by: Lauri Tulmin <ltulmin@splunk.com> Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
parent
c93eecb2f0
commit
5b287e3db0
|
@ -20,6 +20,7 @@ import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
|
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
|
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
|
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor;
|
||||||
|
import io.opentelemetry.instrumentation.api.internal.Experimental;
|
||||||
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
|
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractor;
|
||||||
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
|
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
|
||||||
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter;
|
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter;
|
||||||
|
@ -177,6 +178,18 @@ public final class DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the instrumentation to redact sensitive URL parameters.
|
||||||
|
*
|
||||||
|
* @param redactQueryParameters {@code true} if the sensitive URL parameters have to be redacted.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setRedactQueryParameters(
|
||||||
|
boolean redactQueryParameters) {
|
||||||
|
Experimental.setRedactQueryParameters(httpAttributesExtractorBuilder, redactQueryParameters);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets custom {@link SpanNameExtractor} via transform function. */
|
/** Sets custom {@link SpanNameExtractor} via transform function. */
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setSpanNameExtractor(
|
public DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> setSpanNameExtractor(
|
||||||
|
@ -225,6 +238,7 @@ public final class DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> {
|
||||||
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter))
|
.addAttributesExtractor(HttpExperimentalAttributesExtractor.create(attributesGetter))
|
||||||
.addOperationMetrics(HttpClientExperimentalMetrics.get());
|
.addOperationMetrics(HttpClientExperimentalMetrics.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
builderCustomizer.accept(builder);
|
builderCustomizer.accept(builder);
|
||||||
|
|
||||||
if (headerSetter != null) {
|
if (headerSetter != null) {
|
||||||
|
@ -248,6 +262,7 @@ public final class DefaultHttpClientInstrumenterBuilder<REQUEST, RESPONSE> {
|
||||||
set(
|
set(
|
||||||
config::shouldEmitExperimentalHttpClientTelemetry,
|
config::shouldEmitExperimentalHttpClientTelemetry,
|
||||||
this::setEmitExperimentalHttpClientMetrics);
|
this::setEmitExperimentalHttpClientMetrics);
|
||||||
|
set(config::redactQueryParameters, this::setRedactQueryParameters);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ public final class CommonConfig {
|
||||||
private final boolean statementSanitizationEnabled;
|
private final boolean statementSanitizationEnabled;
|
||||||
private final boolean emitExperimentalHttpClientTelemetry;
|
private final boolean emitExperimentalHttpClientTelemetry;
|
||||||
private final boolean emitExperimentalHttpServerTelemetry;
|
private final boolean emitExperimentalHttpServerTelemetry;
|
||||||
|
private final boolean redactQueryParameters;
|
||||||
private final String loggingTraceIdKey;
|
private final String loggingTraceIdKey;
|
||||||
private final String loggingSpanIdKey;
|
private final String loggingSpanIdKey;
|
||||||
private final String loggingTraceFlagsKey;
|
private final String loggingTraceFlagsKey;
|
||||||
|
@ -57,6 +58,9 @@ public final class CommonConfig {
|
||||||
config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true);
|
config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true);
|
||||||
emitExperimentalHttpClientTelemetry =
|
emitExperimentalHttpClientTelemetry =
|
||||||
config.getBoolean("otel.instrumentation.http.client.emit-experimental-telemetry", false);
|
config.getBoolean("otel.instrumentation.http.client.emit-experimental-telemetry", false);
|
||||||
|
redactQueryParameters =
|
||||||
|
config.getBoolean(
|
||||||
|
"otel.instrumentation.http.client.experimental.redact-query-parameters", true);
|
||||||
emitExperimentalHttpServerTelemetry =
|
emitExperimentalHttpServerTelemetry =
|
||||||
config.getBoolean("otel.instrumentation.http.server.emit-experimental-telemetry", false);
|
config.getBoolean("otel.instrumentation.http.server.emit-experimental-telemetry", false);
|
||||||
enduserConfig = new EnduserConfig(config);
|
enduserConfig = new EnduserConfig(config);
|
||||||
|
@ -111,6 +115,10 @@ public final class CommonConfig {
|
||||||
return emitExperimentalHttpServerTelemetry;
|
return emitExperimentalHttpServerTelemetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean redactQueryParameters() {
|
||||||
|
return redactQueryParameters;
|
||||||
|
}
|
||||||
|
|
||||||
public String getTraceIdKey() {
|
public String getTraceIdKey() {
|
||||||
return loggingTraceIdKey;
|
return loggingTraceIdKey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.api.internal;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
||||||
|
* any time.
|
||||||
|
*/
|
||||||
|
public final class Experimental {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static volatile BiConsumer<HttpClientAttributesExtractorBuilder<?, ?>, Boolean>
|
||||||
|
redactHttpClientQueryParameters;
|
||||||
|
|
||||||
|
private Experimental() {}
|
||||||
|
|
||||||
|
public static void setRedactQueryParameters(
|
||||||
|
HttpClientAttributesExtractorBuilder<?, ?> builder, boolean redactQueryParameters) {
|
||||||
|
if (redactHttpClientQueryParameters != null) {
|
||||||
|
redactHttpClientQueryParameters.accept(builder, redactQueryParameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void internalSetRedactHttpClientQueryParameters(
|
||||||
|
BiConsumer<HttpClientAttributesExtractorBuilder<?, ?>, Boolean>
|
||||||
|
redactHttpClientQueryParameters) {
|
||||||
|
Experimental.redactHttpClientQueryParameters = redactHttpClientQueryParameters;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,9 @@ import io.opentelemetry.instrumentation.api.semconv.network.internal.InternalNet
|
||||||
import io.opentelemetry.instrumentation.api.semconv.network.internal.InternalServerAttributesExtractor;
|
import io.opentelemetry.instrumentation.api.semconv.network.internal.InternalServerAttributesExtractor;
|
||||||
import io.opentelemetry.semconv.HttpAttributes;
|
import io.opentelemetry.semconv.HttpAttributes;
|
||||||
import io.opentelemetry.semconv.UrlAttributes;
|
import io.opentelemetry.semconv.UrlAttributes;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.ToIntFunction;
|
import java.util.function.ToIntFunction;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@ -32,6 +35,9 @@ public final class HttpClientAttributesExtractor<REQUEST, RESPONSE>
|
||||||
REQUEST, RESPONSE, HttpClientAttributesGetter<REQUEST, RESPONSE>>
|
REQUEST, RESPONSE, HttpClientAttributesGetter<REQUEST, RESPONSE>>
|
||||||
implements SpanKeyProvider {
|
implements SpanKeyProvider {
|
||||||
|
|
||||||
|
private static final Set<String> PARAMS_TO_REDACT =
|
||||||
|
new HashSet<>(Arrays.asList("AWSAccessKeyId", "Signature", "sig", "X-Goog-Signature"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the HTTP client attributes extractor with default configuration.
|
* Creates the HTTP client attributes extractor with default configuration.
|
||||||
*
|
*
|
||||||
|
@ -54,6 +60,7 @@ public final class HttpClientAttributesExtractor<REQUEST, RESPONSE>
|
||||||
private final InternalNetworkAttributesExtractor<REQUEST, RESPONSE> internalNetworkExtractor;
|
private final InternalNetworkAttributesExtractor<REQUEST, RESPONSE> internalNetworkExtractor;
|
||||||
private final InternalServerAttributesExtractor<REQUEST> internalServerExtractor;
|
private final InternalServerAttributesExtractor<REQUEST> internalServerExtractor;
|
||||||
private final ToIntFunction<Context> resendCountIncrementer;
|
private final ToIntFunction<Context> resendCountIncrementer;
|
||||||
|
private final boolean redactQueryParameters;
|
||||||
|
|
||||||
HttpClientAttributesExtractor(HttpClientAttributesExtractorBuilder<REQUEST, RESPONSE> builder) {
|
HttpClientAttributesExtractor(HttpClientAttributesExtractorBuilder<REQUEST, RESPONSE> builder) {
|
||||||
super(
|
super(
|
||||||
|
@ -65,6 +72,7 @@ public final class HttpClientAttributesExtractor<REQUEST, RESPONSE>
|
||||||
internalNetworkExtractor = builder.buildNetworkExtractor();
|
internalNetworkExtractor = builder.buildNetworkExtractor();
|
||||||
internalServerExtractor = builder.buildServerExtractor();
|
internalServerExtractor = builder.buildServerExtractor();
|
||||||
resendCountIncrementer = builder.resendCountIncrementer;
|
resendCountIncrementer = builder.resendCountIncrementer;
|
||||||
|
redactQueryParameters = builder.redactQueryParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -104,11 +112,21 @@ public final class HttpClientAttributesExtractor<REQUEST, RESPONSE>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static String stripSensitiveData(@Nullable String url) {
|
private String stripSensitiveData(@Nullable String url) {
|
||||||
if (url == null || url.isEmpty()) {
|
if (url == null || url.isEmpty()) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url = redactUserInfo(url);
|
||||||
|
|
||||||
|
if (redactQueryParameters) {
|
||||||
|
url = redactQueryParameters(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String redactUserInfo(String url) {
|
||||||
int schemeEndIndex = url.indexOf(':');
|
int schemeEndIndex = url.indexOf(':');
|
||||||
|
|
||||||
if (schemeEndIndex == -1) {
|
if (schemeEndIndex == -1) {
|
||||||
|
@ -145,4 +163,57 @@ public final class HttpClientAttributesExtractor<REQUEST, RESPONSE>
|
||||||
}
|
}
|
||||||
return url.substring(0, schemeEndIndex + 3) + "REDACTED:REDACTED" + url.substring(atIndex);
|
return url.substring(0, schemeEndIndex + 3) + "REDACTED:REDACTED" + url.substring(atIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String redactQueryParameters(String url) {
|
||||||
|
int questionMarkIndex = url.indexOf('?');
|
||||||
|
if (questionMarkIndex == -1 || !containsParamToRedact(url)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder urlAfterQuestionMark = new StringBuilder();
|
||||||
|
|
||||||
|
// To build a parameter name until we reach the '=' character
|
||||||
|
// If the parameter name is a one to redact, we will redact the value
|
||||||
|
StringBuilder currentParamName = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = questionMarkIndex + 1; i < url.length(); i++) {
|
||||||
|
char currentChar = url.charAt(i);
|
||||||
|
|
||||||
|
if (currentChar == '=') {
|
||||||
|
urlAfterQuestionMark.append('=');
|
||||||
|
if (PARAMS_TO_REDACT.contains(currentParamName.toString())) {
|
||||||
|
urlAfterQuestionMark.append("REDACTED");
|
||||||
|
// skip over parameter value
|
||||||
|
for (; i + 1 < url.length(); i++) {
|
||||||
|
char c = url.charAt(i + 1);
|
||||||
|
if (c == '&' || c == '#') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (currentChar == '&') { // New parameter delimiter
|
||||||
|
urlAfterQuestionMark.append(currentChar);
|
||||||
|
// To avoid creating a new StringBuilder for each new parameter
|
||||||
|
currentParamName.setLength(0);
|
||||||
|
} else if (currentChar == '#') { // Reference delimiter
|
||||||
|
urlAfterQuestionMark.append(url.substring(i));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// param values can be appended to currentParamName here but it's not an issue
|
||||||
|
currentParamName.append(currentChar);
|
||||||
|
urlAfterQuestionMark.append(currentChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.substring(0, questionMarkIndex) + "?" + urlAfterQuestionMark;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean containsParamToRedact(String urlpart) {
|
||||||
|
for (String param : PARAMS_TO_REDACT) {
|
||||||
|
if (urlpart.contains(param)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
|
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
|
||||||
|
import io.opentelemetry.instrumentation.api.internal.Experimental;
|
||||||
import io.opentelemetry.instrumentation.api.internal.HttpConstants;
|
import io.opentelemetry.instrumentation.api.internal.HttpConstants;
|
||||||
import io.opentelemetry.instrumentation.api.semconv.network.internal.AddressAndPortExtractor;
|
import io.opentelemetry.instrumentation.api.semconv.network.internal.AddressAndPortExtractor;
|
||||||
import io.opentelemetry.instrumentation.api.semconv.network.internal.InternalNetworkAttributesExtractor;
|
import io.opentelemetry.instrumentation.api.semconv.network.internal.InternalNetworkAttributesExtractor;
|
||||||
|
@ -37,6 +38,12 @@ public final class HttpClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
|
||||||
List<String> capturedResponseHeaders = emptyList();
|
List<String> capturedResponseHeaders = emptyList();
|
||||||
Set<String> knownMethods = HttpConstants.KNOWN_METHODS;
|
Set<String> knownMethods = HttpConstants.KNOWN_METHODS;
|
||||||
ToIntFunction<Context> resendCountIncrementer = HttpClientRequestResendCount::getAndIncrement;
|
ToIntFunction<Context> resendCountIncrementer = HttpClientRequestResendCount::getAndIncrement;
|
||||||
|
boolean redactQueryParameters;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Experimental.internalSetRedactHttpClientQueryParameters(
|
||||||
|
(builder, redact) -> builder.redactQueryParameters = redact);
|
||||||
|
}
|
||||||
|
|
||||||
HttpClientAttributesExtractorBuilder(
|
HttpClientAttributesExtractorBuilder(
|
||||||
HttpClientAttributesGetter<REQUEST, RESPONSE> httpAttributesGetter) {
|
HttpClientAttributesGetter<REQUEST, RESPONSE> httpAttributesGetter) {
|
||||||
|
|
|
@ -23,12 +23,14 @@ import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.assertj.core.api.Assertions.entry;
|
import static org.assertj.core.api.Assertions.entry;
|
||||||
|
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||||
|
|
||||||
import io.opentelemetry.api.common.AttributeKey;
|
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.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
|
||||||
|
import io.opentelemetry.instrumentation.api.internal.Experimental;
|
||||||
import io.opentelemetry.instrumentation.api.internal.HttpConstants;
|
import io.opentelemetry.instrumentation.api.internal.HttpConstants;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -36,9 +38,13 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.ToIntFunction;
|
import java.util.function.ToIntFunction;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
@ -200,6 +206,93 @@ class HttpClientAttributesExtractorTest {
|
||||||
entry(NETWORK_PEER_PORT, 456L));
|
entry(NETWORK_PEER_PORT, 456L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ArgumentsSource(UrlSourceToRedact.class)
|
||||||
|
void shouldRedactUserInfoAndQueryParameters(String url, String expectedResult) {
|
||||||
|
Map<String, String> request = new HashMap<>();
|
||||||
|
request.put("urlFull", url);
|
||||||
|
|
||||||
|
HttpClientAttributesExtractorBuilder<Map<String, String>, Map<String, String>> builder =
|
||||||
|
HttpClientAttributesExtractor.builder(new TestHttpClientAttributesGetter());
|
||||||
|
Experimental.setRedactQueryParameters(builder, true);
|
||||||
|
AttributesExtractor<Map<String, String>, Map<String, String>> extractor = builder.build();
|
||||||
|
|
||||||
|
AttributesBuilder attributes = Attributes.builder();
|
||||||
|
extractor.onStart(attributes, Context.root(), request);
|
||||||
|
|
||||||
|
assertThat(attributes.build()).containsOnly(entry(URL_FULL, expectedResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class UrlSourceToRedact implements ArgumentsProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
||||||
|
return Stream.of(
|
||||||
|
arguments("https://user1:secret@github.com", "https://REDACTED:REDACTED@github.com"),
|
||||||
|
arguments(
|
||||||
|
"https://user1:secret@github.com/path/",
|
||||||
|
"https://REDACTED:REDACTED@github.com/path/"),
|
||||||
|
arguments(
|
||||||
|
"https://user1:secret@github.com#test.html",
|
||||||
|
"https://REDACTED:REDACTED@github.com#test.html"),
|
||||||
|
arguments(
|
||||||
|
"https://user1:secret@github.com?foo=b@r",
|
||||||
|
"https://REDACTED:REDACTED@github.com?foo=b@r"),
|
||||||
|
arguments(
|
||||||
|
"https://user1:secret@github.com/p@th?foo=b@r",
|
||||||
|
"https://REDACTED:REDACTED@github.com/p@th?foo=b@r"),
|
||||||
|
arguments("https://github.com/p@th?foo=b@r", "https://github.com/p@th?foo=b@r"),
|
||||||
|
arguments("https://github.com#t@st.html", "https://github.com#t@st.html"),
|
||||||
|
arguments("user1:secret@github.com", "user1:secret@github.com"),
|
||||||
|
arguments("https://github.com@", "https://github.com@"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?paramA=valA¶mB=valB",
|
||||||
|
"https://service.com?paramA=valA¶mB=valB"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7",
|
||||||
|
"https://service.com?AWSAccessKeyId=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?Signature=39Up9jzHkxhuIhFE9594DJxe7w6cIRCg0V6ICGS0%3A377",
|
||||||
|
"https://service.com?Signature=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?sig=39Up9jzHkxhuIhFE9594DJxe7w6cIRCg0V6ICGS0",
|
||||||
|
"https://service.com?sig=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?X-Goog-Signature=39Up9jzHkxhuIhFE9594DJxe7w6cIRCg0V6ICGS0",
|
||||||
|
"https://service.com?X-Goog-Signature=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?paramA=valA&AWSAccessKeyId=AKIAIOSFODNN7¶mB=valB",
|
||||||
|
"https://service.com?paramA=valA&AWSAccessKeyId=REDACTED¶mB=valB"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7¶mA=valA",
|
||||||
|
"https://service.com?AWSAccessKeyId=REDACTED¶mA=valA"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?paramA=valA&AWSAccessKeyId=AKIAIOSFODNN7",
|
||||||
|
"https://service.com?paramA=valA&AWSAccessKeyId=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7&AWSAccessKeyId=ZGIAIOSFODNN7",
|
||||||
|
"https://service.com?AWSAccessKeyId=REDACTED&AWSAccessKeyId=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7#ref",
|
||||||
|
"https://service.com?AWSAccessKeyId=REDACTED#ref"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7&aa&bb",
|
||||||
|
"https://service.com?AWSAccessKeyId=REDACTED&aa&bb"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?aa&bb&AWSAccessKeyId=AKIAIOSFODNN7",
|
||||||
|
"https://service.com?aa&bb&AWSAccessKeyId=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7&&",
|
||||||
|
"https://service.com?AWSAccessKeyId=REDACTED&&"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?&&AWSAccessKeyId=AKIAIOSFODNN7",
|
||||||
|
"https://service.com?&&AWSAccessKeyId=REDACTED"),
|
||||||
|
arguments(
|
||||||
|
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7&a&b#fragment",
|
||||||
|
"https://service.com?AWSAccessKeyId=REDACTED&a&b#fragment"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ValidRequestMethodsProvider.class)
|
@ArgumentsSource(ValidRequestMethodsProvider.class)
|
||||||
void shouldExtractKnownMethods(String requestMethod) {
|
void shouldExtractKnownMethods(String requestMethod) {
|
||||||
|
|
|
@ -311,6 +311,12 @@
|
||||||
"description": "Enable the capture of experimental HTTP client telemetry. Add the <code>http.request.body.size</code> and <code>http.response.body.size> attributes to spans, and record the <code>http.client.request.size</code> and <code>http.client.response.size</code> metrics.",
|
"description": "Enable the capture of experimental HTTP client telemetry. Add the <code>http.request.body.size</code> and <code>http.response.body.size> attributes to spans, and record the <code>http.client.request.size</code> and <code>http.client.response.size</code> metrics.",
|
||||||
"defaultValue": false
|
"defaultValue": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "otel.instrumentation.http.client.experimental.redact-query-parameters",
|
||||||
|
"type": "java.lang.Boolean",
|
||||||
|
"description": "Redact sensitive URL parameters. See https://opentelemetry.io/docs/specs/semconv/http/http-spans.",
|
||||||
|
"defaultValue": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "otel.instrumentation.http.known-methods",
|
"name": "otel.instrumentation.http.known-methods",
|
||||||
"type": "java.util.List<java.lang.String>",
|
"type": "java.util.List<java.lang.String>",
|
||||||
|
|
|
@ -293,4 +293,23 @@ class AbstractOtelSpringStarterSmokeTest extends AbstractSpringStarterSmokeTest
|
||||||
span.hasKind(SpanKind.SERVER).hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping"),
|
span.hasKind(SpanKind.SERVER).hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping"),
|
||||||
span -> withSpanAssert(span)));
|
span -> withSpanAssert(span)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRedactSomeUrlParameters() {
|
||||||
|
testing.clearAllExportedData();
|
||||||
|
|
||||||
|
RestTemplate restTemplate = restTemplateBuilder.rootUri("http://localhost:" + port).build();
|
||||||
|
restTemplate.getForObject(
|
||||||
|
"/test?X-Goog-Signature=39Up9jzHkxhuIhFE9594DJxe7w6cIRCg0V6ICGS0", String.class);
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
traceAssert ->
|
||||||
|
traceAssert.hasSpansSatisfyingExactly(
|
||||||
|
span ->
|
||||||
|
HttpSpanDataAssert.create(span)
|
||||||
|
.assertClientGetRequest("/test?X-Goog-Signature=REDACTED"),
|
||||||
|
span ->
|
||||||
|
span.hasKind(SpanKind.SERVER)
|
||||||
|
.hasAttribute(HttpAttributes.HTTP_ROUTE, "/test")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
public class OtelSpringStarterSmokeTestController {
|
public class OtelSpringStarterSmokeTestController {
|
||||||
|
|
||||||
public static final String PING = "/ping";
|
public static final String PING = "/ping";
|
||||||
|
public static final String TEST = "/test";
|
||||||
public static final String TEST_HISTOGRAM = "histogram-test-otel-spring-starter";
|
public static final String TEST_HISTOGRAM = "histogram-test-otel-spring-starter";
|
||||||
public static final String METER_SCOPE_NAME = "scope";
|
public static final String METER_SCOPE_NAME = "scope";
|
||||||
private final LongHistogram histogram;
|
private final LongHistogram histogram;
|
||||||
|
@ -33,4 +34,9 @@ public class OtelSpringStarterSmokeTestController {
|
||||||
component.withSpanMethod("from-controller");
|
component.withSpanMethod("from-controller");
|
||||||
return "pong";
|
return "pong";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(TEST)
|
||||||
|
public String testUrlToRedact() {
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue