diff --git a/CHANGELOG.md b/CHANGELOG.md index 4deb0480d0..4a01c89915 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ ### Fixes - The `Serilog` integration captures _Structured Logs_ (when enabled) independently of captured Events and added Breadcrumbs ([#4691](https://github.com/getsentry/sentry-dotnet/pull/4691)) +- Deliver system breadcrumbs in the main thread on Android ([#4671](https://github.com/getsentry/sentry-dotnet/pull/4671)) ## 6.0.0-preview.2 diff --git a/integration-test/android.Tests.ps1 b/integration-test/android.Tests.ps1 index 018a7843fc..f7910a5af3 100644 --- a/integration-test/android.Tests.ps1 +++ b/integration-test/android.Tests.ps1 @@ -177,4 +177,28 @@ Describe 'MAUI app (, )' -ForEach @( $result.Envelopes() | Should -HaveCount 1 } } + + It 'Delivers battery breadcrumbs in main thread ()' { + $result = Invoke-SentryServer { + param([string]$url) + RunAndroidApp -Dsn $url -TestArg "BATTERY_CHANGED" + } + + Dump-ServerErrors -Result $result + $result.HasErrors() | Should -BeFalse + $result.Envelopes() | Should -AnyElementMatch "`"type`":`"system`",`"thread_id`":`"1`",`"category`":`"device.event`",`"action`":`"BATTERY_CHANGED`"" + $result.Envelopes() | Should -HaveCount 1 + } + + It 'Delivers network breadcrumbs in main thread ()' { + $result = Invoke-SentryServer { + param([string]$url) + RunAndroidApp -Dsn $url -TestArg "NETWORK_CAPABILITIES_CHANGED" + } + + Dump-ServerErrors -Result $result + $result.HasErrors() | Should -BeFalse + $result.Envelopes() | Should -AnyElementMatch "`"type`":`"system`",`"thread_id`":`"1`",`"category`":`"network.event`",`"action`":`"NETWORK_CAPABILITIES_CHANGED`"" + $result.Envelopes() | Should -HaveCount 1 + } } diff --git a/integration-test/net9-maui/App.xaml.cs b/integration-test/net9-maui/App.xaml.cs index cd3a1668c9..c187a5ad66 100644 --- a/integration-test/net9-maui/App.xaml.cs +++ b/integration-test/net9-maui/App.xaml.cs @@ -1,14 +1,112 @@ -namespace Sentry.Maui.Device.IntegrationTestApp; +#if ANDROID +using Android.OS; +#endif +using System.Collections.Concurrent; + +namespace Sentry.Maui.Device.IntegrationTestApp; public partial class App : Application { + private static readonly ConcurrentDictionary> systemBreadcrumbs = new(); + private static string? testArg; + public App() { InitializeComponent(); } + public static bool HasTestArg(string arg) + { + return string.Equals(testArg, arg, StringComparison.OrdinalIgnoreCase); + } + + public static void ReceiveSystemBreadcrumb(Breadcrumb breadcrumb) + { + if (breadcrumb.Type != "system" || + breadcrumb.Data?.TryGetValue("action", out var action) != true || + string.IsNullOrEmpty(action)) + { + return; + } + + systemBreadcrumbs[action] = new Dictionary() + { + ["action"] = action, + ["category"] = breadcrumb.Category ?? string.Empty, + ["thread_id"] = Thread.CurrentThread.ManagedThreadId.ToString(), + ["type"] = breadcrumb.Type ?? string.Empty, + }; + + if (HasTestArg(action)) + { + // received after OnAppearing + CaptureSystemBreadcrumb(action, systemBreadcrumbs[action]!); + Kill(); + } + } + + public static void CaptureSystemBreadcrumb(string action, Dictionary data) + { + SentrySdk.CaptureMessage(action, scope => + { + foreach (var kvp in data) + { + scope.SetExtra(kvp.Key, kvp.Value); + } + }); + } + protected override Window CreateWindow(IActivationState? activationState) { return new Window(new AppShell()); } + + public static void OnAppearing() + { + testArg = System.Environment.GetEnvironmentVariable("SENTRY_TEST_ARG"); + +#pragma warning disable CS0618 + if (Enum.TryParse(testArg, ignoreCase: true, out var crashType)) + { + SentrySdk.CauseCrash(crashType); + } +#pragma warning restore CS0618 + + if (HasTestArg("NullReferenceException")) + { + try + { + object? obj = null; + _ = obj!.ToString(); + } + catch (NullReferenceException ex) + { + SentrySdk.CaptureException(ex); + } + Kill(); + } + else if (!string.IsNullOrEmpty(testArg) && systemBreadcrumbs.TryGetValue(testArg, out var breadcrumb)) + { + // received before OnAppearing + CaptureSystemBreadcrumb(testArg, breadcrumb); + Kill(); + } + else if (HasTestArg("None")) + { + Kill(); + } + } + + public static void Kill() + { + SentrySdk.Close(); + +#if ANDROID + // prevent auto-restart + Platform.CurrentActivity?.FinishAffinity(); + Process.KillProcess(Process.MyPid()); +#elif IOS + System.Environment.Exit(0); +#endif + } } diff --git a/integration-test/net9-maui/MainPage.xaml.cs b/integration-test/net9-maui/MainPage.xaml.cs index fd47bd30d8..a44a0948df 100644 --- a/integration-test/net9-maui/MainPage.xaml.cs +++ b/integration-test/net9-maui/MainPage.xaml.cs @@ -1,8 +1,4 @@ -#if ANDROID -using Android.OS; -#endif - -namespace Sentry.Maui.Device.IntegrationTestApp; +namespace Sentry.Maui.Device.IntegrationTestApp; public partial class MainPage : ContentPage { @@ -15,35 +11,6 @@ protected override void OnAppearing() { base.OnAppearing(); - var testArg = System.Environment.GetEnvironmentVariable("SENTRY_TEST_ARG"); - -#pragma warning disable CS0618 - if (Enum.TryParse(testArg, ignoreCase: true, out var crashType)) - { - SentrySdk.CauseCrash(crashType); - } -#pragma warning restore CS0618 - - if (testArg?.Equals("NullReferenceException", StringComparison.OrdinalIgnoreCase) == true) - { - try - { - object? obj = null; - _ = obj!.ToString(); - } - catch (NullReferenceException ex) - { - SentrySdk.CaptureException(ex); - } - } - - SentrySdk.Close(); -#if ANDROID - // prevent auto-restart - Platform.CurrentActivity?.FinishAffinity(); - Process.KillProcess(Process.MyPid()); -#elif IOS - System.Environment.Exit(0); -#endif + App.OnAppearing(); } } diff --git a/integration-test/net9-maui/MauiProgram.cs b/integration-test/net9-maui/MauiProgram.cs index 766f64f6a1..e1c619cdb5 100644 --- a/integration-test/net9-maui/MauiProgram.cs +++ b/integration-test/net9-maui/MauiProgram.cs @@ -20,6 +20,12 @@ public static MauiApp CreateMauiApp() // predictable crash envelopes only options.SendClientReports = false; options.AutoSessionTracking = false; + + options.SetBeforeBreadcrumb((breadcrumb, hint) => + { + App.ReceiveSystemBreadcrumb(breadcrumb); + return breadcrumb; + }); }) .ConfigureFonts(fonts => { diff --git a/src/Sentry.Bindings.Android/Transforms/Metadata.xml b/src/Sentry.Bindings.Android/Transforms/Metadata.xml index 35f9c0be20..0abbbd2bd1 100644 --- a/src/Sentry.Bindings.Android/Transforms/Metadata.xml +++ b/src/Sentry.Bindings.Android/Transforms/Metadata.xml @@ -85,6 +85,9 @@ + + +