Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### Fixes

- Avoid logging an error when a float is passed in the manifest ([#4031](https://github.com/getsentry/sentry-java/pull/4031))
- Add `request` details to transactions created through OpenTelemetry ([#4098](https://github.com/getsentry/sentry-java/pull/4098))
- We now add HTTP request method and URL where Sentry expects it to display it in Sentry UI
- Remove `java.lang.ClassNotFoundException` debug logs when searching for OpenTelemetry marker classes ([#4091](https://github.com/getsentry/sentry-java/pull/4091))
- There was up to three of these, one for `io.sentry.opentelemetry.agent.AgentMarker`, `io.sentry.opentelemetry.agent.AgentlessMarker` and `io.sentry.opentelemetry.agent.AgentlessSpringMarker`.
- These were not indicators of something being wrong but rather the SDK looking at what is available at runtime to configure itself accordingly.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
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;Lio/sentry/SentryOptions;)V
}

public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor {
public fun <init> ()V
public fun getOrder ()Ljava/lang/Long;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package io.sentry.opentelemetry;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.semconv.HttpAttributes;
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.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 SentryOptions options) {
final @NotNull Attributes attributes = otelSpan.getAttributes();
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 SentryOptions options) {
if (scope.getRequest() == null) {
scope.setRequest(new Request());
}
final @Nullable Request request = scope.getRequest();
if (request != null) {
final @Nullable String requestMethod = attributes.get(HttpAttributes.HTTP_REQUEST_METHOD);
if (requestMethod != null) {
request.setMethod(requestMethod);
}

if (request.getUrl() == null) {
final @Nullable String urlFull = attributes.get(UrlAttributes.URL_FULL);
if (urlFull != null) {
final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(urlFull);
urlDetails.applyToRequest(request);
}
}

if (request.getUrl() == null) {
final String urlString = buildUrlString(attributes);
if (!urlString.isEmpty()) {
request.setUrl(urlString);
}
}

if (request.getQueryString() == null) {
final @Nullable String query = attributes.get(UrlAttributes.URL_QUERY);
if (query != null) {
request.setQueryString(query);
}
}

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

@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)));
throw new RuntimeException("hey");
} catch (Throwable t) {
options
.getLogger()
.log(SentryLevel.WARNING, "Expected a List<String> as header", t);
}
}
}
}
});
return headers;
}

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 @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);
final @Nullable String path = attributes.get(UrlAttributes.URL_PATH);

if (scheme == null || serverAddress == null) {
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);
}
}

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

return urlBuilder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.sentry.ISpan;
import io.sentry.ITransaction;
import io.sentry.Instrumenter;
import io.sentry.ScopeType;
import io.sentry.ScopesAdapter;
import io.sentry.SentryDate;
import io.sentry.SentryInstantDate;
Expand Down Expand Up @@ -50,6 +51,8 @@ public final class SentrySpanExporter implements SpanExporter {
private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance();
private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor =
new SpanDescriptionExtractor();
private final @NotNull OpenTelemetryAttributesExtractor attributesExtractor =
new OpenTelemetryAttributesExtractor();
private final @NotNull IScopes scopes;

private final @NotNull List<String> attributeKeysToRemove =
Expand Down Expand Up @@ -267,8 +270,10 @@ private void transferSpanDetails(
spanStorage.getSentrySpan(span.getSpanContext());
final @Nullable IScopes scopesMaybe =
sentrySpanMaybe != null ? sentrySpanMaybe.getScopes() : null;
final @NotNull IScopes scopesToUse =
final @NotNull IScopes scopesToUseBeforeForking =
scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe;
final @NotNull IScopes scopesToUse =
scopesToUseBeforeForking.forkedCurrentScope("SentrySpanExporter.createTransaction");
final @NotNull OtelSpanInfo spanInfo =
spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe);

Expand Down Expand Up @@ -331,6 +336,11 @@ private void transferSpanDetails(
setOtelSpanKind(span, sentryTransaction);
transferSpanDetails(sentrySpanMaybe, sentryTransaction);

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

return sentryTransaction;
}

Expand Down
Loading