diff --git a/CHANGELOG.md b/CHANGELOG.md index cbbe2738bf..9ca736c066 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Features +- (Internal) Extend APIs for hybrid SDKs ([#2814](https://github.com/getsentry/sentry-java/pull/2814), [#2846](https://github.com/getsentry/sentry-java/pull/2846)) + ### Fixes - Fix ANRv2 thread dump parsing for native-only threads ([#2839](https://github.com/getsentry/sentry-java/pull/2839)) 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 bdc9f055e5..5302199670 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 @@ -41,7 +41,8 @@ public final class InternalSentrySdk { @Nullable public static Scope getCurrentScope() { final @NotNull AtomicReference scopeRef = new AtomicReference<>(); - HubAdapter.getInstance().withScope(scopeRef::set); + //noinspection Convert2MethodRef + HubAdapter.getInstance().withScope(scope -> scopeRef.set(scope)); return scopeRef.get(); } @@ -60,15 +61,14 @@ public static Map serializeScope( final @NotNull Context context, final @NotNull SentryAndroidOptions options, final @Nullable Scope scope) { + final @NotNull Map data = new HashMap<>(); if (scope == null) { return data; } - - final @NotNull ILogger logger = options.getLogger(); - final @NotNull ObjectWriter writer = new MapObjectWriter(data); - try { + final @NotNull ILogger logger = options.getLogger(); + final @NotNull ObjectWriter writer = new MapObjectWriter(data); final @NotNull DeviceInfoUtil deviceInfoUtil = DeviceInfoUtil.getInstance(context, options); final @NotNull Device deviceInfo = deviceInfoUtil.collectDeviceInformation(true, true); @@ -114,7 +114,7 @@ public static Map serializeScope( writer.name("fingerprint").value(logger, scope.getFingerprint()); writer.name("level").value(logger, scope.getLevel()); writer.name("breadcrumbs").value(logger, scope.getBreadcrumbs()); - } catch (Exception e) { + } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, "Could not serialize scope.", e); return new HashMap<>(); } @@ -124,54 +124,62 @@ public static Map serializeScope( /** * Captures the provided envelope. Compared to {@link IHub#captureEvent(SentryEvent)} this method - * - will not enrich events with additional data (e.g. scope) - will not execute beforeSend: it's - * up to the caller to take care of this - will not perform any sampling: it's up to the caller to - * take care of this - will enrich the envelope with a Session updates is applicable + *
+ * - will not enrich events with additional data (e.g. scope)
+ * - will not execute beforeSend: it's up to the caller to take care of this
+ * - will not perform any sampling: it's up to the caller to take care of this
+ * - will enrich the envelope with a Session update if applicable
* * @param envelopeData the serialized envelope data - * @return The Id (SentryId object) of the event - * @throws Exception In case the provided envelope could not be parsed / is invalid + * @return The Id (SentryId object) of the event, or null in case the envelope could not be + * captured */ - public static SentryId captureEnvelope(final @NotNull byte[] envelopeData) throws Exception { + @Nullable + public static SentryId captureEnvelope(final @NotNull byte[] envelopeData) { final @NotNull IHub hub = HubAdapter.getInstance(); final @NotNull SentryOptions options = hub.getOptions(); - final @NotNull ISerializer serializer = options.getSerializer(); - final @Nullable SentryEnvelope envelope = - options.getEnvelopeReader().read(new ByteArrayInputStream(envelopeData)); - if (envelope == null) { - throw new IllegalArgumentException("Envelope could not be read"); - } + try { + final @NotNull ISerializer serializer = options.getSerializer(); + final @Nullable SentryEnvelope envelope = + options.getEnvelopeReader().read(new ByteArrayInputStream(envelopeData)); + if (envelope == null) { + return null; + } - final @NotNull List envelopeItems = new ArrayList<>(); + final @NotNull List envelopeItems = new ArrayList<>(); - // determine session state based on events inside envelope - @Nullable Session.State status = null; - boolean crashedOrErrored = false; - for (SentryEnvelopeItem item : envelope.getItems()) { - envelopeItems.add(item); + // determine session state based on events inside envelope + @Nullable Session.State status = null; + boolean crashedOrErrored = false; + for (SentryEnvelopeItem item : envelope.getItems()) { + envelopeItems.add(item); - final SentryEvent event = item.getEvent(serializer); - if (event != null) { - if (event.isCrashed()) { - status = Session.State.Crashed; - } - if (event.isCrashed() || event.isErrored()) { - crashedOrErrored = true; + final SentryEvent event = item.getEvent(serializer); + if (event != null) { + if (event.isCrashed()) { + status = Session.State.Crashed; + } + if (event.isCrashed() || event.isErrored()) { + crashedOrErrored = true; + } } } - } - // update session and add it to envelope if necessary - final @Nullable Session session = updateSession(hub, options, status, crashedOrErrored); - if (session != null) { - final SentryEnvelopeItem sessionItem = SentryEnvelopeItem.fromSession(serializer, session); - envelopeItems.add(sessionItem); - } + // update session and add it to envelope if necessary + final @Nullable Session session = updateSession(hub, options, status, crashedOrErrored); + if (session != null) { + final SentryEnvelopeItem sessionItem = SentryEnvelopeItem.fromSession(serializer, session); + envelopeItems.add(sessionItem); + } - final SentryEnvelope repackagedEnvelope = - new SentryEnvelope(envelope.getHeader(), envelopeItems); - return hub.captureEnvelope(repackagedEnvelope); + final SentryEnvelope repackagedEnvelope = + new SentryEnvelope(envelope.getHeader(), envelopeItems); + return hub.captureEnvelope(repackagedEnvelope); + } catch (Throwable t) { + options.getLogger().log(SentryLevel.ERROR, "Failed to capture envelope", t); + } + return null; } @Nullable @@ -181,7 +189,7 @@ private static Session updateSession( final @Nullable Session.State status, final boolean crashedOrErrored) { final @NotNull AtomicReference sessionRef = new AtomicReference<>(); - hub.withScope( + hub.configureScope( scope -> { final @Nullable Session session = scope.getSession(); if (session != null) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index 2e0537365d..c27e199d6c 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -29,10 +29,10 @@ import org.mockito.kotlin.whenever import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStreamReader +import java.util.concurrent.atomic.AtomicReference import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertFails import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue @@ -203,9 +203,7 @@ class InternalSentrySdkTest { @Test fun `captureEnvelope fails if payload is invalid`() { - assertFails { - InternalSentrySdk.captureEnvelope(ByteArray(8)) - } + assertNull(InternalSentrySdk.captureEnvelope(ByteArray(8))) } @Test @@ -251,20 +249,26 @@ class InternalSentrySdkTest { } ) - // then the session should be included val capturedEnvelope = fixture.capturedEnvelopes.first() val capturedEnvelopeItems = capturedEnvelope.items.toList() - // and it should contain the original event / attachment + // then it should contain the original event + session assertEquals(2, capturedEnvelopeItems.size) assertEquals(SentryItemType.Event, capturedEnvelopeItems[0].header.type) assertEquals(SentryItemType.Session, capturedEnvelopeItems[1].header.type) + // and then the sent session should be marked as crashed val capturedSession = fixture.options.serializer.deserialize( InputStreamReader(ByteArrayInputStream(capturedEnvelopeItems[1].data)), Session::class.java )!! - assertEquals(Session.State.Crashed, capturedSession.status) + + // and the local session should be marked as crashed too + val scopeRef = AtomicReference() + Sentry.configureScope { scope -> + scopeRef.set(scope) + } + assertEquals(Session.State.Crashed, scopeRef.get().session!!.status) } }