public AndroidManifestConfiguration()
     : this(() => ScriptableSentryUnityOptions.LoadSentryUnityOptions(BuildPipeline.isBuildingPlayer),
            () => SentryCliOptions.LoadCliOptions(),
            isDevelopmentBuild : EditorUserBuildSettings.development,
            scriptingImplementation : PlayerSettings.GetScriptingBackend(BuildTargetGroup.Android))
 {
 }
예제 #2
0
        public IEnumerator BugFarmScene_MultipleSentryInit_SendEventForTheLatest()
        {
            yield return(SetupSceneCoroutine("1_BugFarm"));

            // We should use the sample Dsn for the nextDsn
            // to avoid static dsn.
            var options = AssetDatabase.LoadAssetAtPath(ScriptableSentryUnityOptions.GetConfigPath(ScriptableSentryUnityOptions.ConfigName),
                                                        typeof(ScriptableSentryUnityOptions)) as ScriptableSentryUnityOptions;

            var sourceEventCapture = new TestEventCapture();
            var sourceDsn          = "https://[email protected]/5439417";

            using var firstDisposable = InitSentrySdk(o =>
            {
                o.Dsn = sourceDsn;
                o.AddIntegration(new UnityApplicationLoggingIntegration(eventCapture: sourceEventCapture));
            });

            var nextEventCapture = new TestEventCapture();
            var nextDsn          = options?.Dsn;

            using var secondDisposable = InitSentrySdk(o =>
            {
                o.Dsn = nextDsn;
                o.AddIntegration(new UnityApplicationLoggingIntegration(eventCapture: nextEventCapture));
            });
            var testBehaviour = new GameObject("TestHolder").AddComponent <TestMonoBehaviour>();

            testBehaviour.gameObject.SendMessage(nameof(testBehaviour.TestException));

            Assert.NotNull(nextDsn);
            Assert.AreEqual(0, sourceEventCapture.Events.Count, sourceDsn);
            Assert.AreEqual(1, nextEventCapture.Events.Count);
        }
        internal static void ToScriptableOptions(TextAsset sentryOptionsTextAsset, ScriptableSentryUnityOptions scriptableOptions)
        {
            var jsonOptions = LoadFromJson(sentryOptionsTextAsset);

            scriptableOptions.Enabled = jsonOptions.Enabled;

            if (jsonOptions.Dsn is { } dsn)
            {
                scriptableOptions.Dsn = dsn;
            }

            scriptableOptions.CaptureInEditor   = jsonOptions.CaptureInEditor;
            scriptableOptions.Debug             = jsonOptions.Debug;
            scriptableOptions.DebugOnlyInEditor = jsonOptions.DebugOnlyInEditor;
            scriptableOptions.DiagnosticLevel   = jsonOptions.DiagnosticLevel;
            scriptableOptions.AttachStacktrace  = jsonOptions.AttachStacktrace;

            if (jsonOptions.SampleRate is { } sampleRate)
            {
                scriptableOptions.SampleRate = sampleRate;
            }

            if (jsonOptions.Release is { } release)
            {
                scriptableOptions.ReleaseOverride = release;
            }

            if (jsonOptions.Environment is { } environment)
            {
                scriptableOptions.EnvironmentOverride = environment;
            }
        }
예제 #4
0
        internal static void Display(ScriptableSentryUnityOptions options)
        {
            GUILayout.Label("Base Options", EditorStyles.boldLabel);

            options.Dsn = EditorGUILayout.TextField(
                new GUIContent("DSN", "The URL to your Sentry project. " +
                               "Get yours on sentry.io -> Project Settings."),
                options.Dsn)?.Trim();

            options.CaptureInEditor = EditorGUILayout.Toggle(
                new GUIContent("Capture In Editor", "Capture errors while running in the Editor."),
                options.CaptureInEditor);

            options.EnableLogDebouncing = EditorGUILayout.Toggle(
                new GUIContent("Enable Log Debouncing", "The SDK debounces log messages of the same type if " +
                               "they are more frequent than once per second."),
                options.EnableLogDebouncing);

            EditorGUILayout.Space();
            EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
            EditorGUILayout.Space();

            GUILayout.Label("Tracing - Performance Monitoring", EditorStyles.boldLabel);

            options.TracesSampleRate = EditorGUILayout.Slider(
                new GUIContent("Traces Sample Rate", "Indicates the percentage of transactions that are " +
                               "captured. Setting this to 0 discards all trace data. " +
                               "Setting this to 1.0 captures all."),
                (float)options.TracesSampleRate, 0.0f, 1.0f);
        }
예제 #5
0
        public void ToSentryUnityOptions_ValueMapping_AreEqual(bool isBuilding, bool enableOfflineCaching)
        {
            var expectedOptions = new SentryUnityOptions
            {
                Enabled                     = false,
                Dsn                         = "test",
                CaptureInEditor             = false,
                EnableLogDebouncing         = true,
                TracesSampleRate            = 1.0f,
                AutoSessionTracking         = false,
                AutoSessionTrackingInterval = TimeSpan.FromSeconds(1),
                AttachStacktrace            = true,
                MaxBreadcrumbs              = 1,
                ReportAssembliesMode        = ReportAssembliesMode.None,
                SendDefaultPii              = true,
                IsEnvironmentUser           = true,
                MaxCacheItems               = 1,
                CacheDirectoryPath          = enableOfflineCaching ? _fixture.Application.PersistentDataPath : null,
                InitCacheFlushTimeout       = TimeSpan.FromSeconds(1),
                SampleRate                  = 0.5f,
                ShutdownTimeout             = TimeSpan.FromSeconds(1),
                MaxQueueItems               = 1,
                Release                     = "testRelease",
                Environment                 = "testEnvironment",
                Debug                       = true,
                DebugOnlyInEditor           = true,
                DiagnosticLevel             = SentryLevel.Info,
            };

            var scriptableOptions = ScriptableObject.CreateInstance <ScriptableSentryUnityOptions>();

            scriptableOptions.Enabled                     = expectedOptions.Enabled;
            scriptableOptions.Dsn                         = expectedOptions.Dsn;
            scriptableOptions.CaptureInEditor             = expectedOptions.CaptureInEditor;
            scriptableOptions.EnableLogDebouncing         = expectedOptions.EnableLogDebouncing;
            scriptableOptions.TracesSampleRate            = expectedOptions.TracesSampleRate;
            scriptableOptions.AutoSessionTracking         = expectedOptions.AutoSessionTracking;
            scriptableOptions.AutoSessionTrackingInterval = (int)expectedOptions.AutoSessionTrackingInterval.TotalMilliseconds;
            scriptableOptions.AttachStacktrace            = expectedOptions.AttachStacktrace;
            scriptableOptions.MaxBreadcrumbs              = expectedOptions.MaxBreadcrumbs;
            scriptableOptions.ReportAssembliesMode        = expectedOptions.ReportAssembliesMode;
            scriptableOptions.SendDefaultPii              = expectedOptions.SendDefaultPii;
            scriptableOptions.IsEnvironmentUser           = expectedOptions.IsEnvironmentUser;
            scriptableOptions.MaxCacheItems               = expectedOptions.MaxCacheItems;
            scriptableOptions.EnableOfflineCaching        = enableOfflineCaching;
            scriptableOptions.InitCacheFlushTimeout       = (int)expectedOptions.InitCacheFlushTimeout.TotalMilliseconds;
            scriptableOptions.SampleRate                  = expectedOptions.SampleRate;
            scriptableOptions.ShutdownTimeout             = (int)expectedOptions.ShutdownTimeout.TotalMilliseconds;
            scriptableOptions.MaxQueueItems               = expectedOptions.MaxQueueItems;
            scriptableOptions.ReleaseOverride             = expectedOptions.Release;
            scriptableOptions.EnvironmentOverride         = expectedOptions.Environment;
            scriptableOptions.Debug                       = expectedOptions.Debug;
            scriptableOptions.DebugOnlyInEditor           = expectedOptions.DebugOnlyInEditor;
            scriptableOptions.DiagnosticLevel             = expectedOptions.DiagnosticLevel;

            var optionsActual = ScriptableSentryUnityOptions.ToSentryUnityOptions(scriptableOptions, isBuilding, _fixture.Application);

            AssertOptions(expectedOptions, optionsActual);
        }
예제 #6
0
        internal static void Display(ScriptableSentryUnityOptions options)
        {
            options.Debug = EditorGUILayout.BeginToggleGroup(
                new GUIContent("Enable Debug Output", "Whether the Sentry SDK should print its " +
                               "diagnostic logs to the console."),
                options.Debug);

            options.DebugOnlyInEditor = EditorGUILayout.Toggle(
                new GUIContent("Only In Editor", "Only print logs when in the editor. Development " +
                               "builds of the player will not include Sentry's SDK diagnostics."),
                options.DebugOnlyInEditor);

            options.DiagnosticLevel = (SentryLevel)EditorGUILayout.EnumPopup(
                new GUIContent("Verbosity Level", "The minimum level allowed to be printed to the console. " +
                               "Log messages with a level below this level are dropped."),
                options.DiagnosticLevel);

            EditorGUILayout.EndToggleGroup();

            EditorGUILayout.Space();
            EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
            EditorGUILayout.Space();

            options.AutoSessionTracking = EditorGUILayout.BeginToggleGroup(
                new GUIContent("Auto Session Tracking", "Whether the SDK should start and end sessions " +
                               "automatically. If the timeout is reached the old session will" +
                               "be ended and a new one started."),
                options.AutoSessionTracking);

            options.AutoSessionTrackingInterval = EditorGUILayout.IntField(
                new GUIContent("Session Timeout [ms]", "The duration of time a session can stay paused " +
                               "(i.e. the application has been put in the background) before " +
                               "it is considered ended."),
                options.AutoSessionTrackingInterval);
            options.AutoSessionTrackingInterval = Mathf.Max(0, options.AutoSessionTrackingInterval);
            EditorGUILayout.EndToggleGroup();

            EditorGUILayout.Space();
            EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
            EditorGUILayout.Space();

            GUILayout.Label("Native Support", EditorStyles.boldLabel);

            options.IosNativeSupportEnabled = EditorGUILayout.Toggle(
                new GUIContent("iOS Native Support", "Whether to enable Native iOS support to capture" +
                               "errors written in languages such as Objective-C, Swift, C and C++."),
                options.IosNativeSupportEnabled);

            options.AndroidNativeSupportEnabled = EditorGUILayout.Toggle(
                new GUIContent("Android Native Support", "Whether to enable Native Android support to " +
                               "capture errors written in languages such as Java, Kotlin, C and C++."),
                options.AndroidNativeSupportEnabled);

            options.WindowsNativeSupportEnabled = EditorGUILayout.Toggle(
                new GUIContent("Windows Native Support", "Whether to enable Native Windows support to " +
                               "capture errors written in languages such as C and C++."),
                options.WindowsNativeSupportEnabled);
        }
예제 #7
0
        internal static void Display(ScriptableSentryUnityOptions options)
        {
            options.EnableOfflineCaching = EditorGUILayout.BeginToggleGroup(
                new GUIContent("Enable Offline Caching", ""),
                options.EnableOfflineCaching);

            options.MaxCacheItems = EditorGUILayout.IntField(
                new GUIContent("Max Cache Items", "The maximum number of files to keep in the disk cache. " +
                               "The SDK deletes the oldest when the limit is reached.\nDefault: 30"),
                options.MaxCacheItems);
            options.MaxCacheItems = Math.Max(0, options.MaxCacheItems);

            options.InitCacheFlushTimeout = EditorGUILayout.IntField(
                new GUIContent("Init Flush Timeout [ms]", "The timeout that limits how long the SDK " +
                               "will attempt to flush existing cache during initialization, " +
                               "potentially slowing down app start up to the specified time." +
                               "\nThis features allows capturing errors that happen during " +
                               "game startup and would not be captured because the process " +
                               "would be killed before Sentry had a chance to capture the event."),
                options.InitCacheFlushTimeout);
            options.InitCacheFlushTimeout = Math.Max(0, options.InitCacheFlushTimeout);

            EditorGUILayout.EndToggleGroup();

            EditorGUILayout.Space();
            EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
            EditorGUILayout.Space();

            // Options.RequestBodyCompressionLevel = (CompressionLevelWithAuto)EditorGUILayout.EnumPopup(
            //     new GUIContent("Compress Payload", "The level of which to compress the Sentry event " +
            //                                        "before sending to Sentry."),
            //     Options.RequestBodyCompressionLevel);

            var sampleRate = options.SampleRate ??= 1.0f;

            sampleRate = EditorGUILayout.Slider(
                new GUIContent("Event Sample Rate", "Indicates the percentage of events that are " +
                               "captured. Setting this to 0.1 captures 10% of events. " +
                               "Setting this to 1.0 captures all events." +
                               "\nThis affects only errors and logs, not performance " +
                               "(transactions) data. See TraceSampleRate for that."),
                sampleRate, 0.01f, 1);
            options.SampleRate = (sampleRate < 1.0f) ? sampleRate : null;

            options.ShutdownTimeout = EditorGUILayout.IntField(
                new GUIContent("Shut Down Timeout [ms]", "How many seconds to wait before shutting down to " +
                               "give Sentry time to send events from the background queue."),
                options.ShutdownTimeout);
            options.ShutdownTimeout = Mathf.Clamp(options.ShutdownTimeout, 0, int.MaxValue);

            options.MaxQueueItems = EditorGUILayout.IntField(
                new GUIContent("Max Queue Items", "The maximum number of events to keep in memory while " +
                               "the worker attempts to send them."),
                options.MaxQueueItems
                );
            options.MaxQueueItems = Math.Max(0, options.MaxQueueItems);
        }
예제 #8
0
        public void ToSentryUnityOptions_HasOptionsConfiguration_GetsCalled(bool isBuilding)
        {
            var optionsConfiguration = ScriptableObject.CreateInstance <TestOptionsConfiguration>();
            var scriptableOptions    = ScriptableObject.CreateInstance <ScriptableSentryUnityOptions>();

            scriptableOptions.OptionsConfiguration = optionsConfiguration;

            ScriptableSentryUnityOptions.ToSentryUnityOptions(scriptableOptions, isBuilding);

            Assert.IsTrue(optionsConfiguration.GotCalled);
        }
예제 #9
0
        public static void OnPostProcessBuild(BuildTarget target, string pathToProject)
        {
            if (target != BuildTarget.iOS)
            {
                return;
            }

            var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(BuildPipeline.isBuildingPlayer);
            var logger  = options?.DiagnosticLogger ?? new UnityLogger(new SentryUnityOptions());

            try
            {
                // Unity doesn't allow an appending builds when switching iOS SDK versions and this will make sure we always copy the correct version of the Sentry.framework
                var frameworkDirectory = PlayerSettings.iOS.sdkVersion == iOSSdkVersion.DeviceSDK ? "Device" : "Simulator";
                var frameworkPath      = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", frameworkDirectory, "Sentry.framework"));
                CopyFramework(frameworkPath, Path.Combine(pathToProject, "Frameworks", "Sentry.framework"), options?.DiagnosticLogger);

                var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", "SentryNativeBridge.m"));
                CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), "SentryNativeBridge.m"), options?.DiagnosticLogger);

                using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject);
                sentryXcodeProject.AddSentryFramework();
                sentryXcodeProject.AddSentryNativeBridge();

                if (options?.IsValid() is not true)
                {
                    logger.LogWarning("Failed to validate Sentry Options. Native support disabled.");
                    return;
                }

                if (!options.IosNativeSupportEnabled)
                {
                    logger.LogDebug("iOS Native support disabled through the options.");
                    return;
                }

                sentryXcodeProject.AddNativeOptions(options);
                sentryXcodeProject.AddSentryToMain(options);

                var sentryCliOptions = SentryCliOptions.LoadCliOptions();
                if (sentryCliOptions.IsValid(logger))
                {
                    SentryCli.CreateSentryProperties(pathToProject, sentryCliOptions);
                    SentryCli.AddExecutableToXcodeProject(pathToProject, logger);
                    sentryXcodeProject.AddBuildPhaseSymbolUpload(logger);
                }
            }
            catch (Exception e)
            {
                logger.LogError("Failed to add the Sentry framework to the generated Xcode project", e);
            }
        }
예제 #10
0
        private ScriptableSentryUnityOptions LoadOptions()
        {
            var options = AssetDatabase.LoadAssetAtPath(
                ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName),
                typeof(ScriptableSentryUnityOptions)) as ScriptableSentryUnityOptions;

            if (options is null)
            {
                options = CreateOptions(SentryOptionsAssetName);
            }

            return(options);
        }
예제 #11
0
        public void ToScriptableOptions_ConvertJsonOptions_AreEqual(bool isBuilding)
        {
            var jsonTextAsset   = new TextAsset(File.ReadAllText(GetTestOptionsFilePath()));
            var expectedOptions = JsonSentryUnityOptions.LoadFromJson(jsonTextAsset);

            var scriptableOptions = ScriptableObject.CreateInstance <ScriptableSentryUnityOptions>();

            SentryOptionsUtility.SetDefaults(scriptableOptions);
            JsonSentryUnityOptions.ToScriptableOptions(jsonTextAsset, scriptableOptions);

            var actualOptions = ScriptableSentryUnityOptions.ToSentryUnityOptions(scriptableOptions, isBuilding);

            AssertOptions(expectedOptions, actualOptions);
        }
예제 #12
0
        public void Setup()
        {
            var options = AssetDatabase.LoadAssetAtPath(ScriptableSentryUnityOptions.GetConfigPath(ScriptableSentryUnityOptions.ConfigName),
                                                        typeof(ScriptableSentryUnityOptions)) as ScriptableSentryUnityOptions;

            if (options?.Enabled != true)
            {
                return;
            }

            Debug.Log("Disabling local options for the duration of the test.");
            _optionsToRestore         = options;
            _optionsToRestore.Enabled = false;
        }
 public static void Display(ScriptableSentryUnityOptions options)
 {
     GUILayout.BeginHorizontal();
     options.OptionsConfiguration = EditorGUILayout.ObjectField(
         new GUIContent(".NET (C#)", "A scriptable object that inherits from " +
                        "'ScriptableOptionsConfiguration' and allows you to " +
                        "programmatically modify Sentry options."),
         options.OptionsConfiguration, typeof(ScriptableOptionsConfiguration), false)
                                    as ScriptableOptionsConfiguration;
     if (GUILayout.Button("New", GUILayout.ExpandWidth(false)))
     {
         CreateScript();
     }
     GUILayout.EndHorizontal();
 }
예제 #14
0
        private void OnLostFocus()
        {
            // Make sure the actual config asset exists before validating/saving. Crashes the editor otherwise.
            if (!File.Exists(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName)))
            {
                new UnityLogger(new SentryOptions()).LogWarning("Sentry option could not been saved. " +
                                                                "The configuration asset is missing.");
                return;
            }

            Validate();

            EditorUtility.SetDirty(Options);
            EditorUtility.SetDirty(CliOptions);
            AssetDatabase.SaveAssets();
        }
예제 #15
0
        public void ToSentryOptions_OptionsCreated_AreEqualToNewOptions(bool isBuilding)
        {
            var expectedOptions = new SentryUnityOptions(_fixture.Application, isBuilding);

            var scriptableOptions = ScriptableObject.CreateInstance <ScriptableSentryUnityOptions>();

            SentryOptionsUtility.SetDefaults(scriptableOptions);

            // These are config window specific differences in default values we actually want
            scriptableOptions.Debug             = false;
            scriptableOptions.DebugOnlyInEditor = false;
            scriptableOptions.DiagnosticLevel   = SentryLevel.Debug;

            var actualOptions = ScriptableSentryUnityOptions.ToSentryUnityOptions(scriptableOptions, isBuilding, _fixture.Application);

            AssertOptions(expectedOptions, actualOptions);
        }
        public static void Display(ScriptableSentryUnityOptions options)
        {
            GUILayout.Label("Programmatic Options Configuration", EditorStyles.boldLabel);

            EditorGUILayout.Space();

            EditorGUILayout.HelpBox("The options configuration allows you to programmatically modify " +
                                    "the Sentry options object during runtime initialization of the SDK. " +
                                    "This allows you to override configuration otherwise unavailable from the " +
                                    "editor UI, e.g. set a custom BeforeSend callback. \n\n" +
                                    // TODO other platforms
                                    // "Because Sentry Unity integration includes both managed C# Unity SDK and a " +
                                    // "platform specific one, you can specify the respective overrides separately.\n\n" +
                                    "You can either select an existing script, or create a new one by clicking the " +
                                    "'New' button, which will create one from a template at a selected location.",
                                    MessageType.Info);

            EditorGUILayout.Space();

            OptionsConfigurationDotNet.Display(options);
        }
예제 #17
0
        public static void OnPostProcessBuild(BuildTarget target, string executablePath)
        {
            if (target is not(BuildTarget.StandaloneWindows or BuildTarget.StandaloneWindows64))
            {
                return;
            }

            var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(BuildPipeline.isBuildingPlayer);
            var logger  = options?.DiagnosticLogger ?? new UnityLogger(options ?? new SentryUnityOptions());

            try
            {
                if (PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup) != ScriptingImplementation.IL2CPP)
                {
                    logger.LogWarning("Failed to enable Native support - only availabile with IL2CPP scripting backend.");
                    return;
                }

                if (options?.IsValid() is not true)
                {
                    logger.LogWarning("Failed to validate Sentry Options. Native support disabled.");
                    return;
                }

                if (!options.WindowsNativeSupportEnabled)
                {
                    logger.LogDebug("Windows Native support disabled through the options.");
                    return;
                }

                var projectDir = Path.GetDirectoryName(executablePath);
                AddCrashHandler(logger, projectDir);
                UploadDebugSymbols(logger, projectDir, Path.GetFileName(executablePath));
            }
            catch (Exception e)
            {
                logger.LogError("Failed to add the Sentry native integration to the built application", e);
                throw new BuildFailedException("Sentry Native BuildPostProcess failed");
            }
        }
예제 #18
0
        internal static ScriptableSentryUnityOptions CreateOptions(string?notDefaultConfigName = null)
        {
            if (!AssetDatabase.IsValidFolder("Assets/Resources"))
            {
                AssetDatabase.CreateFolder("Assets", "Resources");
            }

            if (!AssetDatabase.IsValidFolder($"Assets/Resources/{ScriptableSentryUnityOptions.ConfigRootFolder}"))
            {
                AssetDatabase.CreateFolder("Assets/Resources", ScriptableSentryUnityOptions.ConfigRootFolder);
            }

            var scriptableOptions = CreateInstance <ScriptableSentryUnityOptions>();

            SentryOptionsUtility.SetDefaults(scriptableOptions);

            AssetDatabase.CreateAsset(scriptableOptions,
                                      ScriptableSentryUnityOptions.GetConfigPath(notDefaultConfigName));
            AssetDatabase.SaveAssets();

            return(scriptableOptions);
        }
예제 #19
0
 public void Dispose()
 {
     Close(); // calls 'OnLostFocus' implicitly
     AssetDatabase.DeleteAsset(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName));
 }
예제 #20
0
        internal static void Display(ScriptableSentryUnityOptions options)
        {
            GUILayout.Label("Tag Overrides", EditorStyles.boldLabel);

            options.ReleaseOverride = EditorGUILayout.TextField(
                new GUIContent("Override Release", "By default release is built from " +
                               "'Application.productName'@'Application.version'. " +
                               "This option is an override."),
                options.ReleaseOverride);

            options.EnvironmentOverride = EditorGUILayout.TextField(
                new GUIContent("Override Environment", "Auto detects 'production' or 'editor' by " +
                               "default based on 'Application.isEditor." +
                               "\nThis option is an override."),
                options.EnvironmentOverride);

            EditorGUILayout.Space();
            EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
            EditorGUILayout.Space();

            GUILayout.Label("Stacktrace", EditorStyles.boldLabel);

            options.AttachStacktrace = EditorGUILayout.Toggle(
                new GUIContent("Stacktrace For Logs", "Whether to include a stack trace for non " +
                               "error events like logs. Even when Unity didn't include and no " +
                               "exception was thrown. Refer to AttachStacktrace on sentry docs."),
                options.AttachStacktrace);

            // Enhanced not supported on IL2CPP so not displaying this for the time being:
            // Options.StackTraceMode = (StackTraceMode) EditorGUILayout.EnumPopup(
            //     new GUIContent("Stacktrace Mode", "Enhanced is the default." +
            //                                       "\n - Enhanced: Include async, return type, args,..." +
            //                                       "\n - Original - Default .NET stack trace format."),
            //     Options.StackTraceMode);

            EditorGUILayout.Space();
            EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
            EditorGUILayout.Space();

            options.SendDefaultPii = EditorGUILayout.BeginToggleGroup(
                new GUIContent("Send default Pii", "Whether to include default Personal Identifiable " +
                               "Information."),
                options.SendDefaultPii);

            options.IsEnvironmentUser = EditorGUILayout.Toggle(
                new GUIContent("Auto Set UserName", "Whether to report the 'Environment.UserName' as " +
                               "the User affected in the event. Should be disabled for " +
                               "Android and iOS."),
                options.IsEnvironmentUser);

            EditorGUILayout.EndToggleGroup();

            EditorGUILayout.Space();
            EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
            EditorGUILayout.Space();

            options.MaxBreadcrumbs = EditorGUILayout.IntField(
                new GUIContent("Max Breadcrumbs", "Maximum number of breadcrumbs that get captured." +
                               "\nDefault: 100"),
                options.MaxBreadcrumbs);
            options.MaxBreadcrumbs = Math.Max(0, options.MaxBreadcrumbs);

            options.ReportAssembliesMode = (ReportAssembliesMode)EditorGUILayout.EnumPopup(
                new GUIContent("Report Assemblies Mode", "Whether or not to include referenced assemblies " +
                               "Version or InformationalVersion in each event sent to sentry."),
                options.ReportAssembliesMode);
        }