Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8ef3825
Attach request object to event for OTel
adinauer Jan 23, 2025
d3cab7c
fix test name
adinauer Jan 23, 2025
7c51a68
Merge branch 'main' into feat/request-for-otel
adinauer Jan 23, 2025
9a183ab
add http server request headers to sentry request in payload
adinauer Jan 23, 2025
ecd6040
rename test class
adinauer Jan 23, 2025
6e0b24d
changelog
adinauer Jan 23, 2025
3f10e3c
Merge branch 'main' into feat/request-for-otel
adinauer Jan 23, 2025
28cb76a
Merge branch 'main' into feat/request-for-otel
adinauer Jan 24, 2025
28a7e56
do not override existing url on request even with full url
adinauer Jan 24, 2025
c59f801
Merge branch 'feat/request-for-otel' into feat/otel-server-request-he…
adinauer Jan 24, 2025
0f77ec2
pass in options and use them
adinauer Jan 24, 2025
66d3b7b
remove span param; remove test exception
adinauer Jan 24, 2025
0d96359
changelog
adinauer Jan 24, 2025
ba2cbb0
changelog pii
adinauer Jan 24, 2025
1d89f04
Merge branch 'main' into feat/otel-server-request-headers
adinauer Jan 24, 2025
e1f7fec
Use `java.net.URL` for combining url attributes (#4105)
adinauer Jan 30, 2025
d698a14
changelog
adinauer Feb 3, 2025
12aca6b
do not send request headers in contexts/otel/attributes
adinauer Feb 3, 2025
90fb3d9
Merge branch 'main' into feat/otel-server-request-headers
adinauer Feb 20, 2025
3233b3c
also remove response headers from span attributes sent to Sentry
adinauer Feb 25, 2025
00d488d
Merge branch 'main' into feat/otel-server-request-headers
adinauer Feb 25, 2025
badf8ba
Apply suggestions from code review
adinauer Feb 25, 2025
8bc3f0b
Merge branch 'main' into feat/otel-server-request-headers
adinauer Feb 26, 2025
b2fef65
Merge branch 'main' into feat/otel-server-request-headers
adinauer Feb 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

### Features

- Add HTTP server request headers from OpenTelemetry span attributes to sentry `request` in payload ([#4102](https://github.com/getsentry/sentry-java/pull/4102))
- You have to explicitly enable each header by adding it to the [OpenTelemetry config](https://opentelemetry.io/docs/zero-code/java/agent/instrumentation/http/#capturing-http-request-and-response-headers)
- Please only enable headers you actually want to send to Sentry. Some may contain sensitive data like PII, cookies, tokens etc.
- We are no longer adding request/response headers to `contexts/otel/attributes` of the event.
- The `ignoredErrors` option is now configurable via the manifest property `io.sentry.traces.ignored-errors` ([#4178](https://github.com/getsentry/sentry-java/pull/4178))
- A list of active Spring profiles is attached to payloads sent to Sentry (errors, traces, etc.) and displayed in the UI when using our Spring or Spring Boot integrations ([#4147](https://github.com/getsentry/sentry-java/pull/4147))
- This consists of an empty list when only the default profile is active
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
public final class io/sentry/opentelemetry/OpenTelemetryAttributesExtractor {
public fun <init> ()V
public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/ISpan;Lio/sentry/IScope;)V
public fun extractUrl (Lio/opentelemetry/api/common/Attributes;)Ljava/lang/String;
public fun extract (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/IScope;Lio/sentry/SentryOptions;)V
public fun extractUrl (Lio/opentelemetry/api/common/Attributes;Lio/sentry/SentryOptions;)Ljava/lang/String;
}

public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,39 @@
import io.opentelemetry.semconv.ServerAttributes;
import io.opentelemetry.semconv.UrlAttributes;
import io.sentry.IScope;
import io.sentry.ISpan;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.protocol.Request;
import io.sentry.util.HttpUtils;
import io.sentry.util.StringUtils;
import io.sentry.util.UrlUtils;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Internal
public final class OpenTelemetryAttributesExtractor {

private static final String HTTP_REQUEST_HEADER_PREFIX = "http.request.header.";

public void extract(
final @NotNull SpanData otelSpan,
final @NotNull ISpan sentrySpan,
final @NotNull IScope scope) {
final @NotNull IScope scope,
final @NotNull SentryOptions options) {
final @NotNull Attributes attributes = otelSpan.getAttributes();
addRequestAttributesToScope(attributes, scope);
if (attributes.get(HttpAttributes.HTTP_REQUEST_METHOD) != null) {
addRequestAttributesToScope(attributes, scope, options);
}
}

private void addRequestAttributesToScope(
final @NotNull Attributes attributes, final @NotNull IScope scope) {
final @NotNull Attributes attributes,
final @NotNull IScope scope,
final @NotNull SentryOptions options) {
if (scope.getRequest() == null) {
scope.setRequest(new Request());
}
Expand All @@ -37,7 +50,7 @@ private void addRequestAttributesToScope(
}

if (request.getUrl() == null) {
final @Nullable String url = extractUrl(attributes);
final @Nullable String url = extractUrl(attributes, options);
if (url != null) {
final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(url);
urlDetails.applyToRequest(request);
Expand All @@ -50,24 +63,69 @@ private void addRequestAttributesToScope(
request.setQueryString(query);
}
}

if (request.getHeaders() == null) {
Map<String, String> headers = collectHeaders(attributes, options);
if (!headers.isEmpty()) {
request.setHeaders(headers);
}
}
}
}

public @Nullable String extractUrl(final @NotNull Attributes attributes) {
@SuppressWarnings("unchecked")
private static Map<String, String> collectHeaders(
final @NotNull Attributes attributes, final @NotNull SentryOptions options) {
Map<String, String> headers = new HashMap<>();

attributes.forEach(
(key, value) -> {
final @NotNull String attributeKeyAsString = key.getKey();
if (attributeKeyAsString.startsWith(HTTP_REQUEST_HEADER_PREFIX)) {
final @NotNull String headerName =
StringUtils.removePrefix(attributeKeyAsString, HTTP_REQUEST_HEADER_PREFIX);
if (options.isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) {
if (value instanceof List) {
try {
final @NotNull List<String> headerValues = (List<String>) value;
headers.put(
headerName,
toString(
HttpUtils.filterOutSecurityCookiesFromHeader(
headerValues, headerName, null)));
} catch (Throwable t) {
options
.getLogger()
.log(SentryLevel.WARNING, "Expected a List<String> as header", t);
}
}
}
}
});
return headers;
}

public @Nullable String extractUrl(
final @NotNull Attributes attributes, final @NotNull SentryOptions options) {
final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL);
if (urlFull != null) {
return urlFull;
}

final String urlString = buildUrlString(attributes);
final String urlString = buildUrlString(attributes, options);
if (!urlString.isEmpty()) {
return urlString;
}

return null;
}

private @NotNull String buildUrlString(final @NotNull Attributes attributes) {
private static @Nullable String toString(final @Nullable List<String> list) {
return list != null ? String.join(",", list) : null;
}

private @NotNull String buildUrlString(
final @NotNull Attributes attributes, final @NotNull SentryOptions options) {
final @Nullable String scheme = attributes.get(UrlAttributes.URL_SCHEME);
final @Nullable String serverAddress = attributes.get(ServerAttributes.SERVER_ADDRESS);
final @Nullable Long serverPort = attributes.get(ServerAttributes.SERVER_PORT);
Expand All @@ -77,22 +135,18 @@ private void addRequestAttributesToScope(
return "";
}

final @NotNull StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append(scheme);
urlBuilder.append("://");

if (serverAddress != null) {
urlBuilder.append(serverAddress);
if (serverPort != null) {
urlBuilder.append(":");
urlBuilder.append(serverPort);
try {
final @NotNull String pathToUse = path == null ? "" : path;
if (serverPort == null) {
return new URL(scheme, serverAddress, pathToUse).toString();
} else {
return new URL(scheme, serverAddress, serverPort.intValue(), pathToUse).toString();
}
} catch (Throwable t) {
options
.getLogger()
.log(SentryLevel.WARNING, "Unable to combine URL span attributes into one.", t);
return "";
}

if (path != null) {
urlBuilder.append(path);
}

return urlBuilder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.sentry.ScopesAdapter;
import io.sentry.Sentry;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.SentryTraceHeader;
import io.sentry.exception.InvalidSentryTraceHeaderException;
import io.sentry.util.TracingUtils;
Expand Down Expand Up @@ -76,7 +77,7 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
return;
}

final @Nullable String url = getUrl(sentrySpan);
final @Nullable String url = getUrl(sentrySpan, scopes.getOptions());
final @Nullable TracingUtils.TracingHeaders tracingHeaders =
url == null
? TracingUtils.trace(scopes, Collections.emptyList(), sentrySpan)
Expand All @@ -92,12 +93,13 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
}
}

private @Nullable String getUrl(final @NotNull IOtelSpanWrapper sentrySpan) {
private @Nullable String getUrl(
final @NotNull IOtelSpanWrapper sentrySpan, final @NotNull SentryOptions options) {
final @Nullable Attributes attributes = sentrySpan.getOpenTelemetrySpanAttributes();
if (attributes == null) {
return null;
}
return attributesExtractor.extractUrl(attributes);
return attributesExtractor.extractUrl(attributes, options);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ public final class SentrySpanExporter implements SpanExporter {
InternalSemanticAttributes.PARENT_SAMPLED.getKey(),
ProcessIncubatingAttributes.PROCESS_COMMAND_ARGS.getKey() // can be very long
);

private final @NotNull List<String> attributeToRemoveByPrefix =
Arrays.asList("http.request.header.", "http.response.header.");
private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60);

public static final String TRACE_ORIGIN = "auto.opentelemetry";
Expand Down Expand Up @@ -338,7 +341,8 @@ private void transferSpanDetails(
transferSpanDetails(sentrySpanMaybe, sentryTransaction);

scopesToUse.configureScope(
ScopeType.CURRENT, scope -> attributesExtractor.extract(span, sentryTransaction, scope));
ScopeType.CURRENT,
scope -> attributesExtractor.extract(span, scope, scopesToUse.getOptions()));

return sentryTransaction;
}
Expand Down Expand Up @@ -488,7 +492,17 @@ private SpanStatus mapOtelStatus(
}

private boolean shouldRemoveAttribute(final @NotNull String key) {
return attributeKeysToRemove.contains(key);
if (attributeKeysToRemove.contains(key)) {
return true;
}

for (String prefix : attributeToRemoveByPrefix) {
if (key.startsWith(prefix)) {
return true;
}
}

return false;
}

private void setOtelInstrumentationInfo(
Expand Down
Loading
Loading