From 610dee87287bfcb0138090f040511a154f6aa9db Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 21 Jun 2022 14:38:06 -0700 Subject: [PATCH] Android Scope Sync (#1737) * Add AndroidScopeObserver * Update CHANGELOG.md --- CHANGELOG.md | 6 ++ .../Sentry.Samples.Android/MainActivity.cs | 19 +++- src/Sentry/Android/AndroidScopeObserver.cs | 101 ++++++++++++++++++ .../Android/Extensions/UserExtensions.cs | 29 +++++ src/Sentry/Android/SentrySdk.cs | 2 + 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 src/Sentry/Android/AndroidScopeObserver.cs create mode 100644 src/Sentry/Android/Extensions/UserExtensions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index ee1b5d2007..dd9e0f2ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- Android Scope Sync ([#1737](https://github.com/getsentry/sentry-dotnet/pull/1737)) + ## Sentry.Maui 3.18.0-preview.1 ### Features diff --git a/samples/Sentry.Samples.Android/MainActivity.cs b/samples/Sentry.Samples.Android/MainActivity.cs index cb8ff06b90..d042f261fa 100644 --- a/samples/Sentry.Samples.Android/MainActivity.cs +++ b/samples/Sentry.Samples.Android/MainActivity.cs @@ -9,8 +9,25 @@ protected override void OnCreate(Bundle? savedInstanceState) { SentrySdk.Init(this, o => { - o.Debug = true; o.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; + o.SendDefaultPii = true; // adds the user's IP address automatically + }); + + // Here's an example of adding custom scope information. + // This can be done at any time, and will be passed through to the Java SDK as well. + SentrySdk.ConfigureScope(scope => + { + scope.AddBreadcrumb("Custom Breadcrumb"); + scope.SetExtra("Test", "Custom Extra Data"); + scope.User = new User + { + Username = "SomeUser", + Email = "test@example.com", + Other = + { + ["CustomInfo"] = "Custom User Info" + } + }; }); base.OnCreate(savedInstanceState); diff --git a/src/Sentry/Android/AndroidScopeObserver.cs b/src/Sentry/Android/AndroidScopeObserver.cs new file mode 100644 index 0000000000..c820b95eeb --- /dev/null +++ b/src/Sentry/Android/AndroidScopeObserver.cs @@ -0,0 +1,101 @@ +using Sentry.Android.Extensions; +using Sentry.Extensibility; + +namespace Sentry.Android; + +internal sealed class AndroidScopeObserver : IScopeObserver +{ + private readonly SentryOptions _options; + private readonly IScopeObserver? _innerObserver; + + public AndroidScopeObserver(SentryOptions options) + { + _options = options; + + // Chain any previous observer, but guard against circular reference. + var observer = options.ScopeObserver; + _innerObserver = observer is AndroidScopeObserver ? null : observer; + } + + public void AddBreadcrumb(Breadcrumb breadcrumb) + { + try + { + var b = breadcrumb.ToJavaBreadcrumb(); + Java.Sentry.AddBreadcrumb(b); + } + finally + { + _innerObserver?.AddBreadcrumb(breadcrumb); + } + } + + public void SetExtra(string key, object? value) + { + try + { + if (value is null) + { + _options.LogDebug("Extra with key '{0}' was null.", key); + return; + } + + if (value is string s) + { + Java.Sentry.SetExtra(key, s); + return; + } + + try + { + var json = JsonSerializer.Serialize(value); + Java.Sentry.SetExtra(key, json); + } + catch (Exception ex) + { + _options.LogError("Extra with key '{0}' could not be serialized.", ex, key); + } + } + finally + { + _innerObserver?.SetExtra(key, value); + } + } + + public void SetTag(string key, string value) + { + try + { + Java.Sentry.SetTag(key, value); + } + finally + { + _innerObserver?.SetTag(key, value); + } + } + + public void UnsetTag(string key) + { + try + { + Java.Sentry.RemoveTag(key); + } + finally + { + _innerObserver?.UnsetTag(key); + } + } + + public void SetUser(User? user) + { + try + { + var u = user?.ToJavaUser(); + Java.Sentry.SetUser(u); + } + finally + { + _innerObserver?.SetUser(user); + } + } +} diff --git a/src/Sentry/Android/Extensions/UserExtensions.cs b/src/Sentry/Android/Extensions/UserExtensions.cs new file mode 100644 index 0000000000..0110bdeded --- /dev/null +++ b/src/Sentry/Android/Extensions/UserExtensions.cs @@ -0,0 +1,29 @@ +using System.Collections.ObjectModel; + +namespace Sentry.Android.Extensions; + +internal static class UserExtensions +{ + private static readonly IDictionary EmptyDictionary = + new ReadOnlyDictionary(new Dictionary()); + + public static User ToUser(this Java.Protocol.User user) => + new() + { + Email = user.Email, + Id = user.Id, + IpAddress = user.IpAddress, + Username = user.Username, + Other = user.Others ?? EmptyDictionary + }; + + public static Java.Protocol.User ToJavaUser(this User user) => + new() + { + Email = user.Email, + Id = user.Id, + IpAddress = user.IpAddress, + Username = user.Username, + Others = user.Other.Count == 0 ? null : user.Other + }; +} diff --git a/src/Sentry/Android/SentrySdk.cs b/src/Sentry/Android/SentrySdk.cs index 519de58b4f..cf8c10d6e8 100644 --- a/src/Sentry/Android/SentrySdk.cs +++ b/src/Sentry/Android/SentrySdk.cs @@ -160,6 +160,8 @@ public static IDisposable Init(AndroidContext context, SentryOptions options) options.IsGlobalModeEnabled = true; options.AddEventProcessor(new AndroidEventProcessor(androidOptions!)); options.CrashedLastRun = () => Java.Sentry.IsCrashedLastRun()?.BooleanValue() is true; + options.EnableScopeSync = true; + options.ScopeObserver = new AndroidScopeObserver(options); // TODO: Pause/Resume // Init the managed SDK