diff --git a/CHANGELOG.md b/CHANGELOG.md index be8a1c26ac..a179e82555 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,31 +11,31 @@ ### Behavioural Changes - (Android) The JNI layer for sentry-native has now been moved from sentry-java to sentry-native ([#3189](https://github.com/getsentry/sentry-java/pull/3189)) - - This now includes prefab support for sentry-native, allowing you to link and access the sentry-native API within your native app code - - Checkout the `sentry-samples/sentry-samples-android` example on how to configure CMake and consume `sentry.h` + - This now includes prefab support for sentry-native, allowing you to link and access the sentry-native API within your native app code + - Checkout the `sentry-samples/sentry-samples-android` example on how to configure CMake and consume `sentry.h` ### Features - Our `sentry-opentelemetry-agent` has been completely reworked and now plays nicely with the rest of the Java SDK - - You may also want to give this new agent a try even if you haven't used OpenTelemetry (with Sentry) before. It offers support for [many more libraries and frameworks](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), improving on our trace propagation, `Scopes` (used to be `Hub`) propagation as well as performance instrumentation (i.e. more spans). - - If you are using a framework we did not support before and currently resort to manual instrumentation, please give the agent a try. See [here for a list of supported libraries, frameworks and application servers](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md). - - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. Features of note that are not working yet: - - Metrics - - Measurements - - `forceFinish` on transaction - - `scheduleFinish` on transaction - - see [#3436](https://github.com/getsentry/sentry-java/issues/3436) for a more up-to-date list of features we have (not) implemented - - Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. - - What's new about the Agent - - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. - - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. - - OpenTelemetry spans are now only turned into Sentry spans when they are finished so they can be sent to the Sentry server. - - Now registers an OpenTelemetry `Sampler` which uses Sentry sampling configuration - - Other Performance integrations automatically stop creating spans to avoid duplicate spans - - The Sentry SDK now makes use of OpenTelemetry `Context` for storing Sentry `Scopes` (which is similar to what used to be called `Hub`) and thus relies on OpenTelemetry for `Context` propagation. - - Classes used for the previous version of our OpenTelemetry support have been deprecated but can still be used manually. We're not planning to keep the old agent around in favor of less complexity in the SDK. + - You may also want to give this new agent a try even if you haven't used OpenTelemetry (with Sentry) before. It offers support for [many more libraries and frameworks](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), improving on our trace propagation, `Scopes` (used to be `Hub`) propagation as well as performance instrumentation (i.e. more spans). + - If you are using a framework we did not support before and currently resort to manual instrumentation, please give the agent a try. See [here for a list of supported libraries, frameworks and application servers](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md). + - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. Features of note that are not working yet: + - Metrics + - Measurements + - `forceFinish` on transaction + - `scheduleFinish` on transaction + - see [#3436](https://github.com/getsentry/sentry-java/issues/3436) for a more up-to-date list of features we have (not) implemented + - Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. + - What's new about the Agent + - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. + - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. + - OpenTelemetry spans are now only turned into Sentry spans when they are finished so they can be sent to the Sentry server. + - Now registers an OpenTelemetry `Sampler` which uses Sentry sampling configuration + - Other Performance integrations automatically stop creating spans to avoid duplicate spans + - The Sentry SDK now makes use of OpenTelemetry `Context` for storing Sentry `Scopes` (which is similar to what used to be called `Hub`) and thus relies on OpenTelemetry for `Context` propagation. + - Classes used for the previous version of our OpenTelemetry support have been deprecated but can still be used manually. We're not planning to keep the old agent around in favor of less complexity in the SDK. - Add `ignoredSpanOrigins` option for ignoring spans coming from certain integrations - - We pre-configure this to ignore Performance instrumentation for Spring and other integrations when using our OpenTelemetry Agent to avoid duplicate spans + - We pre-configure this to ignore Performance instrumentation for Spring and other integrations when using our OpenTelemetry Agent to avoid duplicate spans - Add data fetching environment hint to breadcrumb for GraphQL (#3413) ([#3431](https://github.com/getsentry/sentry-java/pull/3431)) ### Fixes @@ -60,9 +60,9 @@ If you've been using the previous version of `sentry-opentelemetry-agent`, simpl #### New to the agent If you've not been using OpenTelemetry before, you can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application -- `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` -- Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. -- You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be valid. + - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` + - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. + - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be valid. If you want to skip auto initialization of the SDK performed by the agent, please follow the steps above and set the environment variable `SENTRY_AUTO_INIT` to `false` then add the following to your `Sentry.init`: @@ -87,8 +87,8 @@ Sentry.OptionsConfiguration optionsConfiguration() { ### Dependencies - Bump Native SDK from v0.7.0 to v0.7.5 ([#3441](https://github.com/getsentry/sentry-java/pull/3189)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#075) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.0...0.7.5) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#075) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.0...0.7.5) ## 8.0.0-alpha.1 diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index b93a81fb32..40cbdb62b7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -196,7 +196,7 @@ public static SentryId captureEnvelope( deleteCurrentSessionFile( options, // should be sync if going to crash or already not a main thread - !maybeStartNewSession || !hub.getOptions().getMainThreadChecker().isMainThread()); + !maybeStartNewSession || !scopes.getOptions().getMainThreadChecker().isMainThread()); if (maybeStartNewSession) { scopes.startSession(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 01b15c0286..49ad3ffeaa 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -90,7 +90,7 @@ public static synchronized void init( Sentry.init( OptionsContainer.create(SentryAndroidOptions.class), options -> { - final LoadClass classLoader = new LoadClass(); + final io.sentry.util.LoadClass classLoader = new io.sentry.util.LoadClass(); final boolean isTimberUpstreamAvailable = classLoader.isClassAvailable(TIMBER_CLASS_NAME, options); final boolean isFragmentUpstreamAvailable = @@ -104,7 +104,7 @@ public static synchronized void init( && classLoader.isClassAvailable(SENTRY_TIMBER_INTEGRATION_CLASS_NAME, options)); final BuildInfoProvider buildInfoProvider = new BuildInfoProvider(logger); - final LoadClass loadClass = new LoadClass(); + final io.sentry.util.LoadClass loadClass = new io.sentry.util.LoadClass(); final ActivityFramesTracker activityFramesTracker = new ActivityFramesTracker(loadClass, options); @@ -154,7 +154,7 @@ public static synchronized void init( // so only start a session if it's not already started // This e.g. happens on React Native, or e.g. on deferred SDK init final AtomicBoolean sessionStarted = new AtomicBoolean(false); - hub.configureScope( + scopes.configureScope( scope -> { final @Nullable Session currentSession = scope.getSession(); if (currentSession != null && currentSession.getStarted() != null) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt index 3d4d0a9837..be0f5cd71a 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt @@ -319,6 +319,7 @@ class SentryAndroidTest { @Test fun `init does not start a session if one is already running`() { val client = mock() + whenever(client.isEnabled).thenReturn(true) initSentryWithForegroundImportance(true, { options -> options.addIntegration { hub, _ -> diff --git a/sentry-android-okhttp/src/test/java/io/sentry/android/okhttp/SentryOkHttpEventListenerTest.kt b/sentry-android-okhttp/src/test/java/io/sentry/android/okhttp/SentryOkHttpEventListenerTest.kt deleted file mode 100644 index 9ed110ef7e..0000000000 --- a/sentry-android-okhttp/src/test/java/io/sentry/android/okhttp/SentryOkHttpEventListenerTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package io.sentry.android.okhttp - -import io.sentry.IHub -import io.sentry.SentryOptions -import io.sentry.SentryTracer -import io.sentry.TransactionContext -import okhttp3.EventListener -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okhttp3.mockwebserver.SocketPolicy -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever -import kotlin.test.Test -import kotlin.test.assertEquals - -@SuppressWarnings("Deprecated") -class SentryOkHttpEventListenerTest { - - class Fixture { - val hub = mock() - val server = MockWebServer() - lateinit var sentryTracer: SentryTracer - - @SuppressWarnings("LongParameterList") - fun getSut( - eventListener: EventListener? = null - ): OkHttpClient { - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - } - whenever(hub.options).thenReturn(options) - - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) - whenever(hub.span).thenReturn(sentryTracer) - server.enqueue( - MockResponse() - .setBody("responseBody") - .setSocketPolicy(SocketPolicy.KEEP_OPEN) - .setResponseCode(200) - ) - - val builder = OkHttpClient.Builder().addInterceptor(SentryOkHttpInterceptor(hub)) - val sentryOkHttpEventListener = when { - eventListener != null -> SentryOkHttpEventListener(hub, eventListener) - else -> SentryOkHttpEventListener(hub) - } - return builder.eventListener(sentryOkHttpEventListener).build() - } - } - - private val fixture = Fixture() - - private fun getRequest(url: String = "/hello"): Request { - return Request.Builder() - .addHeader("myHeader", "myValue") - .get() - .url(fixture.server.url(url)) - .build() - } - - @Test - fun `when there are multiple SentryOkHttpEventListeners, they don't duplicate spans`() { - val sut = fixture.getSut(eventListener = SentryOkHttpEventListener(fixture.hub)) - val call = sut.newCall(getRequest()) - call.execute().close() - assertEquals(8, fixture.sentryTracer.children.size) - } -} diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt index 2f3862bd67..28d7375450 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt @@ -63,7 +63,7 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques callRootSpan?.setData("url", url) callRootSpan?.setData("host", host) callRootSpan?.setData("path", encodedPath) - callRootSpan?.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)) + callRootSpan?.setData(SpanDataConvention.HTTP_METHOD_KEY, method.uppercase(Locale.ROOT)) } /** diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index a106b61618..68ad64144f 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -250,6 +250,7 @@ public final class io/sentry/CombinedScopeView : io/sentry/IScope { public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V + public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; @@ -326,6 +327,7 @@ public final class io/sentry/DataCategory : java/lang/Enum { public static final field Profile Lio/sentry/DataCategory; public static final field Security Lio/sentry/DataCategory; public static final field Session Lio/sentry/DataCategory; + public static final field Span Lio/sentry/DataCategory; public static final field Transaction Lio/sentry/DataCategory; public static final field Unknown Lio/sentry/DataCategory; public static final field UserReport Lio/sentry/DataCategory; @@ -738,6 +740,7 @@ public abstract interface class io/sentry/IScope { public abstract fun clear ()V public abstract fun clearAttachments ()V public abstract fun clearBreadcrumbs ()V + public abstract fun clearSession ()V public abstract fun clearTransaction ()V public abstract fun clone ()Lio/sentry/IScope; public abstract fun endSession ()Lio/sentry/Session; @@ -1412,6 +1415,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V + public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; @@ -1884,6 +1888,7 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V + public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; @@ -3632,6 +3637,7 @@ public final class io/sentry/clientreport/ClientReportRecorder : io/sentry/clien public fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V + public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public final class io/sentry/clientreport/DiscardReason : java/lang/Enum { @@ -3677,6 +3683,7 @@ public abstract interface class io/sentry/clientreport/IClientReportRecorder { public abstract fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public abstract fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public abstract fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V + public abstract fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public abstract interface class io/sentry/clientreport/IClientReportStorage { @@ -3690,6 +3697,7 @@ public final class io/sentry/clientreport/NoOpClientReportRecorder : io/sentry/c public fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V + public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public abstract interface class io/sentry/config/PropertiesProvider { diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 86b90379ad..c22fd060bb 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -412,6 +412,11 @@ public void withTransaction(Scope.@NotNull IWithTransaction callback) { return globalScope.getSession(); } + @Override + public void clearSession() { + getDefaultWriteScope().clearSession(); + } + @Override public void setPropagationContext(@NotNull PropagationContext propagationContext) { getDefaultWriteScope().setPropagationContext(propagationContext); diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 314063b340..1e26eefeba 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -775,7 +775,7 @@ public void flush(long timeoutMillis) { getOptions() .getClientReportRecorder() .recordLostEvent(DiscardReason.BACKPRESSURE, DataCategory.Transaction); - options + getOptions() .getClientReportRecorder() .recordLostEvent( DiscardReason.BACKPRESSURE, @@ -785,7 +785,7 @@ public void flush(long timeoutMillis) { getOptions() .getClientReportRecorder() .recordLostEvent(DiscardReason.SAMPLE_RATE, DataCategory.Transaction); - options + getOptions() .getClientReportRecorder() .recordLostEvent( DiscardReason.SAMPLE_RATE, diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index e50ee4258e..409d3b971b 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -308,10 +308,10 @@ class UncaughtExceptionHandlerIntegrationTest { } val integration1 = UncaughtExceptionHandlerIntegration(handler) - integration1.register(fixture.hub, fixture.options) + integration1.register(fixture.scopes, fixture.options) val integration2 = UncaughtExceptionHandlerIntegration(handler) - integration2.register(fixture.hub, fixture.options) + integration2.register(fixture.scopes, fixture.options) assertEquals(currentDefaultHandler, integration2) integration2.close() @@ -334,10 +334,10 @@ class UncaughtExceptionHandlerIntegrationTest { } val integration1 = UncaughtExceptionHandlerIntegration(handler) - integration1.register(fixture.hub, fixture.options) + integration1.register(fixture.scopes, fixture.options) val integration2 = UncaughtExceptionHandlerIntegration(handler) - integration2.register(fixture.hub, fixture.options) + integration2.register(fixture.scopes, fixture.options) assertEquals(currentDefaultHandler, integration2) integration2.close() diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index 96468808ab..0cd9be9094 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -95,9 +95,9 @@ class ClientReportTest { @Test fun `lost transaction records dropped spans`() { givenClientReportRecorder() - val hub = mock() - whenever(hub.options).thenReturn(opts) - val transaction = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), hub) + val scopes = mock() + whenever(scopes.options).thenReturn(opts) + val transaction = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes) transaction.startChild("lost span", "span1").finish() transaction.startChild("lost span", "span2").finish() transaction.startChild("lost span", "span3").finish()