From 910a9a30d9eb4bfc33aff7d31d8be59fb60c5415 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Thu, 25 Sep 2025 11:11:06 +0200 Subject: [PATCH 1/8] Added summariy to keep my sanity --- .../Integrations/UnityApplicationLoggingIntegration.cs | 7 +++++-- .../Integrations/UnityLogHandlerIntegration.cs | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs b/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs index 11c4ac0f7..404e6f0f3 100644 --- a/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs @@ -1,10 +1,13 @@ -using Sentry.Extensibility; using Sentry.Integrations; -using Sentry.Protocol; using UnityEngine; namespace Sentry.Unity.Integrations; +/// +/// Hooks into Unity's `Application.LogMessageReceived` to capture breadcrumbs for Debug log methods +/// and optionally capture LogError events. Does not handle `Debug.LogException` since it lacks the +/// actual exception object needed for IL2CPP processing, except on WebGL where it's treated as a log message. +/// internal class UnityApplicationLoggingIntegration : ISdkIntegration { private readonly IApplication _application; diff --git a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs index 79a610685..93953f3f7 100644 --- a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs @@ -6,10 +6,12 @@ namespace Sentry.Unity.Integrations; +/// +/// Intercepts Unity's log handler to capture `Debug.LogException` calls with actual exception objects. +/// Other log types are handled by ApplicationLoggingIntegration. +/// internal sealed class UnityLogHandlerIntegration : ISdkIntegration, ILogHandler { - - private readonly IApplication _application; private IHub? _hub; From 2b81c995d541da7b0c9870dcad707d1a3997039a Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Thu, 25 Sep 2025 18:58:49 +0200 Subject: [PATCH 2/8] Removed LogType.Exception from optionally being added as breadcrumb --- src/Sentry.Unity.Editor/ConfigurationWindow/LoggingTab.cs | 3 --- src/Sentry.Unity/Integrations/UnityErrorLogException.cs | 1 - src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs | 6 ------ src/Sentry.Unity/ScriptableSentryUnityOptions.cs | 2 -- src/Sentry.Unity/SentryUnityOptions.cs | 1 - 5 files changed, 13 deletions(-) diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/LoggingTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/LoggingTab.cs index be81ad3af..da13dd041 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/LoggingTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/LoggingTab.cs @@ -66,9 +66,6 @@ internal static void Display(ScriptableSentryUnityOptions options) options.BreadcrumbsForErrors = EditorGUILayout.Toggle( new GUIContent("Debug.Error", "Whether the SDK automatically adds breadcrumbs for 'Debug.LogError'."), options.BreadcrumbsForErrors); - options.BreadcrumbsForExceptions = EditorGUILayout.Toggle( - new GUIContent("Debug.Exception", "Whether the SDK automatically adds breadcrumbs for exceptions and 'Debug.LogException'."), - options.BreadcrumbsForExceptions); EditorGUI.indentLevel--; } diff --git a/src/Sentry.Unity/Integrations/UnityErrorLogException.cs b/src/Sentry.Unity/Integrations/UnityErrorLogException.cs index c62d966a0..a803bf585 100644 --- a/src/Sentry.Unity/Integrations/UnityErrorLogException.cs +++ b/src/Sentry.Unity/Integrations/UnityErrorLogException.cs @@ -49,7 +49,6 @@ public SentryException ToSentryException() return new SentryException { Stacktrace = stacktrace, - Type = ExceptionType, Value = _logString, Mechanism = new Mechanism { diff --git a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs index 93953f3f7..b93b15a1a 100644 --- a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs @@ -75,12 +75,6 @@ internal void CaptureException(Exception exception, UnityEngine.Object? context) exception.Data[Mechanism.HandledKey] = false; exception.Data[Mechanism.MechanismKey] = "Unity.LogException"; _ = _hub.CaptureException(exception); - - if (_sentryOptions?.AddBreadcrumbsForLogType[LogType.Exception] is true) - { - // So the next event includes this error as a breadcrumb - _hub.AddBreadcrumb(message: $"{exception.GetType()}: {exception.Message}", category: "unity.logger", level: BreadcrumbLevel.Error); - } } public void LogFormat(LogType logType, UnityEngine.Object? context, string format, params object[] args) diff --git a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs index 0f8cf340b..ccd7d4aa5 100644 --- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs +++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs @@ -63,7 +63,6 @@ public static string GetConfigPath(string? notDefaultConfigName = null) [field: SerializeField] public bool BreadcrumbsForWarnings { get; set; } = true; [field: SerializeField] public bool BreadcrumbsForAsserts { get; set; } = true; [field: SerializeField] public bool BreadcrumbsForErrors { get; set; } = true; - [field: SerializeField] public bool BreadcrumbsForExceptions { get; set; } = true; [field: SerializeField] public bool CaptureLogErrorEvents { get; set; } = true; [field: SerializeField] public int MaxBreadcrumbs { get; set; } = SentryConstants.DefaultMaxBreadcrumbs; @@ -210,7 +209,6 @@ internal SentryUnityOptions ToSentryUnityOptions( options.AddBreadcrumbsForLogType[LogType.Warning] = BreadcrumbsForWarnings; options.AddBreadcrumbsForLogType[LogType.Assert] = BreadcrumbsForAsserts; options.AddBreadcrumbsForLogType[LogType.Error] = BreadcrumbsForErrors; - options.AddBreadcrumbsForLogType[LogType.Exception] = BreadcrumbsForExceptions; options.FailedRequestStatusCodes = new List(); for (var i = 0; i < FailedRequestStatusCodes.Count; i += 2) diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index 69954a83d..59491830b 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -386,7 +386,6 @@ internal SentryUnityOptions(IApplication? application = null, { LogType.Warning, true}, { LogType.Assert, true}, { LogType.Error, true}, - { LogType.Exception, true}, }; // Only assign the cache directory path if we're on a "known" platform. Accessing `Application.persistentDataPath` From c2cedcc54d2fc0f354e2d47bc8a3877eee2957b6 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 26 Sep 2025 12:21:41 +0200 Subject: [PATCH 3/8] Testing and early bailing --- .../UnityApplicationLoggingIntegration.cs | 6 +++- .../Integrations/UnityErrorLogException.cs | 1 + .../UnityLogHandlerIntegrationTests.cs | 36 ------------------- 3 files changed, 6 insertions(+), 37 deletions(-) diff --git a/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs b/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs index 404e6f0f3..52143fe65 100644 --- a/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs @@ -83,8 +83,12 @@ internal void OnLogMessageReceived(string message, string stacktrace, LogType lo { var ule = new UnityErrorLogException(message, stacktrace, _options); _hub.CaptureException(ule); + + // We don't capture breadcrumbs for exceptions - the .NET SDK handles this + return; } - else if (logType is LogType.Error && _options?.CaptureLogErrorEvents is true) + + if (logType is LogType.Error && _options?.CaptureLogErrorEvents is true) { if (_options?.AttachStacktrace is true && !string.IsNullOrEmpty(stacktrace)) { diff --git a/src/Sentry.Unity/Integrations/UnityErrorLogException.cs b/src/Sentry.Unity/Integrations/UnityErrorLogException.cs index a803bf585..8d7a6daeb 100644 --- a/src/Sentry.Unity/Integrations/UnityErrorLogException.cs +++ b/src/Sentry.Unity/Integrations/UnityErrorLogException.cs @@ -50,6 +50,7 @@ public SentryException ToSentryException() { Stacktrace = stacktrace, Value = _logString, + Type = ExceptionType, Mechanism = new Mechanism { Handled = true, diff --git a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs index a17a5a5d3..66d243533 100644 --- a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs @@ -37,22 +37,6 @@ public void SetUp() }; } - [Test] - public void CaptureException_AddAsBreadcrumbEnabled_AddedAsBreadcrumb() - { - _fixture.SentryOptions.AddBreadcrumbsForLogType[LogType.Exception] = true; - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - - sut.CaptureException(new Exception(message), null); - - var scope = new Scope(_fixture.SentryOptions); - _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); - var breadcrumb = scope.Breadcrumbs.Single(); - - StringAssert.Contains(message, breadcrumb.Message); - } - [Test] public void CaptureException_AddAsBreadcrumbEnabled_NotAddedAsBreadcrumb() { @@ -88,26 +72,6 @@ public void CaptureException_ExceptionCapturedAndMechanismSet() Assert.AreEqual("Unity.LogException", (string)capturedEvent.Exception!.Data[Mechanism.MechanismKey]); } - [Test] - public void CaptureException_CapturedExceptionAddedAsBreadcrumb() - { - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - var exception = new Exception(message); - - sut.CaptureException(exception, null); - - Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); // Sanity check - - var scope = new Scope(_fixture.SentryOptions); - _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); - var breadcrumb = scope.Breadcrumbs.Single(); - - Assert.AreEqual(exception.GetType() + ": " + message, breadcrumb.Message); - Assert.AreEqual("unity.logger", breadcrumb.Category); - Assert.AreEqual(BreadcrumbLevel.Error, breadcrumb.Level); - } - [Test] public void Register_RegisteredASecondTime_LogsWarningAndReturns() { From 0d33cdfba69d2e88a5c2b52e00ce23ce24924652 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 26 Sep 2025 13:11:12 +0200 Subject: [PATCH 4/8] Updated CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a3715ac5..3055b35fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Breaking Changes + +- The SDK will now always add a breadcrumb when capturing an exception. The option to opt-out of this has been removed. ([#2335](https://github.com/getsentry/sentry-unity/pull/2335)) + ### Features - Added Xbox Native Support ([#2314](https://github.com/getsentry/sentry-unity/pull/2314)) From 6e0967ff11392149d61b3ec703b3545d71f22582 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 29 Sep 2025 12:13:57 +0200 Subject: [PATCH 5/8] Removed now unnecessary test --- .../UnityLogHandlerIntegrationTests.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs index 66d243533..9070daa95 100644 --- a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs @@ -37,18 +37,6 @@ public void SetUp() }; } - [Test] - public void CaptureException_AddAsBreadcrumbEnabled_NotAddedAsBreadcrumb() - { - _fixture.SentryOptions.AddBreadcrumbsForLogType[LogType.Exception] = false; - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - - sut.CaptureException(new Exception("Test Exception"), null); - - Assert.IsFalse(_fixture.Hub.ConfigureScopeCalls.Count > 0); - } - [Test] public void CaptureException_ExceptionCapturedAndMechanismSet() { From fb4452f967f5d5439b10c7da0a9c085c7c8419a0 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 29 Sep 2025 12:29:40 +0200 Subject: [PATCH 6/8] Install pinned .NET SDK --- .github/workflows/format-code.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index ef4dbcaed..d76eb56fa 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -21,6 +21,12 @@ jobs: -o -name "*.m" | xargs clang-format -i -style=file + - name: Install .NET SDK + if: runner.os != 'Windows' + uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 + with: + global-json-file: src/sentry-dotnet/global.json + - name: Format C# Code Whitespace run: dotnet format whitespace Sentry.Unity.sln --exclude src/sentry-dotnet --verbosity diag From 742553e251049c5a1c82f38cc67385fee228b088 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 29 Sep 2025 12:49:18 +0200 Subject: [PATCH 7/8] Restore workload after installation --- .github/workflows/format-code.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index d76eb56fa..e73388028 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -27,6 +27,9 @@ jobs: with: global-json-file: src/sentry-dotnet/global.json + - name: Restore .NET Workload + run: dotnet workload restore + - name: Format C# Code Whitespace run: dotnet format whitespace Sentry.Unity.sln --exclude src/sentry-dotnet --verbosity diag From ea18d1c9f4fec001b4f94f785aa114f514a6b82c Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 29 Sep 2025 12:59:16 +0200 Subject: [PATCH 8/8] Use Unity global.json --- .github/workflows/build.yml | 2 +- .github/workflows/format-code.yml | 2 +- global.json | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 702c196ee..281c578dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -63,7 +63,7 @@ jobs: if: runner.os != 'Windows' uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 with: - global-json-file: src/sentry-dotnet/global.json + global-json-file: global.json - name: Install Android dotnet workflow run: dotnet workload install android --temp-dir "$RUNNER_TEMP" diff --git a/.github/workflows/format-code.yml b/.github/workflows/format-code.yml index e73388028..fbac3e1e8 100644 --- a/.github/workflows/format-code.yml +++ b/.github/workflows/format-code.yml @@ -25,7 +25,7 @@ jobs: if: runner.os != 'Windows' uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 with: - global-json-file: src/sentry-dotnet/global.json + global-json-file: global.json - name: Restore .NET Workload run: dotnet workload restore diff --git a/global.json b/global.json index 47a7fa603..8d925dc19 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,7 @@ { "sdk": { + "version": "9.0.304", + "workloadVersion": "9.0.304", "allowPrerelease": false } }