diff --git a/CHANGELOG.md b/CHANGELOG.md index 5deb951993..682c030231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Fixes - Fixed JNI Error when accessing Android device data from multiple threads ([#3802](https://github.com/getsentry/sentry-dotnet/pull/3802)) +- Android - fix bug that prevents logcat.log from getting attached to unhandled events (SIGSEGV Segfault) ([#3694](https://github.com/getsentry/sentry-dotnet/pull/3694)) - Fix "System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'idData')" error propagating OpenTelemetry span ids ([#3850](https://github.com/getsentry/sentry-dotnet/pull/3850)) ### Dependencies @@ -44,6 +45,7 @@ ### Fixes +- Fixed ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) - Fixed NullReferenceException in SentryTraceHeader when parsing null or empty values ([#3757](https://github.com/getsentry/sentry-dotnet/pull/3757)) - ArgumentNullException in FormRequestPayloadExtractor when handling invalid form data on ASP.NET ([#3734](https://github.com/getsentry/sentry-dotnet/pull/3734)) - Crash when using NLog with FailedRequestStatusCodes options in a Maui app with Trimming enabled ([#3743](https://github.com/getsentry/sentry-dotnet/pull/3743)) @@ -81,16 +83,16 @@ ## 4.12.2 +### Fixes + +- Events from NDK on Android will report sdk.name `sentry.native.android.dotnet` ([#3682](https://github.com/getsentry/sentry-dotnet/pull/3682)) + ### Features - Android - allow logcat attachments to be previewed in Sentry ([#3711](https://github.com/getsentry/sentry-dotnet/pull/3711)) - Added a `SetBeforeScreenshotCapture` callback to the options: allowing the user to set an action before the screenshot is taken ([#3661](https://github.com/getsentry/sentry-dotnet/pull/3661)) - Make `Sentry.AspNetCore.Blazor.WebAssembly` generally available. ([#3674](https://github.com/getsentry/sentry-dotnet/pull/3674)) -### Fixes - -- Events from NDK on Android will report sdk.name `sentry.native.android.dotnet` ([#3682](https://github.com/getsentry/sentry-dotnet/pull/3682)) - ### Dependencies - Bump Java SDK from v7.14.0 to v7.16.0 ([#3670](https://github.com/getsentry/sentry-dotnet/pull/3670), [#3707](https://github.com/getsentry/sentry-dotnet/pull/3707)) diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index 6b5d2ba42d..f7a5b9f686 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -31,6 +31,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) // Define the configuration for the Android SDK SentryAndroidOptions? nativeOptions = null; + var configuration = new OptionsConfigurationCallback(o => { // Capture the android options reference on the outer scope @@ -59,6 +60,7 @@ private static void InitSentryAndroidSdk(SentryOptions options) o.ServerName = options.ServerName; o.SessionTrackingIntervalMillis = (long)options.AutoSessionTrackingInterval.TotalMilliseconds; o.ShutdownTimeoutMillis = (long)options.ShutdownTimeout.TotalMilliseconds; + o.SetNativeHandlerStrategy(JavaSdk.Android.Core.NdkHandlerStrategy.SentryHandlerStrategyChainAtStart); if (options.CacheDirectoryPath is { } cacheDirectoryPath) { diff --git a/test/Sentry.Maui.Tests/SentryMauiLogcatsTests.cs b/test/Sentry.Maui.Tests/SentryMauiLogcatsTests.cs index e590e687a9..82e0d7a17e 100644 --- a/test/Sentry.Maui.Tests/SentryMauiLogcatsTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiLogcatsTests.cs @@ -29,13 +29,165 @@ public Fixture() options.CacheDirectoryPath = null; //Do not wrap our FakeTransport with a caching transport options.FlushTimeout = TimeSpan.FromSeconds(10); }); + Builder = builder; } } private readonly Fixture _fixture = new(); + #if ANDROID + [SkippableFact] + public async Task CaptureException_WhenAttachLogcats_DefaultAsync() + { + // Arrange + var builder = _fixture.Builder.UseSentry(); + + // Act + using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + var sentryId = client.CaptureException(new Exception()); + await client.FlushAsync(); + + var options = app.Services.GetRequiredService>().Value; + + var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); + envelope.Should().NotBeNull("Envelope with sentryId {0} should be sent", sentryId); + var envelopeItem = envelope!.Items.FirstOrDefault(item => item.TryGetType() == "attachment"); + + // Assert + envelopeItem.Should().BeNull(); + } + + [SkippableFact] + public async Task CaptureException_WhenAttachLogcats_AllExceptionsAsync() + { + + // Arrange + var builder = _fixture.Builder.UseSentry(options => + { + options.Android.LogCatIntegration = Android.LogCatIntegrationType.All; + }); + + // Act + using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + var sentryId = client.CaptureException(new Exception()); + await client.FlushAsync(); + + var options = app.Services.GetRequiredService>().Value; + + var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); + envelope.Should().NotBeNull("Envelope with sentryId {0} should be sent", sentryId); + + // Assert + envelope!.Items.Any(env => env.TryGetFileName() == "logcat.log").Should().BeTrue(); + } + + [SkippableFact] + public async Task CaptureException_WhenAttachLogcats_UnhandledExceptionsAsync() + { + + // Arrange + var builder = _fixture.Builder.UseSentry(options => + { + options.Android.LogCatIntegration = Android.LogCatIntegrationType.Unhandled; + }); + + // Act + using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + var sentryId = client.CaptureException(BuildUnhandledException()); + + await client.FlushAsync(); + + var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); + envelope.Should().NotBeNull("Envelope with sentryId {0} should be sent", sentryId); + var envelopeItem = envelope!.Items.FirstOrDefault(item => item.TryGetType() == "attachment"); + + // Assert + envelopeItem.Should().NotBeNull(); + envelopeItem!.TryGetFileName().Should().Be("logcat.log"); + } + + [SkippableFact] + public async Task CaptureException_WhenAttachLogcats_HandledExceptionsAsync() + { + + // Arrange + var builder = _fixture.Builder.UseSentry(options => + { + options.Android.LogCatIntegration = Android.LogCatIntegrationType.Unhandled; + }); + + // Act + using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + var sentryId = client.CaptureException(new Exception()); + + await client.FlushAsync(); + + var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); + envelope.Should().NotBeNull("Envelope with sentryId {0} should be sent", sentryId); + + // Assert + envelope!.Items.Any(item => item.TryGetType() == "attachment").Should().BeFalse(); + } + + [SkippableFact] + public async Task CaptureException_WhenAttachLogcats_ErrorsAsync() + { + + // Arrange + var builder = _fixture.Builder.UseSentry(options => + { + options.Android.LogCatIntegration = Android.LogCatIntegrationType.Errors; + }); + + // Act + using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + var sentryId = client.CaptureException(new Exception()); + await client.FlushAsync(); + + var options = app.Services.GetRequiredService>().Value; + + var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); + envelope.Should().NotBeNull("Envelope with sentryId {0} should be sent", sentryId); + var envelopeItem = envelope!.Items.FirstOrDefault(item => item.TryGetType() == "attachment"); + + // Assert + envelopeItem.Should().NotBeNull(); + envelopeItem!.TryGetFileName().Should().Be("logcat.log"); + } + + [SkippableFact] + public async Task CaptureException_WhenAttachLogcats_NoneAsync() + { + + // Arrange + var builder = _fixture.Builder.UseSentry(options => + { + options.Android.LogCatIntegration = Android.LogCatIntegrationType.None; + }); + + // Act + using var app = builder.Build(); + var client = app.Services.GetRequiredService(); + var sentryId = client.CaptureException(new Exception()); + await client.FlushAsync(); + + var options = app.Services.GetRequiredService>().Value; + + var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); + envelope.Should().NotBeNull("Envelope with sentryId {0} should be sent", sentryId); + var envelopeItem = envelope!.Items.FirstOrDefault(item => item.TryGetType() == "attachment"); + + // Assert + envelopeItem.Should().BeNull(); + } + [Fact] public void CaptureException_CheckLogcatType() { @@ -63,5 +215,22 @@ public void CaptureException_CheckLogcatType() hint.Should().NotBeNull(); hint.Attachments.First().ContentType.Should().Be("text/plain", hint.Attachments.First().ContentType); } + + private static Exception BuildUnhandledException() + { + try + { + // Throwing will put a stack trace on the exception + throw new Exception("Error"); + } + catch (Exception exception) + { + // Add extra data to test fully + exception.Data[Mechanism.HandledKey] = false; + exception.Data[Mechanism.MechanismKey] = "AppDomain.UnhandledException"; + exception.Data["foo"] = "bar"; + return exception; + } + } #endif } diff --git a/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs b/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs index 15fe05a767..55d1ab909a 100644 --- a/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs @@ -52,12 +52,32 @@ public void CacheDirectoryPath_Default() #endif } +#if ANDROID [Fact] - public void AttachScreenshots_Default() + public void HandlerStrategy_Default() { + // Arrange + var expected = Android.LogCatIntegrationType.None; + var options = new SentryMauiOptions(); + + // Assert + Assert.Equal(expected, options.Android.LogCatIntegration); + } + + [Fact] + public void HandlerStrategy_Set() + { + // Arrange + var expected = Android.LogCatIntegrationType.None; var options = new SentryMauiOptions(); - Assert.False(options.AttachScreenshot); + + // Act + options.Android.LogCatIntegration = Android.LogCatIntegrationType.All; + + // Assert + Assert.NotEqual(expected, options.Android.LogCatIntegration); } +#endif [Fact] public void BeforeCaptureScreenshot_Set()