diff --git a/Runtime/BacktraceClient.cs b/Runtime/BacktraceClient.cs index b158d336..3d9926c7 100644 --- a/Runtime/BacktraceClient.cs +++ b/Runtime/BacktraceClient.cs @@ -930,25 +930,32 @@ internal void HandleLowMemory() /// log type internal void HandleUnityMessage(string message, string stackTrace, LogType type) { - if (!Enabled) + if (!Enabled || !Configuration.HandleUnhandledExceptions) { return; } - var unityMessage = new BacktraceUnityMessage(message, stackTrace, type); - if (Configuration.HandleUnhandledExceptions && unityMessage.IsUnhandledException()) + if (string.IsNullOrEmpty(message) || (type != LogType.Error && type != LogType.Exception)) { - BacktraceUnhandledException exception = null; - var invokeSkipApi = true; - - // detect sampling flow - // we should apply sampling only to unhandled exceptions that are type LogType == Error - // log type error won't provide full exception information - if (type == LogType.Error && SamplingShouldSkip()) + return; + } + BacktraceUnhandledException exception = null; + var invokeSkipApi = true; + // detect sampling flow for LogType.Error + filter LogType.Error if client prefer to ignore them. + if (type == LogType.Error) + { + if (Configuration.ReportFilterType.HasFlag(ReportFilterType.Error)) + { + return; + } + if (SamplingShouldSkip()) { - if (SkipReport != null || Configuration.ReportFilterType.HasFlag(ReportFilterType.UnhandledException)) + if (SkipReport != null) { - exception = new BacktraceUnhandledException(unityMessage.Message, unityMessage.StackTrace); - if (ShouldSkipReport(ReportFilterType.UnhandledException, exception, string.Empty)) + exception = new BacktraceUnhandledException(message, stackTrace) + { + Type = type + }; + if (ShouldSkipReport(ReportFilterType.Error, exception, string.Empty)) { return; } @@ -959,14 +966,17 @@ internal void HandleUnityMessage(string message, string stackTrace, LogType type return; } } + } - if (exception == null) + if (exception == null) + { + exception = new BacktraceUnhandledException(message, stackTrace) { - exception = new BacktraceUnhandledException(unityMessage.Message, unityMessage.StackTrace); - } - - SendUnhandledException(exception, invokeSkipApi); + Type = type + }; } + + SendUnhandledException(exception, invokeSkipApi); } /// @@ -1010,9 +1020,10 @@ private bool ShouldSendReport(Exception exception, List attachmentPaths, var filterType = ReportFilterType.Exception; if (exception is BacktraceUnhandledException) { - filterType = (exception as BacktraceUnhandledException).Classifier == "ANRException" + var unhandledException = (exception as BacktraceUnhandledException); + filterType = unhandledException.Classifier == "ANRException" ? ReportFilterType.Hang - : ReportFilterType.UnhandledException; + : unhandledException.Type == LogType.Exception ? ReportFilterType.UnhandledException: ReportFilterType.Error; } diff --git a/Runtime/Model/BacktraceUnhandledException.cs b/Runtime/Model/BacktraceUnhandledException.cs index 6ba69362..ab9df01b 100644 --- a/Runtime/Model/BacktraceUnhandledException.cs +++ b/Runtime/Model/BacktraceUnhandledException.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using UnityEngine; namespace Backtrace.Unity.Model { @@ -38,6 +39,7 @@ public override string StackTrace return _stacktrace; } } + public LogType Type { get; set; } = LogType.Exception; /// /// Unhandled exception stack frames diff --git a/Runtime/Model/BacktraceUnityMessage.cs b/Runtime/Model/BacktraceUnityMessage.cs deleted file mode 100644 index cd3d3102..00000000 --- a/Runtime/Model/BacktraceUnityMessage.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Globalization; -using System.Text; -using UnityEngine; - -namespace Backtrace.Unity.Model -{ - /// - /// Unity message representation in Backtrace-Unity library - /// - internal class BacktraceUnityMessage - { - private readonly string _formattedMessage; - public readonly string Message; - public readonly string StackTrace; - public readonly LogType Type; - - public BacktraceUnityMessage(BacktraceReport report) - { - if (report == null) - { - throw new ArgumentException("report"); - } - Message = report.Message; - - if (report.ExceptionTypeReport) - { - Type = LogType.Exception; - StackTrace = GetFormattedStackTrace(report.Exception.StackTrace); - _formattedMessage = GetFormattedMessage(true); - } - else - { - StackTrace = string.Empty; - Type = LogType.Warning; - _formattedMessage = GetFormattedMessage(true); - } - } - public BacktraceUnityMessage(string message, string stacktrace, LogType type) - { - Message = message; - StackTrace = GetFormattedStackTrace(stacktrace); - Type = type; - _formattedMessage = GetFormattedMessage(false); - } - - private string GetFormattedMessage(bool backtraceFrame) - { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendFormat( - "[{0}] {1}<{2}>: {3}", new object[4] { - DateTime.Now.ToUniversalTime().ToString(CultureInfo.InvariantCulture), - backtraceFrame ? "(Backtrace)" : string.Empty, - Enum.GetName(typeof(LogType), Type), - Message} - ); - - // include stack trace if log was generated by exception/error - if (IsUnhandledException()) - { - stringBuilder.AppendLine(); - stringBuilder.Append(string.IsNullOrEmpty(StackTrace) ? "No stack trace available" : StackTrace); - } - return stringBuilder.ToString(); - } - - private string GetFormattedStackTrace(string stacktrace) - { - return !string.IsNullOrEmpty(stacktrace) && stacktrace.EndsWith("\n") - ? stacktrace.Remove(stacktrace.LastIndexOf("\n")) - : stacktrace; - } - - /// - /// Return information if current Unity message contain information about error or exception - /// - /// True if unity message is an exception/error message - public bool IsUnhandledException() - { - return ((Type == LogType.Exception || Type == LogType.Error) - && !string.IsNullOrEmpty(Message)); - } - - /// - /// Convert Backtrace Untiy Message to string that will be acceptable by source code format - /// - /// Source code string - public override string ToString() - { - return _formattedMessage; - } - } -} diff --git a/Runtime/Model/BacktraceUnityMessage.cs.meta b/Runtime/Model/BacktraceUnityMessage.cs.meta deleted file mode 100644 index 257150d6..00000000 --- a/Runtime/Model/BacktraceUnityMessage.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1d8cc9a1d1a0c4b4d99ae26b4bbb5d1c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Runtime/Types/ReportFilterType.cs b/Runtime/Types/ReportFilterType.cs index a4cc7a0d..c540dfd9 100644 --- a/Runtime/Types/ReportFilterType.cs +++ b/Runtime/Types/ReportFilterType.cs @@ -44,6 +44,15 @@ public enum ReportFilterType /// Game hang /// [Tooltip("Game hang.")] - Hang = 8 + Hang = 8, + + /// + /// Unhandled exception - Error and Exception messages generated by Unity Logger. + /// + [Tooltip("Game error.")] +#if UNITY_2019_2_OR_NEWER + [InspectorName("Game error")] +#endif + Error = 16 } } diff --git a/Tests/Runtime/ReportFilter/ReportFilterTypeTests.cs b/Tests/Runtime/ReportFilter/ReportFilterTypeTests.cs index a2fb65e1..4767349b 100644 --- a/Tests/Runtime/ReportFilter/ReportFilterTypeTests.cs +++ b/Tests/Runtime/ReportFilter/ReportFilterTypeTests.cs @@ -19,6 +19,59 @@ public void Setup() AfterSetup(false); } + [UnityTest] + public IEnumerator TestErrorTypeFilter_ShouldFilterErrorLog_ShouldPreventFromSendingDataToBacktrace() + { + const string errorMessage = "errorMessage"; + var eventCalled = false; + BacktraceClient.SkipReport = (ReportFilterType type, Exception e, string msg) => + { + eventCalled = true; + return false; + }; + BacktraceClient.Configuration.ReportFilterType = ReportFilterType.Error; + + BacktraceClient.HandleUnityMessage(errorMessage, string.Empty, LogType.Error); + yield return new WaitForEndOfFrame(); + + Assert.IsFalse(eventCalled); + } + + [Test] + public void TestErrorTypeFilter_ShouldntFilterErrorLogWhenFilterDoesntIncludeIt_ShouldInvokeSkipCallback() + { + const string errorMessage = "errorMessage"; + var eventCalled = false; + BacktraceClient.SkipReport = (ReportFilterType type, Exception e, string msg) => + { + eventCalled = true; + return false; + }; + BacktraceClient.Configuration.ReportFilterType = ReportFilterType.UnhandledException; + + BacktraceClient.HandleUnityMessage(errorMessage, string.Empty, LogType.Error); + + Assert.IsTrue(eventCalled); + } + + + [Test] + public void TestErrorTypeFilterShouldSetCorrectReportFilterType_ReportFilterTypeHasCorrectValue() + { + const string errorMessage = "errorMessage"; + var reportFilterType = ReportFilterType.None; + BacktraceClient.SkipReport = (ReportFilterType type, Exception e, string msg) => + { + reportFilterType = type; + return false; + }; + BacktraceClient.Configuration.ReportFilterType = ReportFilterType.UnhandledException; + + BacktraceClient.HandleUnityMessage(errorMessage, string.Empty, LogType.Error); + + Assert.AreEqual(ReportFilterType.Error, reportFilterType); + } + [UnityTest] public IEnumerator TestReportFilter_ShouldPreventFromSendingMessage_ClientNotSendingData() {