diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa7dd6a2c..b4a2c96d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
### 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))
- The deprecated Runtime- and BuildTime-Configuration have been removed in favor of the single `OptionsConfiguration` script. When migrating you can make use of preprocessor directives to set specific options for specific platforms. Check out the ([Migration Guide](https://docs.sentry.io/platforms/unity/migration/#changes-to-the-programmatic-configuration)). ([#2337](https://github.com/getsentry/sentry-unity/pull/2337))
### Features
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/UnityApplicationLoggingIntegration.cs b/src/Sentry.Unity/Integrations/UnityApplicationLoggingIntegration.cs
index 11c4ac0f7..52143fe65 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;
@@ -80,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 c62d966a0..8d7a6daeb 100644
--- a/src/Sentry.Unity/Integrations/UnityErrorLogException.cs
+++ b/src/Sentry.Unity/Integrations/UnityErrorLogException.cs
@@ -49,8 +49,8 @@ public SentryException ToSentryException()
return new SentryException
{
Stacktrace = stacktrace,
- Type = ExceptionType,
Value = _logString,
+ Type = ExceptionType,
Mechanism = new Mechanism
{
Handled = true,
diff --git a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs
index 79a610685..b93b15a1a 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;
@@ -73,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 5e480ad5e..90e5808fb 100644
--- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs
+++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs
@@ -62,7 +62,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;
@@ -207,7 +206,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 9d5c05ec8..e0008f79c 100644
--- a/src/Sentry.Unity/SentryUnityOptions.cs
+++ b/src/Sentry.Unity/SentryUnityOptions.cs
@@ -388,7 +388,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`
diff --git a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs
index a17a5a5d3..9070daa95 100644
--- a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs
+++ b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs
@@ -37,34 +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()
- {
- _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()
{
@@ -88,26 +60,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()
{