diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c1beb2b4..40dd39dc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,18 @@ ### Breaking Changes -- Dropped support for Unity 2019. It reached End of Life in 2022 ([#2231](https://github.com/getsentry/sentry-unity/pull/2231)) +- Removed Unity 2019 support, which reached End of Life in 2022. Minimum supported version now is 2020 ([#2231](https://github.com/getsentry/sentry-unity/pull/2231)) + +- **Breaking Change**: The Unity SDK's static API has been simplified moved from `Sentry.Unity.SentryUnity` and `Sentry.SentrySdk` + to `Sentry.Unity.SentrySdk`. + This change enables [manual/programatic SDK initialization](https://docs.sentry.io/platforms/unity/configuration/options/programmatic-configuration/) with full functionality, previously only available through auto-initialization. + The underlying .NET SDK's `SentrySdk` class is now internal, and several previously public classes like `SentryInitialization` + and `SentryIntegrations` are now internal. + + **Migration**: Update your `using` directives from `using Sentry;` to `using Sentry.Unity;`. IDEs like Rider can automatically + import the missing references. In some cases, you may need both `using Sentry.Unity;` (for the static API) and `using Sentry;` + (for types like `SentryId`). No changes are required to your actual SDK method calls (e.g., `SentrySdk.CaptureException()` + remains the same). ([#2227](https://github.com/getsentry/sentry-unity/pull/2227)) ### Features diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index e98ce8318..dc0485125 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -15,7 +15,9 @@ #endif using System; +using Sentry.Unity; using Sentry.Extensibility; +using Sentry.Unity.NativeUtils; #if UNITY_2020_3_OR_NEWER using System.Buffers; using System.Runtime.InteropServices; @@ -39,61 +41,57 @@ namespace Sentry.Unity { - public static class SentryInitialization + internal static class SentryInitialization { + /// + /// This is intended for internal use only. + /// The SDK relies on SetupPlatformServices getting called as the very first thing during the game's + /// startup. This ensures that features like line number and native support are set up and configured properly. + /// This is also the case when initializing manually from code. + /// #if SENTRY_WEBGL // On WebGL SubsystemRegistration is too early for the UnityWebRequestTransport and errors with 'URI empty' [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] #else [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] #endif - public static void Init() + private static void Init() { - var unityInfo = new SentryUnityInfo(); + // We're setting up `UnityInfo` and the platform specific configure callbacks as the very first thing. + // These are required to be available during initialization. + SetupPlatformServices(); + // Loading the options invokes the ScriptableOption`Configure` callback. Users can disable the SDK via code. - var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(unityInfo); + var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(); if (options != null && options.ShouldInitializeSdk()) { - // Configures scope sync and (by default) initializes the native SDK. - SetupNativeSdk(options, unityInfo); - SentryUnity.Init(options); + SentrySdk.Init(options); } else { // If the SDK is not `enabled` we're closing down the native layer as well. This is especially relevant // in a `built-time-initialization` scenario where the native SDKs self-initialize. #if SENTRY_NATIVE_COCOA - SentryNativeCocoa.Close(options, unityInfo); + SentryNativeCocoa.Close(options); #elif SENTRY_NATIVE_ANDROID - SentryNativeAndroid.Close(options, unityInfo); + SentryNativeAndroid.Close(options); #endif } } - private static void SetupNativeSdk(SentryUnityOptions options, SentryUnityInfo unityInfo) + private static void SetupPlatformServices() { - try - { + SentryPlatformServices.UnityInfo = new SentryUnityInfo(); + #if SENTRY_NATIVE_COCOA - SentryNativeCocoa.Configure(options, unityInfo); + SentryPlatformServices.PlatformConfiguration = SentryNativeCocoa.Configure; #elif SENTRY_NATIVE_ANDROID - SentryNativeAndroid.Configure(options, unityInfo); + SentryPlatformServices.PlatformConfiguration = SentryNativeAndroid.Configure; #elif SENTRY_NATIVE - SentryNative.Configure(options, unityInfo); + SentryPlatformServices.PlatformConfiguration = SentryNative.Configure; #elif SENTRY_WEBGL - SentryWebGL.Configure(options); + SentryPlatformServices.PlatformConfiguration = SentryWebGL.Configure; #endif - } - catch (DllNotFoundException e) - { - options.DiagnosticLogger?.LogError(e, - "Sentry native-error capture configuration failed to load a native library. This usually " + - "means the library is missing from the application bundle or the installation directory."); - } - catch (Exception e) - { - options.DiagnosticLogger?.LogError(e, "Sentry native error capture configuration failed."); - } } } diff --git a/package-dev/Runtime/SentryUserFeedback.cs b/package-dev/Runtime/SentryUserFeedback.cs index 4e01d3a06..b6475b802 100644 --- a/package-dev/Runtime/SentryUserFeedback.cs +++ b/package-dev/Runtime/SentryUserFeedback.cs @@ -86,7 +86,7 @@ public void SendFeedback() else { // Since there is no screenshot added we can capture the feedback right away - SentryUnity.CaptureFeedback(_description.text, _email.text, _name.text, addScreenshot: false); + SentrySdk.CaptureFeedback(_description.text, _email.text, _name.text, addScreenshot: false); } } @@ -99,7 +99,7 @@ private IEnumerator HideFormAndCaptureFeedback() // We're waiting for the EndOfFrame so the FeedbackForm gets updated before capturing the screenshot yield return new WaitForEndOfFrame(); - SentryUnity.CaptureFeedback(_description.text, _email.text, _name.text, addScreenshot: true); + SentrySdk.CaptureFeedback(_description.text, _email.text, _name.text, addScreenshot: true); ResetUserFeedback(); } diff --git a/samples/unity-of-bugs/Assets/Scripts/AdditionalButtons.cs b/samples/unity-of-bugs/Assets/Scripts/AdditionalButtons.cs index 7d8afd0a7..9ab032cac 100644 --- a/samples/unity-of-bugs/Assets/Scripts/AdditionalButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/AdditionalButtons.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Threading; -using System.Threading.Tasks; using Sentry; +using Sentry.Unity; using UnityEngine; public class AdditionalButtons : MonoBehaviour diff --git a/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs b/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs index 674fc54c3..f43152920 100644 --- a/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs @@ -1,7 +1,7 @@ using System; -using System.Globalization; using System.Runtime.CompilerServices; using Sentry; +using Sentry.Unity; using UnityEngine; using UnityEngine.Assertions; diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/NativeButtons.cs b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/NativeButtons.cs index c43ea33da..28ce26844 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/NativeButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/NativeButtons.cs @@ -1,7 +1,7 @@ -using Sentry; -using System; +using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using Sentry.Unity; using UnityEngine; using UnityEngine.Diagnostics; using UnityEngine.UI; diff --git a/samples/unity-of-bugs/Assets/Scripts/ThreadingSamples.cs b/samples/unity-of-bugs/Assets/Scripts/ThreadingSamples.cs index 7aac93994..b8cf7e3be 100644 --- a/samples/unity-of-bugs/Assets/Scripts/ThreadingSamples.cs +++ b/samples/unity-of-bugs/Assets/Scripts/ThreadingSamples.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Sentry; +using Sentry.Unity; using UnityEngine; using UnityEngine.Assertions; diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 76ee1f7ae..6c3e6daea 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -1,6 +1,7 @@ using System; using Sentry.Extensibility; using Sentry.Unity.Integrations; +using Sentry.Unity.NativeUtils; using UnityEngine; using UnityEngine.Analytics; @@ -19,7 +20,7 @@ public static class SentryNativeAndroid /// Configures the native Android support. /// /// The Sentry Unity options to use. - public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) + public static void Configure(SentryUnityOptions options) { MainThreadData.CollectData(); @@ -105,7 +106,7 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry e, "Failed to reinstall backend. Captured native crashes will miss scope data and tag."); } - options.NativeSupportCloseCallback = () => Close(options, sentryUnityInfo); + options.NativeSupportCloseCallback = () => Close(options); options.DiagnosticLogger?.LogDebug("Fetching installation ID"); @@ -136,14 +137,11 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry /// /// Closes the native Android support. /// - public static void Close(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) => - Close(options, sentryUnityInfo, ApplicationAdapter.Instance.Platform); - - internal static void Close(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo, RuntimePlatform platform) + public static void Close(SentryUnityOptions options) { options.DiagnosticLogger?.LogInfo("Attempting to close the Android SDK"); - if (!sentryUnityInfo.IsNativeSupportEnabled(options, platform)) + if (!options.UnityInfo.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform)) { options.DiagnosticLogger?.LogDebug("Android Native Support is not enabled. Skipping closing the Android SDK"); return; diff --git a/src/Sentry.Unity.Editor/SentryScriptableObject.cs b/src/Sentry.Unity.Editor/SentryScriptableObject.cs index 7764c119f..844e43f93 100644 --- a/src/Sentry.Unity.Editor/SentryScriptableObject.cs +++ b/src/Sentry.Unity.Editor/SentryScriptableObject.cs @@ -36,7 +36,7 @@ internal static (SentryUnityOptions?, SentryCliOptions?) ConfiguredBuildTimeOpti SentryUnityOptions? options = null; if (scriptableOptions is not null) { - options = scriptableOptions.ToSentryUnityOptions(isBuilding: true, unityInfo: null); + options = scriptableOptions.ToSentryUnityOptions(isBuilding: true); // TODO: Move this into `Load` once we remove Runtime- and BuildTimeConfig // We're calling `Configure` here and not in `Load` so the new Config does not overwrite the BuildTimeConfig diff --git a/src/Sentry.Unity.Native/SentryNative.cs b/src/Sentry.Unity.Native/SentryNative.cs index 6f15a65f8..d4b132d52 100644 --- a/src/Sentry.Unity.Native/SentryNative.cs +++ b/src/Sentry.Unity.Native/SentryNative.cs @@ -2,6 +2,7 @@ using Sentry.Extensibility; using Sentry.Unity.Integrations; using System.Collections.Generic; +using Sentry.Unity.NativeUtils; using UnityEngine; using UnityEngine.Analytics; @@ -22,13 +23,13 @@ public static class SentryNative /// /// The Sentry Unity options to use. /// Infos about the current Unity environment - public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) + public static void Configure(SentryUnityOptions options) { Logger = options.DiagnosticLogger; Logger?.LogInfo("Attempting to configure native support via the Native SDK"); - if (!sentryUnityInfo.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform)) + if (!options.UnityInfo.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform)) { Logger?.LogDebug("Native support is disabled for '{0}'.", ApplicationAdapter.Instance.Platform); return; @@ -36,7 +37,7 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry try { - if (!SentryNativeBridge.Init(options, sentryUnityInfo)) + if (!SentryNativeBridge.Init(options)) { Logger?.LogWarning("Sentry native initialization failed - native crashes are not captured."); return; diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index 7d67d3325..bdfab9f00 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -12,13 +12,11 @@ namespace Sentry.Unity.Native; /// P/Invoke to `sentry-native` functions. /// /// -public static class SentryNativeBridge +internal static class SentryNativeBridge { - public static bool CrashedLastRun; - - public static bool Init(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) + public static bool Init(SentryUnityOptions options) { - _isLinux = sentryUnityInfo.IsLinux(); + _isLinux = options.UnityInfo.IsLinux(); var cOptions = sentry_options_new(); diff --git a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs index 766fe5a55..6da06232d 100644 --- a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs +++ b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs @@ -14,15 +14,15 @@ public static class SentryNativeCocoa /// Configures the native support. /// /// The Sentry Unity options to use. - /// Infos about the current Unity environment - public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) => - Configure(options, sentryUnityInfo, ApplicationAdapter.Instance.Platform); + public static void Configure(SentryUnityOptions options) => + Configure(options, ApplicationAdapter.Instance.Platform); - internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo, RuntimePlatform platform) + // For testing + internal static void Configure(SentryUnityOptions options, RuntimePlatform platform) { options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Cocoa SDK"); - if (!sentryUnityInfo.IsNativeSupportEnabled(options, platform)) + if (!options.UnityInfo.IsNativeSupportEnabled(options, platform)) { options.DiagnosticLogger?.LogDebug("Native support is disabled for: '{0}'", platform); return; @@ -66,8 +66,8 @@ internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sent return crashedLastRun; }; - options.NativeSupportCloseCallback += () => Close(options, sentryUnityInfo, platform); - if (sentryUnityInfo.IL2CPP) + options.NativeSupportCloseCallback += () => Close(options); + if (options.UnityInfo.IL2CPP) { options.DefaultUserId = SentryCocoaBridgeProxy.GetInstallationId(); if (string.IsNullOrEmpty(options.DefaultUserId)) @@ -96,14 +96,11 @@ internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sent /// /// Closes the native Cocoa support. /// - public static void Close(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) => - Close(options, sentryUnityInfo, ApplicationAdapter.Instance.Platform); - - internal static void Close(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo, RuntimePlatform platform) + public static void Close(SentryUnityOptions options) { options.DiagnosticLogger?.LogInfo("Attempting to close the Cocoa SDK"); - if (!sentryUnityInfo.IsNativeSupportEnabled(options, platform)) + if (!options.UnityInfo.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform)) { options.DiagnosticLogger?.LogDebug("Cocoa Native Support is not enable. Skipping closing the Cocoa SDK"); return; diff --git a/src/Sentry.Unity/Il2CppEventProcessor.cs b/src/Sentry.Unity/Il2CppEventProcessor.cs index 612dcbc93..68247ef77 100644 --- a/src/Sentry.Unity/Il2CppEventProcessor.cs +++ b/src/Sentry.Unity/Il2CppEventProcessor.cs @@ -17,11 +17,11 @@ internal class UnityIl2CppEventExceptionProcessor : ISentryEventExceptionProcess private static ISentryUnityInfo UnityInfo = null!; // private static will be initialized in the constructor private readonly Il2CppMethods _il2CppMethods; - public UnityIl2CppEventExceptionProcessor(SentryUnityOptions options, ISentryUnityInfo unityInfo) + public UnityIl2CppEventExceptionProcessor(SentryUnityOptions options) { Options = options; - UnityInfo = unityInfo; - _il2CppMethods = unityInfo.Il2CppMethods ?? throw new ArgumentNullException(nameof(unityInfo.Il2CppMethods), + UnityInfo = options.UnityInfo; + _il2CppMethods = UnityInfo.Il2CppMethods ?? throw new ArgumentNullException(nameof(UnityInfo.Il2CppMethods), "Unity IL2CPP methods are not available."); Options.SdkIntegrationNames.Add("IL2CPPLineNumbers"); diff --git a/src/Sentry.Unity/NativeUtils/SentryPlatformServices.cs b/src/Sentry.Unity/NativeUtils/SentryPlatformServices.cs new file mode 100644 index 000000000..3070de469 --- /dev/null +++ b/src/Sentry.Unity/NativeUtils/SentryPlatformServices.cs @@ -0,0 +1,36 @@ +using System; + +namespace Sentry.Unity.NativeUtils; + +/// +/// These are SDK's services that are only available at runtime and cannot be baked into the SDK. The +/// SentryInitialization.cs is provided as .cs and gets compiled with the game. It sets IUnityInfo +/// and the PlatformConfiguration callback during the game's startup so that they are available during initializtion. +/// +/// Consider this internal. +public static class SentryPlatformServices +{ + private static ISentryUnityInfo? _unityInfo; + + /// + /// The UnityInfo holds methods that rely on conditionally compilation, i.e. IL2CPP backend. + /// + public static ISentryUnityInfo UnityInfo + { + get => _unityInfo ?? throw new InvalidOperationException("UnityInfo is null."); + set + { + if (_unityInfo != null) + { + throw new InvalidOperationException("Should not set twice. lol."); + } + + _unityInfo = value; + } + } + + /// + /// The PlatformConfiguration callback is responsible for configuring the native SDK and setting up scope sync. + /// + public static Action? PlatformConfiguration { get; set; } +} diff --git a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs index 3684172fb..3c7ec50c9 100644 --- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs +++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Sentry.Extensibility; using Sentry.Unity.Integrations; +using Sentry.Unity.NativeUtils; using UnityEngine; namespace Sentry.Unity; @@ -120,22 +121,22 @@ public static string GetConfigPath(string? notDefaultConfigName = null) /// /// Used for loading the SentryUnityOptions from the ScriptableSentryUnityOptions during runtime. /// - public static SentryUnityOptions? LoadSentryUnityOptions(ISentryUnityInfo unityInfo) + public static SentryUnityOptions? LoadSentryUnityOptions() { var scriptableOptions = Resources.Load($"{ConfigRootFolder}/{ConfigName}"); if (scriptableOptions is not null) { - return scriptableOptions.ToSentryUnityOptions(false, unityInfo); + return scriptableOptions.ToSentryUnityOptions(false); } return null; } - internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityInfo? unityInfo, IApplication? application = null) + internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, IApplication? application = null) { application ??= ApplicationAdapter.Instance; - var options = new SentryUnityOptions(isBuilding, application, unityInfo) + var options = new SentryUnityOptions(isBuilding, application) { Enabled = Enabled, Dsn = Dsn, @@ -235,12 +236,12 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn options.AddEventProcessor(new ScreenshotEventProcessor(options)); } - if (!application.IsEditor && options.Il2CppLineNumberSupportEnabled && unityInfo is not null) + if (!application.IsEditor && options.Il2CppLineNumberSupportEnabled) { - options.AddIl2CppExceptionProcessor(unityInfo); + options.AddIl2CppExceptionProcessor(); } - HandlePlatformRestrictedOptions(options, unityInfo, application); + HandlePlatformRestrictedOptions(options, application); HandleExceptionFilter(options); if (!AnrDetectionEnabled) @@ -251,9 +252,9 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn return options; } - internal void HandlePlatformRestrictedOptions(SentryUnityOptions options, ISentryUnityInfo? unityInfo, IApplication application) + internal void HandlePlatformRestrictedOptions(SentryUnityOptions options, IApplication application) { - if (unityInfo?.IsKnownPlatform() == false) + if (!options.UnityInfo.IsKnownPlatform()) { options.DisableFileWrite = true; diff --git a/src/Sentry.Unity/SentrySdk.Dotnet.cs b/src/Sentry.Unity/SentrySdk.Dotnet.cs new file mode 100644 index 000000000..f9548d83e --- /dev/null +++ b/src/Sentry.Unity/SentrySdk.Dotnet.cs @@ -0,0 +1,543 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Threading.Tasks; +using Sentry.Infrastructure; +using Sentry.Protocol.Envelopes; + +namespace Sentry.Unity; + +public static partial class SentrySdk +{ + /// + /// Flushes the queue of captured events until the timeout set in + /// is reached. + /// + /// + /// Blocks synchronously. Prefer in async code. + /// + [DebuggerStepThrough] + public static void Flush() => Sentry.SentrySdk.Flush(); + + /// + /// Flushes the queue of captured events until the timeout is reached. + /// + /// The amount of time allowed for flushing. + /// + /// Blocks synchronously. Prefer in async code. + /// + [DebuggerStepThrough] + public static void Flush(TimeSpan timeout) => Sentry.SentrySdk.Flush(timeout); + + /// + /// Flushes the queue of captured events until the timeout set in + /// is reached. + /// + /// A task to await for the flush operation. + [DebuggerStepThrough] + public static Task FlushAsync() => Sentry.SentrySdk.FlushAsync(); + + /// + /// Flushes the queue of captured events until the timeout is reached. + /// + /// The amount of time allowed for flushing. + /// A task to await for the flush operation. + [DebuggerStepThrough] + public static Task FlushAsync(TimeSpan timeout) => Sentry.SentrySdk.FlushAsync(timeout); + + /// + /// Whether the SDK is enabled or not. + /// + public static bool IsEnabled { [DebuggerStepThrough] get => Sentry.SentrySdk.IsEnabled; } + + /// + /// Creates a new scope that will terminate when disposed. + /// + /// + /// Pushes a new scope while inheriting the current scope's data. + /// + /// A state object to be added to the scope. + /// A disposable that when disposed, ends the created scope. + [DebuggerStepThrough] + public static IDisposable PushScope(TState state) => Sentry.SentrySdk.PushScope(state); + + /// + /// Creates a new scope that will terminate when disposed. + /// + /// A disposable that when disposed, ends the created scope. + [DebuggerStepThrough] + public static IDisposable PushScope() => Sentry.SentrySdk.PushScope(); + + /// + /// Binds the client to the current scope. + /// + /// The client. + [DebuggerStepThrough] + public static void BindClient(ISentryClient client) => Sentry.SentrySdk.BindClient(client); + + /// + /// Adds a breadcrumb to the current Scope. + /// + /// + /// If a message is provided it’s rendered as text and the whitespace is preserved. + /// Very long text might be abbreviated in the UI. + /// + /// Categories are dotted strings that indicate what the crumb is or where it comes from. + /// Typically it’s a module name or a descriptive string. + /// For instance ui.click could be used to indicate that a click happened in the UI or flask could be used to indicate that the event originated in the Flask framework. + /// + /// + /// The type of breadcrumb. + /// The default type is default which indicates no specific handling. + /// Other types are currently http for HTTP requests and navigation for navigation events. + /// + /// + /// + /// Data associated with this breadcrumb. + /// Contains a sub-object whose contents depend on the breadcrumb type. + /// Additional parameters that are unsupported by the type are rendered as a key/value table. + /// + /// Breadcrumb level. + /// + [DebuggerStepThrough] + public static void AddBreadcrumb( + string message, + string? category = null, + string? type = null, + IDictionary? data = null, + BreadcrumbLevel level = default) + => Sentry.SentrySdk.AddBreadcrumb(message, category, type, data, level); + + /// + /// Adds a breadcrumb to the current scope. + /// + /// + /// This overload is intended to be used by integrations only. + /// The objective is to allow better testability by allowing control of the timestamp set to the breadcrumb. + /// + /// An optional . + /// The message. + /// The category. + /// The type. + /// The data. + /// The level. + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public static void AddBreadcrumb( + ISystemClock? clock, + string message, + string? category = null, + string? type = null, + IDictionary? data = null, + BreadcrumbLevel level = default) + => Sentry.SentrySdk.AddBreadcrumb(clock, message, category, type, data, level); + + /// + /// Adds a breadcrumb to the current Scope. + /// + /// The breadcrumb to be added + /// A hint providing additional context that can be used in the BeforeBreadcrumb callback + /// + [DebuggerStepThrough] + public static void AddBreadcrumb(Breadcrumb breadcrumb, SentryHint? hint = null) + => Sentry.SentrySdk.AddBreadcrumb(breadcrumb, hint); + + /// + /// Configures the scope through the callback. + /// + /// The configure scope callback. + [DebuggerStepThrough] + public static void ConfigureScope(Action configureScope) + => Sentry.SentrySdk.ConfigureScope(configureScope); + + /// + /// Configures the scope through the callback. + /// + /// + /// object someValue = ...; + /// SentrySdk.ConfigureScope(static (scope, arg) => scope.SetExtra("key", arg), someValue); + /// + /// + /// + /// The configure scope callback. + /// The argument to pass to the configure scope callback. + public static void ConfigureScope(Action configureScope, TArg arg) + => Sentry.SentrySdk.ConfigureScope(configureScope, arg); + + /// + /// Configures the scope through the callback asynchronously. + /// + /// The configure scope callback. + /// A task that completes when the callback is done or a completed task if the SDK is disabled. + [DebuggerStepThrough] + public static Task ConfigureScopeAsync(Func configureScope) + => Sentry.SentrySdk.ConfigureScopeAsync(configureScope); + + /// + /// Configures the scope through the callback asynchronously. + /// + /// + /// object someValue = ...; + /// SentrySdk.ConfigureScopeAsync(static async (scope, arg) => + /// { + /// scope.SetExtra("key", arg); + /// }, someValue); + /// + /// + /// + /// The configure scope callback. + /// The argument to pass to the configure scope callback. + /// A task that completes when the callback is done or a completed task if the SDK is disabled. + [DebuggerStepThrough] + public static Task ConfigureScopeAsync(Func configureScope, TArg arg) + => Sentry.SentrySdk.ConfigureScopeAsync(configureScope, arg); + + /// + /// Sets a tag on the current scope. + /// + [DebuggerStepThrough] + public static void SetTag(string key, string value) + => Sentry.SentrySdk.SetTag(key, value); + + /// + /// Removes a tag from the current scope. + /// + [DebuggerStepThrough] + public static void UnsetTag(string key) + => Sentry.SentrySdk.UnsetTag(key); + + /// + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public static bool CaptureEnvelope(Envelope envelope) + => Sentry.SentrySdk.CaptureEnvelope(envelope); + + /// + /// Captures the event, passing a hint, using the specified scope. + /// + /// The event. + /// The scope. + /// a hint for the event. + /// The Id of the event. + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public static SentryId CaptureEvent(SentryEvent evt, Scope? scope = null, SentryHint? hint = null) + => Sentry.SentrySdk.CaptureEvent(evt, scope, hint); + + /// + /// Captures an event with a configurable scope. + /// + /// + /// This allows modifying a scope without affecting other events. + /// + /// The event. + /// The callback to configure the scope. + /// The Id of the event. + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public static SentryId CaptureEvent(SentryEvent evt, Action configureScope) + => Sentry.SentrySdk.CaptureEvent(evt, null, configureScope); + + /// + /// Captures an event with a configurable scope. + /// + /// + /// This allows modifying a scope without affecting other events. + /// + /// The event. + /// An optional hint to be provided with the event + /// The callback to configure the scope. + /// The Id of the event. + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public static SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Action configureScope) + => Sentry.SentrySdk.CaptureEvent(evt, hint, configureScope); + + /// + /// Captures the exception. + /// + /// The exception. + /// The Id of the event. + [DebuggerStepThrough] + public static SentryId CaptureException(Exception exception) + => Sentry.SentrySdk.CaptureException(exception); + + /// + /// Captures the exception with a configurable scope. + /// + /// + /// This allows modifying a scope without affecting other events. + /// + /// The exception. + /// The callback to configure the scope. + /// The Id of the event. + [DebuggerStepThrough] + public static SentryId CaptureException(Exception exception, Action configureScope) + => Sentry.SentrySdk.CaptureException(exception, configureScope); + + /// + /// Captures the message. + /// + /// The message to send. + /// The message level. + /// The Id of the event. + [DebuggerStepThrough] + public static SentryId CaptureMessage(string message, SentryLevel level = SentryLevel.Info) + => Sentry.SentrySdk.CaptureMessage(message, level); + + /// + /// Captures the message with a configurable scope. + /// + /// + /// This allows modifying a scope without affecting other events. + /// + /// The message to send. + /// The callback to configure the scope. + /// The message level. + /// The Id of the event. + [DebuggerStepThrough] + public static SentryId CaptureMessage(string message, Action configureScope, SentryLevel level = SentryLevel.Info) + => Sentry.SentrySdk.CaptureMessage(message, configureScope, level); + + /// + /// Captures feedback from the user. + /// + [DebuggerStepThrough] + public static void CaptureFeedback(SentryFeedback feedback, Action configureScope, SentryHint? hint = null) + => Sentry.SentrySdk.CaptureFeedback(feedback, configureScope, hint); + + /// + /// Captures feedback from the user. + /// + [DebuggerStepThrough] + public static void CaptureFeedback(SentryFeedback feedback, Scope? scope = null, SentryHint? hint = null) + => Sentry.SentrySdk.CaptureFeedback(feedback, scope, hint); + + /// + /// Captures feedback from the user. + /// + [DebuggerStepThrough] + public static void CaptureFeedback(string message, string? contactEmail = null, string? name = null, + string? replayId = null, string? url = null, SentryId? associatedEventId = null, Scope? scope = null, + SentryHint? hint = null) + => Sentry.SentrySdk.CaptureFeedback(new SentryFeedback(message, contactEmail, name, replayId, url, associatedEventId), + scope, hint); + + /// + /// Captures a user feedback. + /// + /// The user feedback to send to Sentry. + [DebuggerStepThrough] + [Obsolete("Use CaptureFeedback instead.")] + public static void CaptureUserFeedback(UserFeedback userFeedback) + => Sentry.SentrySdk.CaptureUserFeedback(userFeedback); + + /// + /// Captures a user feedback. + /// + /// The event Id. + /// The user email. + /// The user comments. + /// The optional username. + [DebuggerStepThrough] + [Obsolete("Use CaptureFeedback instead.")] + public static void CaptureUserFeedback(SentryId eventId, string email, string comments, string? name = null) + => Sentry.SentrySdk.CaptureUserFeedback(new UserFeedback(eventId, name, email, comments)); + + /// + /// Captures a transaction. + /// + /// + /// Note: this method is NOT meant to be called from user code! + /// Instead, call on the transaction. + /// + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public static void CaptureTransaction(SentryTransaction transaction) + => Sentry.SentrySdk.CaptureTransaction(transaction); + + /// + /// Captures a transaction. + /// + /// + /// Note: this method is NOT meant to be called from user code! + /// Instead, call on the transaction. + /// + [DebuggerStepThrough] + [EditorBrowsable(EditorBrowsableState.Never)] + public static void CaptureTransaction(SentryTransaction transaction, Scope? scope, SentryHint? hint) + => Sentry.SentrySdk.CaptureTransaction(transaction, scope, hint); + + /// + /// Captures a session update. + /// + [DebuggerStepThrough] + public static void CaptureSession(SessionUpdate sessionUpdate) + => Sentry.SentrySdk.CaptureSession(sessionUpdate); + + /// + /// Captures a check-in. + /// + /// + /// Capturing a check-in returns an ID. The ID can be used to update the status. I.e. to update a check-in you + /// captured from `CheckInStatus.InProgress` to `CheckInStatus.Ok`. + /// + /// The monitor slug of the check-in. + /// The status of the check-in. + /// The associated with the check-in. + /// The duration of the check-in. + /// The scope of the check-in. + /// The optional monitor config used to create a Check-In programmatically. + /// The ID of the check-in. + [DebuggerStepThrough] + public static SentryId CaptureCheckIn(string monitorSlug, + CheckInStatus status, + SentryId? sentryId = null, + TimeSpan? duration = null, + Scope? scope = null, + Action? configureMonitorOptions = null) + => Sentry.SentrySdk.CaptureCheckIn( + monitorSlug, + status, + sentryId, + duration, + scope, + configureMonitorOptions); + + /// + /// Starts a transaction. + /// + [DebuggerStepThrough] + public static ITransactionTracer StartTransaction( + ITransactionContext context, + IReadOnlyDictionary customSamplingContext) + => Sentry.SentrySdk.StartTransaction(context, customSamplingContext); + + /// + /// Starts a transaction. + /// + [DebuggerStepThrough] + internal static ITransactionTracer StartTransaction( + ITransactionContext context, + IReadOnlyDictionary customSamplingContext, + DynamicSamplingContext? dynamicSamplingContext) + => Sentry.SentrySdk.StartTransaction(context, customSamplingContext, dynamicSamplingContext); + + /// + /// Starts a transaction. + /// + [DebuggerStepThrough] + public static ITransactionTracer StartTransaction(ITransactionContext context) + => Sentry.SentrySdk.StartTransaction(context); + + /// + /// Starts a transaction. + /// + [DebuggerStepThrough] + public static ITransactionTracer StartTransaction(string name, string operation) + => Sentry.SentrySdk.StartTransaction(name, operation); + + /// + /// Starts a transaction. + /// + [DebuggerStepThrough] + public static ITransactionTracer StartTransaction(string name, string operation, string? description) + => Sentry.SentrySdk.StartTransaction(name, operation, description); + + /// + /// Starts a transaction. + /// + [DebuggerStepThrough] + public static ITransactionTracer StartTransaction(string name, string operation, SentryTraceHeader traceHeader) + => Sentry.SentrySdk.StartTransaction(name, operation, traceHeader); + + /// + /// Binds specified exception the specified span. + /// + /// + /// This method is used internally and is not meant for public use. + /// + [DebuggerStepThrough] + public static void BindException(Exception exception, ISpan span) + => Sentry.SentrySdk.BindException(exception, span); + + /// + /// Gets the last active span. + /// + [DebuggerStepThrough] + public static ISpan? GetSpan() + => Sentry.SentrySdk.GetSpan(); + + /// + /// Gets the Sentry trace header of the parent that allows tracing across services + /// + [DebuggerStepThrough] + public static SentryTraceHeader? GetTraceHeader() + => Sentry.SentrySdk.GetTraceHeader(); + + /// + /// Gets the Sentry "baggage" header that allows tracing across services + /// + [DebuggerStepThrough] + public static BaggageHeader? GetBaggage() + => Sentry.SentrySdk.GetBaggage(); + + /// + /// Continues a trace based on HTTP header values provided as strings. + /// + /// + /// If no "sentry-trace" header is provided a random trace ID and span ID is created. + /// + [DebuggerStepThrough] + public static TransactionContext ContinueTrace( + string? traceHeader, + string? baggageHeader, + string? name = null, + string? operation = null) + => Sentry.SentrySdk.ContinueTrace(traceHeader, baggageHeader, name, operation); + + /// + /// Continues a trace based on HTTP header values. + /// + /// + /// If no "sentry-trace" header is provided a random trace ID and span ID is created. + /// + [DebuggerStepThrough] + public static TransactionContext ContinueTrace( + SentryTraceHeader? traceHeader, + BaggageHeader? baggageHeader, + string? name = null, + string? operation = null) + => Sentry.SentrySdk.ContinueTrace(traceHeader, baggageHeader, name, operation); + + /// + [DebuggerStepThrough] + public static void StartSession() + => Sentry.SentrySdk.StartSession(); + + /// + [DebuggerStepThrough] + public static void EndSession(SessionEndStatus status = SessionEndStatus.Exited) + => Sentry.SentrySdk.EndSession(status); + + /// + [DebuggerStepThrough] + public static void PauseSession() + => Sentry.SentrySdk.PauseSession(); + + /// + [DebuggerStepThrough] + public static void ResumeSession() + => Sentry.SentrySdk.ResumeSession(); + + /// + /// Deliberately crashes an application, which is useful for testing and demonstration purposes. + /// + /// + /// The method is marked obsolete only to discourage accidental misuse. + /// We do not intend to remove it. + /// + [Obsolete("WARNING: This method deliberately causes a crash, and should not be used in a real application.")] + public static void CauseCrash(CrashType crashType) => Sentry.SentrySdk.CauseCrash(crashType); +} diff --git a/src/Sentry.Unity/SentryUnity.cs b/src/Sentry.Unity/SentrySdk.cs similarity index 74% rename from src/Sentry.Unity/SentryUnity.cs rename to src/Sentry.Unity/SentrySdk.cs index c3b69806e..d5034a267 100644 --- a/src/Sentry.Unity/SentryUnity.cs +++ b/src/Sentry.Unity/SentrySdk.cs @@ -1,14 +1,14 @@ using System; using System.ComponentModel; using Sentry.Extensibility; -using UnityEngine; +using Sentry.Unity.NativeUtils; namespace Sentry.Unity; /// /// Sentry Unity initialization class. /// -public static class SentryUnity +public static partial class SentrySdk { private static SentryUnitySdk? UnitySdk; @@ -33,7 +33,23 @@ public static void Init(SentryUnityOptions options) { if (UnitySdk is not null) { - options.DiagnosticLogger?.LogWarning("The SDK has already been initialized."); + options.LogWarning("The SDK has already been initialized. Skipping initialization."); + } + + try + { + // Since this mutates the options (i.e. adding scope observer) we have to invoke before initializing the SDK + options.PlatformConfiguration.Invoke(options); + } + catch (DllNotFoundException e) + { + options.LogError(e, + "Sentry native-error capture configuration failed to load a native library. This usually " + + "means the library is missing from the application bundle or the installation directory."); + } + catch (Exception e) + { + options.LogError(e, "Sentry native error capture configuration failed."); } UnitySdk = SentryUnitySdk.Init(options); @@ -50,7 +66,7 @@ public static void Close() } /// - /// Represents the crash state of the games's previous run. + /// Represents the crash state of the game's previous run. /// Used to determine if the last execution terminated normally or crashed. /// public enum CrashedLastRun diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index ed1184db2..94bed43f8 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -4,6 +4,7 @@ using System.Text.RegularExpressions; using Sentry.Unity.Integrations; using Sentry.Extensibility; +using Sentry.Unity.NativeUtils; using UnityEngine; using CompressionLevel = System.IO.Compression.CompressionLevel; @@ -294,6 +295,9 @@ internal string? DefaultUserId internal List SdkIntegrationNames { get; set; } = new(); + internal ISentryUnityInfo UnityInfo { get; private set; } + internal Action PlatformConfiguration { get; private set; } + public SentryUnityOptions() : this(false, ApplicationAdapter.Instance) { } internal SentryUnityOptions(bool isBuilding, IApplication application, ISentryUnityInfo? unityInfo = null) : @@ -301,8 +305,14 @@ internal SentryUnityOptions(bool isBuilding, IApplication application, ISentryUn { } // For testing - internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication application, bool isBuilding, ISentryUnityInfo? unityInfo = null) + private SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication application, bool isBuilding, ISentryUnityInfo? unityInfo) { + // This should never happen. The PlatformServices are set through the RuntimeLoad attribute in 'SentryInitialization.cs' + // and required to be present. + UnityInfo = unityInfo ?? SentryPlatformServices.UnityInfo; + PlatformConfiguration = SentryPlatformServices.PlatformConfiguration + ?? throw new ArgumentNullException(nameof(SentryPlatformServices.PlatformConfiguration), "PlatformConfiguration is null."); + // IL2CPP doesn't support Process.GetCurrentProcess().StartupTime DetectStartupTime = StartupTimeDetectionMode.Fast; diff --git a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs index 773e45391..ea97618f5 100644 --- a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs +++ b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs @@ -63,16 +63,9 @@ internal static void SetupUnityLogging(this SentryUnityOptions options) } } - internal static void AddIl2CppExceptionProcessor(this SentryUnityOptions options, ISentryUnityInfo unityInfo) + internal static void AddIl2CppExceptionProcessor(this SentryUnityOptions options) { - if (unityInfo.Il2CppMethods is not null) - { - options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options, unityInfo)); - } - else - { - options.DiagnosticLogger?.LogWarning("Failed to find required IL2CPP methods - Skipping line number support"); - } + options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options)); } /// diff --git a/src/Sentry.Unity/SentryUnitySDK.cs b/src/Sentry.Unity/SentryUnitySdk.cs similarity index 86% rename from src/Sentry.Unity/SentryUnitySDK.cs rename to src/Sentry.Unity/SentryUnitySdk.cs index 2228ef8db..052139275 100644 --- a/src/Sentry.Unity/SentryUnitySDK.cs +++ b/src/Sentry.Unity/SentryUnitySdk.cs @@ -49,7 +49,11 @@ private SentryUnitySdk(SentryUnityOptions options) } } - unitySdk._dotnetSdk = SentrySdk.Init(options); + unitySdk._dotnetSdk = Sentry.SentrySdk.Init(options); + + // We can safely call this during initialization. If the SDK self-initialized we're right on time. If the SDK + // was initialized manually, the RuntimeOnLoad attributes already triggered, making this call a no-op. + StartupTracingIntegration.StartTracing(); // We can safely call this during initialization. If the SDK self-initialized we're right on time. If the SDK // was initialized manually, the RuntimeOnLoad attributes already triggered, making this call a no-op. @@ -57,7 +61,7 @@ private SentryUnitySdk(SentryUnityOptions options) if (options.NativeContextWriter is { } contextWriter) { - SentrySdk.ConfigureScope((scope) => + Sentry.SentrySdk.CurrentHub.ConfigureScope((scope) => { var task = Task.Run(() => contextWriter.Write(scope)).ContinueWith(t => { @@ -104,18 +108,18 @@ public void Close() } } - public SentryUnity.CrashedLastRun CrashedLastRun() + public SentrySdk.CrashedLastRun CrashedLastRun() { if (_options.CrashedLastRun is null) { _options.DiagnosticLogger?.LogDebug("The SDK does not have a 'CrashedLastRun' set. " + "This might be due to a missing or disabled native integration."); - return SentryUnity.CrashedLastRun.Unknown; + return SentrySdk.CrashedLastRun.Unknown; } return _options.CrashedLastRun.Invoke() - ? SentryUnity.CrashedLastRun.Crashed - : SentryUnity.CrashedLastRun.DidNotCrash; + ? SentrySdk.CrashedLastRun.Crashed + : SentrySdk.CrashedLastRun.DidNotCrash; } public void CaptureFeedback(string message, string? email, string? name, bool addScreenshot) @@ -135,7 +139,6 @@ public void CaptureFeedback(string message, string? email, string? name, bool ad "image/jpeg")) : null; - - SentrySdk.CaptureFeedback(message, email, name, hint: hint); + Sentry.SentrySdk.CurrentHub.CaptureFeedback(message, email, name, hint: hint); } } diff --git a/src/Sentry.Unity/WebGL/SentryWebGL.cs b/src/Sentry.Unity/WebGL/SentryWebGL.cs index c46f68888..a863a23b5 100644 --- a/src/Sentry.Unity/WebGL/SentryWebGL.cs +++ b/src/Sentry.Unity/WebGL/SentryWebGL.cs @@ -1,4 +1,5 @@ using Sentry.Extensibility; +using Sentry.Unity.NativeUtils; using UnityEngine.Analytics; namespace Sentry.Unity.WebGL; diff --git a/src/sentry-dotnet b/src/sentry-dotnet index 997dcae2f..2aa2bb216 160000 --- a/src/sentry-dotnet +++ b/src/sentry-dotnet @@ -1 +1 @@ -Subproject commit 997dcae2fc7c52d0c49c1f412cee329aeb1c8d29 +Subproject commit 2aa2bb21648ef7645c13fc263449de2c326fdd1d diff --git a/test/Scripts.Integration.Test/Scripts/SmokeTester.cs b/test/Scripts.Integration.Test/Scripts/SmokeTester.cs index edd0a4430..16becdfdf 100644 --- a/test/Scripts.Integration.Test/Scripts/SmokeTester.cs +++ b/test/Scripts.Integration.Test/Scripts/SmokeTester.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Sentry; +using Sentry.Unity; using UnityEngine; using Debug = UnityEngine.Debug; diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index ca9d40c11..b432c5ea0 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -79,7 +79,7 @@ public void Arguments() // act MainThreadData.SentrySystemInfo = sysInfo; - SentryUnity.Init(options); + SentrySdk.Init(options); Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); // assert diff --git a/test/Sentry.Unity.Tests/IntegrationTests.cs b/test/Sentry.Unity.Tests/IntegrationTests.cs index 10e7e4924..23f9def4f 100644 --- a/test/Sentry.Unity.Tests/IntegrationTests.cs +++ b/test/Sentry.Unity.Tests/IntegrationTests.cs @@ -34,7 +34,7 @@ public void TearDown() { if (SentrySdk.IsEnabled) { - SentryUnity.Close(); + SentrySdk.Close(); } } @@ -338,7 +338,7 @@ internal static IEnumerator SetupSceneCoroutine(string sceneName, [CallerMemberN internal IDisposable InitSentrySdk(Action? configure = null) { - SentryUnity.Init(options => + SentrySdk.Init(options => { options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; options.CreateHttpMessageHandler = () => _testHttpClientHandler; diff --git a/test/Sentry.Unity.Tests/ScreenshotEventProcessorTests.cs b/test/Sentry.Unity.Tests/ScreenshotEventProcessorTests.cs index fcad4a82a..36252b96e 100644 --- a/test/Sentry.Unity.Tests/ScreenshotEventProcessorTests.cs +++ b/test/Sentry.Unity.Tests/ScreenshotEventProcessorTests.cs @@ -24,7 +24,7 @@ public void TearDown() { if (SentrySdk.IsEnabled) { - SentryUnity.Close(); + SentrySdk.Close(); } } diff --git a/test/Sentry.Unity.Tests/SentryPlatformServicesTests.cs b/test/Sentry.Unity.Tests/SentryPlatformServicesTests.cs new file mode 100644 index 000000000..8957e396c --- /dev/null +++ b/test/Sentry.Unity.Tests/SentryPlatformServicesTests.cs @@ -0,0 +1,20 @@ +using NUnit.Framework; +using Sentry.Unity.NativeUtils; + +namespace Sentry.Unity.Tests; + +[SetUpFixture] +public class SentryPlatformServicesTests +{ + [OneTimeSetUp] + public void OneTimeSetUp() + { + SentryPlatformServices.UnityInfo = new TestUnityInfo(); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + + } +} diff --git a/test/Sentry.Unity.Tests/SentryTests.cs b/test/Sentry.Unity.Tests/SentryTests.cs index d0d8494c1..4761751a3 100644 --- a/test/Sentry.Unity.Tests/SentryTests.cs +++ b/test/Sentry.Unity.Tests/SentryTests.cs @@ -6,7 +6,7 @@ public static class SentryTests { internal static IDisposable InitSentrySdk(Action? configure = null, TestHttpClientHandler? testHttpClientHandler = null) { - SentryUnity.Init(options => + SentrySdk.Init(options => { options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; if (testHttpClientHandler is not null) diff --git a/test/Sentry.Unity.Tests/SentryUnityTests.cs b/test/Sentry.Unity.Tests/SentryUnityTests.cs index 0c1c2f9e9..e3326cad6 100644 --- a/test/Sentry.Unity.Tests/SentryUnityTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityTests.cs @@ -18,7 +18,7 @@ public void TearDown() { if (SentrySdk.IsEnabled) { - SentryUnity.Close(); + SentrySdk.Close(); } } @@ -72,7 +72,7 @@ public void SentryUnity_OptionsValid_Initializes() Dsn = TestDsn }; - SentryUnity.Init(options); + SentrySdk.Init(options); Assert.IsTrue(SentrySdk.IsEnabled); } @@ -83,7 +83,7 @@ public void SentryUnity_OptionsInvalid_DoesNotInitialize() var options = new SentryUnityOptions(); // Even tho the defaults are set the DSN is missing making the options invalid for initialization - SentryUnity.Init(options); + SentrySdk.Init(options); Assert.IsFalse(SentrySdk.IsEnabled); } @@ -99,8 +99,8 @@ public void Init_MultipleTimes_LogsWarning() DiagnosticLogger = testLogger, }; - SentryUnity.Init(options); - SentryUnity.Init(options); + SentrySdk.Init(options); + SentrySdk.Init(options); Assert.IsTrue(testLogger.Logs.Any(log => log.logLevel == SentryLevel.Warning && @@ -111,13 +111,13 @@ public void Init_MultipleTimes_LogsWarning() public void GetLastRunState_WithoutInit_ReturnsUnknown() { // Make sure SDK is closed - SentryUnity.Close(); + SentrySdk.Close(); // Act - var result = SentryUnity.GetLastRunState(); + var result = SentrySdk.GetLastRunState(); // Assert - Assert.AreEqual(SentryUnity.CrashedLastRun.Unknown, result); + Assert.AreEqual(SentrySdk.CrashedLastRun.Unknown, result); } [Test] @@ -131,11 +131,11 @@ public void GetLastRunState_WhenCrashed_ReturnsCrashed() }; // Act - SentryUnity.Init(options); - var result = SentryUnity.GetLastRunState(); + SentrySdk.Init(options); + var result = SentrySdk.GetLastRunState(); // Assert - Assert.AreEqual(SentryUnity.CrashedLastRun.Crashed, result); + Assert.AreEqual(SentrySdk.CrashedLastRun.Crashed, result); } [Test] @@ -149,11 +149,11 @@ public void GetLastRunState_WhenNotCrashed_ReturnsDidNotCrash() }; // Act - SentryUnity.Init(options); - var result = SentryUnity.GetLastRunState(); + SentrySdk.Init(options); + var result = SentrySdk.GetLastRunState(); // Assert - Assert.AreEqual(SentryUnity.CrashedLastRun.DidNotCrash, result); + Assert.AreEqual(SentrySdk.CrashedLastRun.DidNotCrash, result); } [Test] @@ -167,10 +167,10 @@ public void GetLastRunState_WithNullDelegate_ReturnsUnknown() }; // Act - SentryUnity.Init(options); - var result = SentryUnity.GetLastRunState(); + SentrySdk.Init(options); + var result = SentrySdk.GetLastRunState(); // Assert - Assert.AreEqual(SentryUnity.CrashedLastRun.Unknown, result); + Assert.AreEqual(SentrySdk.CrashedLastRun.Unknown, result); } } diff --git a/test/Sentry.Unity.Tests/SessionIntegrationTests.cs b/test/Sentry.Unity.Tests/SessionIntegrationTests.cs index 6fb7aee3f..d53f81698 100644 --- a/test/Sentry.Unity.Tests/SessionIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/SessionIntegrationTests.cs @@ -27,7 +27,7 @@ public IEnumerator SessionIntegration_Init_SentryMonoBehaviourCreated() internal IDisposable InitSentrySdk(Action? configure = null) { - SentryUnity.Init(options => + SentrySdk.Init(options => { options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; configure?.Invoke(options); diff --git a/test/Sentry.Unity.Tests/StartupTracingIntegrationTests.cs b/test/Sentry.Unity.Tests/StartupTracingIntegrationTests.cs index 403b7d8e9..d72cdeafb 100644 --- a/test/Sentry.Unity.Tests/StartupTracingIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/StartupTracingIntegrationTests.cs @@ -173,7 +173,7 @@ public void StartTracing_WhenNotAllowed_DoesNotCreateTransaction() [Test] public void StartupSequence_CallsInOrder_CreatesAndFinishesTransactionCorrectly() { - SentrySdk.UseHub(_fixture.Hub); + Sentry.SentrySdk.UseHub(_fixture.Hub); _fixture.Application.IsEditor = false; _fixture.Application.Platform = RuntimePlatform.WindowsPlayer; StartupTracingIntegration.Application = _fixture.Application; diff --git a/test/Sentry.Unity.Tests/TraceGenerationIntegrationTests.cs b/test/Sentry.Unity.Tests/TraceGenerationIntegrationTests.cs index 751c35d5a..91189d44e 100644 --- a/test/Sentry.Unity.Tests/TraceGenerationIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/TraceGenerationIntegrationTests.cs @@ -29,7 +29,7 @@ private class Fixture public void SetUp() { _fixture.TestHub = new TestHub(); - SentrySdk.UseHub(_fixture.TestHub); + Sentry.SentrySdk.UseHub(_fixture.TestHub); } [Test] diff --git a/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs b/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs index bfab5d235..8ec055f79 100644 --- a/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs +++ b/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs @@ -26,7 +26,7 @@ public void Filter_FiltersBadGatewayExceptionsOfTypeException() => internal IDisposable InitSentrySdk(Action? configure = null) { - SentryUnity.Init(options => + SentrySdk.Init(options => { options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; options.CreateHttpMessageHandler = () => _testHttpClientHandler; diff --git a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs index 303bcace5..2085e1682 100644 --- a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs +++ b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs @@ -37,7 +37,7 @@ public void TearDown() if (SentrySdk.IsEnabled) { - SentryUnity.Close(); + SentrySdk.Close(); } } @@ -66,7 +66,7 @@ public void SentrySdkCaptureEvent_OnNotUIThread_Succeeds() Debug = true, DiagnosticLogger = _testLogger }; - SentryUnity.Init(options); + SentrySdk.Init(options); var sentryEvent = new SentryEvent { @@ -82,7 +82,11 @@ public void SentrySdkCaptureEvent_OnNotUIThread_Succeeds() SentrySdk.FlushAsync(TimeSpan.FromSeconds(1)).GetAwaiter().GetResult(); // assert - var logsFound = _testLogger.Logs.Where(log => log.logLevel >= SentryLevel.Warning && log.message != "Cache directory is empty.").ToList(); + var logsFound = _testLogger.Logs.Where(log => + log.logLevel >= SentryLevel.Warning && + // Ignore expected or harmless warnings + log.message != "Cache directory is empty." && + log.message != "The SDK's Platform Services have not been set up. Native support will be limited.").ToList(); Assert.Zero(logsFound.Count, FormatLogs(logsFound)); @@ -123,7 +127,7 @@ public void SentrySdkCaptureEvent(bool captureOnUiThread) MainThreadData.SentrySystemInfo = systemInfo; MainThreadData.CollectData(); - SentryUnity.Init(options); + SentrySdk.Init(options); // Act var @event = new SentryEvent diff --git a/test/Sentry.Unity.Tests/ViewHierarchyEventProcessorTests.cs b/test/Sentry.Unity.Tests/ViewHierarchyEventProcessorTests.cs index 85adba0b8..93ec9dfc4 100644 --- a/test/Sentry.Unity.Tests/ViewHierarchyEventProcessorTests.cs +++ b/test/Sentry.Unity.Tests/ViewHierarchyEventProcessorTests.cs @@ -26,7 +26,7 @@ public void TearDown() { if (SentrySdk.IsEnabled) { - SentryUnity.Close(); + SentrySdk.Close(); } } diff --git a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs index 133cef2ab..c79c78753 100644 --- a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs +++ b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs @@ -1,20 +1,30 @@ using System; using NUnit.Framework; +using Sentry.Unity.NativeUtils; +using Sentry.Unity.Tests.Stubs; using UnityEngine; namespace Sentry.Unity.iOS.Tests; public class SentryNativeCocoaTests { + [TearDown] + public void TearDown() + { + SentryPlatformServices.UnityInfo = null; + } + [Test] public void Configure_DefaultConfiguration_iOS() { - var unityInfo = new TestUnityInfo { IL2CPP = false }; + SentryPlatformServices.UnityInfo = new TestUnityInfo { IL2CPP = false }; + ; var options = new SentryUnityOptions(); + // Note: can't test iOS - throws because it tries to call SentryCocoaBridgeProxy.Init() // but the bridge isn't loaded now... Assert.Throws(() => - SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.IPhonePlayer)); + SentryNativeCocoa.Configure(options, RuntimePlatform.IPhonePlayer)); } [Test] @@ -22,7 +32,7 @@ public void Configure_NativeSupportDisabled_iOS() { var unityInfo = new TestUnityInfo(true, false, false) { IL2CPP = false }; var options = new SentryUnityOptions { IosNativeSupportEnabled = false }; - SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.IPhonePlayer); + SentryNativeCocoa.Configure(options, RuntimePlatform.IPhonePlayer); Assert.Null(options.ScopeObserver); Assert.Null(options.CrashedLastRun); Assert.False(options.EnableScopeSync);