Skip to content

Commit

Permalink
Merge branch 'main' into lcian/feat/spring-profiles-event-processor
Browse files Browse the repository at this point in the history
  • Loading branch information
lcian authored Feb 17, 2025
2 parents 0a95314 + cf3d1e2 commit 27218c5
Show file tree
Hide file tree
Showing 64 changed files with 1,250 additions and 264 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report_java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ body:
- sentry-openfeign
- sentry-apache-http-client-5
- sentry-okhttp
- sentry-reactor
- other
validations:
required: true
Expand Down
20 changes: 19 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

## Unreleased

### Fixes

- `SentryOptions.setTracePropagationTargets` is no longer marked internal ([#4170](https://github.com/getsentry/sentry-java/pull/4170))

### Behavioural Changes

- The class `io.sentry.spring.jakarta.webflux.ReactorUtils` is now deprecated, please use `io.sentry.reactor.SentryReactorUtils` in the new `sentry-reactor` module instead ([#4155](https://github.com/getsentry/sentry-java/pull/4155))
- The new module will be exposed as an `api` dependency when using `sentry-spring-boot-jakarta` (Spring Boot 3) or `sentry-spring-jakarta` (Spring 6).
Therefore, if you're using one of those modules, changing your imports will suffice.

## 8.2.0

### Breaking Changes

- The Kotlin Language version is now set to 1.6 ([#3936](https://github.com/getsentry/sentry-java/pull/3936))
Expand All @@ -15,9 +27,14 @@
- Expose new `withSentryObservableEffect` method overload that accepts `SentryNavigationListener` as a parameter ([#4143](https://github.com/getsentry/sentry-java/pull/4143))
- This allows sharing the same `SentryNavigationListener` instance across fragments and composables to preserve the trace
- (Internal) Add API to filter native debug images based on stacktrace addresses ([#4089](https://github.com/getsentry/sentry-java/pull/4089))
- Propagate sampling random value ([#4153](https://github.com/getsentry/sentry-java/pull/4153))
- The random value used for sampling traces is now sent to Sentry and attached to the `baggage` header on outgoing requests
- Update `sampleRate` that is sent to Sentry and attached to the `baggage` header on outgoing requests ([#4158](https://github.com/getsentry/sentry-java/pull/4158))
- If the SDK uses its `sampleRate` or `tracesSampler` callback, it now updates the `sampleRate` in Dynamic Sampling Context.

### Fixes

- Log a warning when envelope or items are dropped due to rate limiting ([#4148](https://github.com/getsentry/sentry-java/pull/4148))
- Do not log if `OtelContextScopesStorage` cannot be found ([#4127](https://github.com/getsentry/sentry-java/pull/4127))
- Previously `java.lang.ClassNotFoundException: io.sentry.opentelemetry.OtelContextScopesStorage` was shown in the log if the class could not be found.
- This is just a lookup the SDK performs to configure itself. The SDK also works without OpenTelemetry.
Expand Down Expand Up @@ -401,6 +418,8 @@ If you have been using `8.0.0-rc.4` of the Java SDK, here's the new changes that

### Behavioural Changes

- (changed in [7.20.1](https://github.com/getsentry/sentry-java/releases/tag/7.20.1)) The user ip-address is now only set to `"{{auto}}"` if sendDefaultPii is enabled ([#4071](https://github.com/getsentry/sentry-java/pull/4071))
- This change gives you control over IP address collection directly on the client
- Reduce the number of broadcasts the SDK is subscribed for ([#4052](https://github.com/getsentry/sentry-java/pull/4052))
- Drop `TempSensorBreadcrumbsIntegration`
- Drop `PhoneStateBreadcrumbsIntegration`
Expand Down Expand Up @@ -451,7 +470,6 @@ If you would like to keep some of the default broadcast events as breadcrumbs, c
- The user ip-address is now only set to `"{{auto}}"` if sendDefaultPii is enabled ([#4071](https://github.com/getsentry/sentry-java/pull/4071))
- This change gives you control over IP address collection directly on the client


## 7.20.0

### Features
Expand Down
1 change: 1 addition & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ object Config {
val SENTRY_SERVLET_JAKARTA_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.servlet.jakarta"
val SENTRY_COMPOSE_HELPER_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.compose.helper"
val SENTRY_OKHTTP_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.okhttp"
val SENTRY_REACTOR_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.reactor"
val group = "io.sentry"
val description = "SDK for sentry.io"
val versionNameProp = "versionName"
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ org.gradle.workers.max=2
android.useAndroidX=true

# Release information
versionName=8.1.0
versionName=8.2.0

# Override the SDK name on native crashes on Android
sentryAndroidSdkName=sentry.native.android
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public final class io/sentry/opentelemetry/InternalSemanticAttributes {
public static final field PROFILE_SAMPLED Lio/opentelemetry/api/common/AttributeKey;
public static final field PROFILE_SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey;
public static final field SAMPLED Lio/opentelemetry/api/common/AttributeKey;
public static final field SAMPLE_RAND Lio/opentelemetry/api/common/AttributeKey;
public static final field SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey;
public fun <init> ()V
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public final class InternalSemanticAttributes {
public static final AttributeKey<Boolean> SAMPLED = AttributeKey.booleanKey("sentry.sampled");
public static final AttributeKey<Double> SAMPLE_RATE =
AttributeKey.doubleKey("sentry.sample_rate");
public static final AttributeKey<Double> SAMPLE_RAND =
AttributeKey.doubleKey("sentry.sample_rand");
public static final AttributeKey<Boolean> PARENT_SAMPLED =
AttributeKey.booleanKey("sentry.parent_sampled");
public static final AttributeKey<Boolean> PROFILE_SAMPLED =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ public OtelSpanFactory() {
spanBuilder.setAttribute(InternalSemanticAttributes.SAMPLED, samplingDecision.getSampled());
spanBuilder.setAttribute(
InternalSemanticAttributes.SAMPLE_RATE, samplingDecision.getSampleRate());
spanBuilder.setAttribute(
InternalSemanticAttributes.SAMPLE_RAND, samplingDecision.getSampleRand());
spanBuilder.setAttribute(
InternalSemanticAttributes.PROFILE_SAMPLED, samplingDecision.getProfileSampled());
spanBuilder.setAttribute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ public final class OtelSamplingUtil {
final @Nullable Boolean sampled = attributes.get(InternalSemanticAttributes.SAMPLED);
if (sampled != null) {
final @Nullable Double sampleRate = attributes.get(InternalSemanticAttributes.SAMPLE_RATE);
final @Nullable Double sampleRand = attributes.get(InternalSemanticAttributes.SAMPLE_RAND);
final @Nullable Boolean profileSampled =
attributes.get(InternalSemanticAttributes.PROFILE_SAMPLED);
final @Nullable Double profileSampleRate =
attributes.get(InternalSemanticAttributes.PROFILE_SAMPLE_RATE);

return new TracesSamplingDecision(
sampled, sampleRate, profileSampled == null ? false : profileSampled, profileSampleRate);
sampled,
sampleRate,
sampleRand,
profileSampled == null ? false : profileSampled,
profileSampleRate);
} else {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
import io.sentry.Baggage;
import io.sentry.BaggageHeader;
import io.sentry.IScopes;
import io.sentry.PropagationContext;
import io.sentry.ScopesAdapter;
import io.sentry.Sentry;
import io.sentry.SentryLevel;
import io.sentry.SentryTraceHeader;
import io.sentry.exception.InvalidSentryTraceHeaderException;
import io.sentry.util.TracingUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
Expand Down Expand Up @@ -73,12 +73,17 @@ public <C> void inject(final Context context, final C carrier, final TextMapSett
return;
}

final @NotNull SentryTraceHeader sentryTraceHeader = sentrySpan.toSentryTrace();
setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue());
final @Nullable BaggageHeader baggageHeader =
sentrySpan.toBaggageHeader(Collections.emptyList());
if (baggageHeader != null) {
setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue());
// TODO can we use traceIfAllowed? do we have the URL here? need to access span attrs
final @Nullable TracingUtils.TracingHeaders tracingHeaders =
TracingUtils.trace(scopes, Collections.emptyList(), sentrySpan);

if (tracingHeaders != null) {
final @NotNull SentryTraceHeader sentryTraceHeader = tracingHeaders.getSentryTraceHeader();
setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue());
final @Nullable BaggageHeader baggageHeader = tracingHeaders.getBaggageHeader();
if (baggageHeader != null) {
setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue());
}
}
}

Expand Down Expand Up @@ -125,11 +130,6 @@ public <C> Context extract(
.getLogger()
.log(SentryLevel.DEBUG, "Continuing Sentry trace %s", sentryTraceHeader.getTraceId());

final @NotNull PropagationContext propagationContext =
PropagationContext.fromHeaders(
scopes.getOptions().getLogger(), sentryTraceString, baggageString);
scopesToUse.getIsolationScope().setPropagationContext(propagationContext);

return modifiedContext;
} catch (InvalidSentryTraceHeaderException e) {
scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,10 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
baggage = baggageFromContext;
}

final @Nullable Boolean baggageMutable =
otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE);
final @Nullable String baggageString =
otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE);
if (baggageString != null) {
baggage = Baggage.fromHeader(baggageString);
if (baggageMutable == true) {
baggage.freeze();
}
}

final @Nullable Boolean sampled = isSampled(otelSpan, samplingDecision);
Expand All @@ -87,6 +82,9 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
new PropagationContext(
new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled);

baggage = propagationContext.getBaggage();
baggage.setValuesFromSamplingDecision(samplingDecision);

updatePropagationContext(scopes, propagationContext);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public final class OtelSpanWrapper implements IOtelSpanWrapper {
private final @NotNull Contexts contexts = new Contexts();
private @Nullable String transactionName;
private @Nullable TransactionNameSource transactionNameSource;
private final @Nullable Baggage baggage;
private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock();

private final @NotNull Map<String, Object> data = new ConcurrentHashMap<>();
Expand All @@ -86,17 +85,12 @@ public OtelSpanWrapper(
this.scopes = Objects.requireNonNull(scopes, "scopes are required");
this.span = new WeakReference<>(span);
this.startTimestamp = startTimestamp;

if (parentSpan != null) {
this.baggage = parentSpan.getSpanContext().getBaggage();
} else if (baggage != null) {
this.baggage = baggage;
} else {
this.baggage = null;
}

final @Nullable Baggage baggageToUse =
baggage != null
? baggage
: (parentSpan != null ? parentSpan.getSpanContext().getBaggage() : null);
this.context =
new OtelSpanContext(span, samplingDecision, parentSpan, parentSpanId, this.baggage);
new OtelSpanContext(span, samplingDecision, parentSpan, parentSpanId, baggageToUse);
}

@Override
Expand Down Expand Up @@ -207,15 +201,16 @@ public OtelSpanWrapper(
@Override
public @Nullable TraceContext traceContext() {
if (scopes.getOptions().isTraceSampling()) {
final @Nullable Baggage baggage = context.getBaggage();
if (baggage != null) {
updateBaggageValues();
updateBaggageValues(baggage);
return baggage.toTraceContext();
}
}
return null;
}

private void updateBaggageValues() {
private void updateBaggageValues(final @NotNull Baggage baggage) {
try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) {
if (baggage != null && baggage.isMutable()) {
final AtomicReference<SentryId> replayIdAtomicReference = new AtomicReference<>();
Expand All @@ -238,8 +233,9 @@ private void updateBaggageValues() {
@Override
public @Nullable BaggageHeader toBaggageHeader(@Nullable List<String> thirdPartyBaggageHeaders) {
if (scopes.getOptions().isTraceSampling()) {
final @Nullable Baggage baggage = context.getBaggage();
if (baggage != null) {
updateBaggageValues();
updateBaggageValues(baggage);
return BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public SamplingResult shouldSample(
scopes
.getOptions()
.getInternalTracesSampler()
.sample(new SamplingContext(transactionContext, null));
.sample(
new SamplingContext(transactionContext, null, propagationContext.getSampleRand()));

if (!sentryDecision.getSampled()) {
scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public Attributes getAttributes() {
return Attributes.builder()
.put(InternalSemanticAttributes.SAMPLED, sentryDecision.getSampled())
.put(InternalSemanticAttributes.SAMPLE_RATE, sentryDecision.getSampleRate())
.put(InternalSemanticAttributes.SAMPLE_RAND, sentryDecision.getSampleRand())
.put(InternalSemanticAttributes.PROFILE_SAMPLED, sentryDecision.getProfileSampled())
.put(InternalSemanticAttributes.PROFILE_SAMPLE_RATE, sentryDecision.getProfileSampleRate())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public final class SentrySpanExporter implements SpanExporter {
InternalSemanticAttributes.BAGGAGE_MUTABLE.getKey(),
InternalSemanticAttributes.SAMPLED.getKey(),
InternalSemanticAttributes.SAMPLE_RATE.getKey(),
InternalSemanticAttributes.SAMPLE_RAND.getKey(),
InternalSemanticAttributes.PROFILE_SAMPLED.getKey(),
InternalSemanticAttributes.PROFILE_SAMPLE_RATE.getKey(),
InternalSemanticAttributes.PARENT_SAMPLED.getKey(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,15 @@ class SentrySpanProcessorTest {
assertEquals("1", it.baggage?.sampleRate)
assertEquals("HTTP GET", it.baggage?.transaction)
assertEquals("502f25099c204a2fbf4cb16edc5975d1", it.baggage?.publicKey)
assertFalse(it.baggage!!.isMutable)
} else {
assertNotNull(it.baggage)
assertNull(it.baggage?.traceId)
assertNull(it.baggage?.sampleRate)
assertNull(it.baggage?.transaction)
assertNull(it.baggage?.publicKey)
assertFalse(it.baggage!!.isMutable)
assertTrue(it.baggage!!.isMutable)
}
assertFalse(it.baggage!!.isMutable)
},
check<TransactionOptions> {
assertNotNull(it.startTimestamp)
Expand All @@ -434,7 +434,7 @@ class SentrySpanProcessorTest {
assertEquals(otelSpan.spanContext.traceId, it.traceId.toString())
assertNull(it.parentSpanId)
assertNull(it.parentSamplingDecision)
assertNull(it.baggage)
assertNotNull(it.baggage)
},
check<TransactionOptions> {
assertNotNull(it.startTimestamp)
Expand Down
68 changes: 68 additions & 0 deletions sentry-reactor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# sentry-reactor

This module provides a set of utilities to use Sentry with [Reactor](https://projectreactor.io/).

## Setup

Please refer to the documentation on how to set up our [Java SDK](https://docs.sentry.io/platforms/java/),
or our [Spring](https://docs.sentry.io/platforms/java/guides/spring/)
or [Spring Boot](https://docs.sentry.io/platforms/java/guides/spring-boot/) integrations if you're using Spring WebFlux.

If you're using our Spring Boot SDK with Spring Boot (`sentry-spring-boot` or `sentry-spring-boot-jakarta`), this module will be available and used under the hood to automatically instrument WebFlux.
If you're using our Spring SDK (`sentry-spring` or `sentry-spring-jakarta`), you need to configure WebFlux as we do in [SentryWebFluxAutoConfiguration](https://github.com/getsentry/sentry-java/blob/a5098280b52aec28c71c150e286b5c937767634d/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java) for Spring Boot.

Otherwise, read on to find out how to set up and use the integration.

Add the latest version of `io.sentry.reactor` as a dependency.
Make sure you're using `io.micrometer:context-propagation:1.0.2` or later, and `io.projectreactor:reactor-core:3.5.3` or later.

Then, enable automatic context propagation:
```java
import reactor.core.publisher.Hooks;
// ...
Hooks.enableAutomaticContextPropagation();
```

## Usage

You can use the utilities provided by this module to wrap `Mono` and `Flux` objects to enable correct errors, breadcrumbs and tracing in your application.

For normal use cases, you should wrap your operations on `Mono` or `Flux` objects using the `withSentry` function.
This will fork the *current scopes* and use them throughout the stream's execution context.

For example:
```java
import reactor.core.publisher.Mono;
import io.sentry.Sentry;
import io.sentry.ISpan;
import io.sentry.ITransaction;
import io.sentry.TransactionOptions;

TransactionOptions txOptions = new TransactionOptions();
txOptions.setBindToScope(true);
ITransaction tx = Sentry.startTransaction("Transaction", "op", txOptions);
ISpan child = tx.startChild("Outside Mono", "op")
Sentry.captureMessage("Message outside Mono")
child.finish()
String result = SentryReactorUtils.withSentry(
Mono.just("hello")
.map({ (it) ->
ISpan span = Sentry.getCurrentScopes().transaction.startChild("Inside Mono", "map");
Sentry.captureMessage("Message inside Mono");
span.finish();
return it;
})
).block();
System.out.println(result);
tx.finish();
```

For more complex use cases, you can also use `withSentryForkedRoots` to fork the root scopes or `withSentryScopes` to wrap the operation in arbitrary scopes.

For more information on scopes and scope forking, please consult our [scopes documentation](https://docs.sentry.io/platforms/java/enriching-events/scopes).

Examples of usage of this module (with Spring WebFlux) are provided in
[sentry-samples-spring-boot-webflux](https://github.com/getsentry/sentry-java/tree/main/sentry-samples/sentry-samples-spring-boot-webflux)
and
[sentry-samples-spring-boot-webflux-jakarta](https://github.com/getsentry/sentry-java/tree/main/sentry-samples/sentry-samples-spring-boot-webflux-jakarta)
.
Loading

0 comments on commit 27218c5

Please sign in to comment.