/// <summary> /// Collect additional report information from client and convert report to backtrace data /// </summary> /// <param name="report">Backtrace report</param> /// <returns>Backtrace data</returns> private BacktraceData SetupBacktraceData(BacktraceReport report) { // apply _mod fingerprint attribute when client should use // normalized exception message instead environment stack trace // for exceptions without stack trace. if (Configuration.UseNormalizedExceptionMessage) { report.SetReportFingerPrintForEmptyStackTrace(); } // add environment information to backtrace report var sourceCode = _backtraceLogManager.Disabled ? new BacktraceUnityMessage(report).ToString() : _backtraceLogManager.ToSourceCode(); report.AssignSourceCodeToReport(sourceCode); // pass copy of dictionary to prevent overriding client attributes var result = report.ToBacktraceData(null, GameObjectDepth); // add native attributes to client report if (_nativeClient != null) { _nativeClient.GetAttributes(result.Attributes.Attributes); } // apply client attributes foreach (var attribute in _clientAttributes) { result.Attributes.Attributes[attribute.Key] = attribute.Value; } return(result); }
public IEnumerator TestFingerprintBehaviorForNormalizedExceptionMessage_ShouldGenerateFingerprintForExceptionReportWithoutStackTrace_ShouldIncludeFingerprintInBacktraceReport() { BacktraceClient.Configuration = GetValidClientConfiguration(); BacktraceClient.Configuration.UseNormalizedExceptionMessage = true; BacktraceClient.Refresh(); // exception without stack trace might happened when exception occured because of // invalid game object setting or via weird crash // exception below has empty exception stack trace var exception = new BacktraceUnhandledException("00:00:00 00/00/00 Unhandled exception", string.Empty); var expectedNormalizedMessage = "Unhandledexception"; var report = new BacktraceReport(exception); bool eventFired = false; BacktraceClient.BeforeSend = (BacktraceData data) => { Assert.IsNotNull(data.Attributes.Attributes["_mod_fingerprint"]); Assert.AreEqual(expectedNormalizedMessage.GetSha(), data.Attributes.Attributes["_mod_fingerprint"]); eventFired = true; // prevent backtrace data from sending to Backtrace. return(null); }; yield return(BacktraceClient.StartCoroutine(CallBacktraceClientAndWait(report))); Assert.IsTrue(eventFired); }
private async Task <BacktraceResult> HandleAggregateException(BacktraceReport report) { var aggregateException = report.Exception as AggregateException; BacktraceResult result = null; foreach (var ex in aggregateException.InnerExceptions) { var innerReport = new BacktraceReport( exception: ex, attributes: report.Attributes, attachmentPaths: report.AttachmentPaths, reflectionMethodName: report._reflectionMethodName) { Factor = report.Factor, Fingerprint = report.Fingerprint }; if (result == null) { result = await SendAsync(innerReport); } else { result.AddInnerResult(await SendAsync(innerReport)); } } return(result ?? new BacktraceResult() { Status = BacktraceResultStatus.Empty, BacktraceReport = report }); }
private IEnumerator Send(string json, List <string> attachments, BacktraceReport report, Action <BacktraceResult> callback) { using (var request = new UnityWebRequest(_serverurl, "POST")) { AppendBodyToFile(json); byte[] bodyRaw = Encoding.UTF8.GetBytes(json); request.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw); request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer(); request.SetRequestHeader("Content-Type", "application/json"); yield return(request.SendWebRequest()); BacktraceResult result; if (request.responseCode == 200) { result = new BacktraceResult(); OnServerResponse?.Invoke(result); var response = BacktraceResult.FromJson(request.downloadHandler.text); if (attachments != null && attachments.Count > 0) { var stack = new Stack <string>(attachments); yield return(SendAttachment(response.Object, stack)); } } else { PrintLog(request); var exception = new Exception(request.error); result = BacktraceResult.OnError(report, exception); OnServerError?.Invoke(exception); } callback?.Invoke(result); yield return(result); } }
public IEnumerator TestReportLimit_TestSendingBacktraceReport_SkippProcessingReports() { BacktraceClient.SetClientReportLimit(CLIENT_RATE_LIMIT); int totalNumberOfReports = 5; int maximumNumberOfRetries = 0; BacktraceClient.RequestHandler = (string url, BacktraceData data) => { maximumNumberOfRetries++; return(new BacktraceResult()); }; int skippedReports = 0; BacktraceClient.OnClientReportLimitReached = (BacktraceReport report) => { skippedReports++; }; for (int i = 0; i < totalNumberOfReports; i++) { var report = new BacktraceReport(new Exception("Exception")); BacktraceClient.Send(report); } Assert.AreEqual(totalNumberOfReports, maximumNumberOfRetries + skippedReports); Assert.AreEqual(maximumNumberOfRetries, CLIENT_RATE_LIMIT); Assert.AreEqual(totalNumberOfReports - CLIENT_RATE_LIMIT, skippedReports); yield return(null); }
/// <summary> /// Send asynchronous report to Backtrace API /// </summary> /// <param name="report">Report to send</param> public virtual async Task <BacktraceResult> SendAsync(BacktraceReport report) { if (UnpackAggregateExcetpion && report.Exception is AggregateException) { return(await HandleAggregateException(report)); } var record = Database.Add(report, Attributes, MiniDumpType); //create a JSON payload instance var data = record?.BacktraceData ?? report.ToBacktraceData(Attributes); //valid user custom events data = BeforeSend?.Invoke(data) ?? data; var result = await BacktraceApi.SendAsync(data); record?.Dispose(); if (result?.Status == BacktraceResultStatus.Ok) { Database.Delete(record); } //check if there is more errors to send //handle inner exception result.InnerExceptionResult = await HandleInnerExceptionAsync(report); return(result); }
public void TestReportStackTrace_StackTraceShouldBeTheSameLikeExceptionStackTrace_ShouldReturnCorrectStackTrace() { var exception = new Exception("exception"); var report = new BacktraceReport(exception); Assert.AreEqual(report.DiagnosticStack.Count, exception.StackTrace?.Count() ?? 0); }
public IEnumerator TestFingerprintBehaviorForNormalizedExceptionMessage_ShouldGenerateFingerprintAndShouldntRemoveAnyLetter_ShouldIncludeFingerprintInBacktraceReport() { BacktraceClient.Configuration = GetValidClientConfiguration(); BacktraceClient.Configuration.UseNormalizedExceptionMessage = true; BacktraceClient.Refresh(); var normalizedMessage = "Unhandledexception"; var exception = new BacktraceUnhandledException(normalizedMessage, string.Empty); var report = new BacktraceReport(exception); bool eventFired = false; BacktraceClient.BeforeSend = (BacktraceData data) => { const string modFingerprintKey = "_mod_fingerprint"; Assert.IsTrue(data.Attributes.Attributes.ContainsKey(modFingerprintKey)); Assert.IsNotNull(data.Attributes.Attributes[modFingerprintKey]); Assert.AreEqual(normalizedMessage.GetSha(), data.Attributes.Attributes[modFingerprintKey]); // prevent backtrace data from sending to Backtrace. eventFired = true; // prevent backtrace data from sending to Backtrace. return(null); }; yield return(BacktraceClient.StartCoroutine(CallBacktraceClientAndWait(report))); Assert.IsTrue(eventFired); }
/// <summary> /// Create instance of Backtrace Attribute /// </summary> /// <param name="report">Received report</param> /// <param name="clientAttributes">Client's attributes (report and client)</param> public BacktraceAttributes(BacktraceReport report, Dictionary <string, string> clientAttributes, bool onlyBuiltInAttributes = false) { if (clientAttributes == null) { clientAttributes = new Dictionary <string, string>(); } Attributes = clientAttributes; if (report != null) { // Add report attributes if (report.Attributes != null) { foreach (var attribute in report.Attributes) { Attributes[attribute.Key] = attribute.Value; } } SetExceptionAttributes(report); } //Environment attributes override user attributes SetLibraryAttributes(report); SetMachineAttributes(onlyBuiltInAttributes); SetProcessAttributes(onlyBuiltInAttributes); SetSceneInformation(onlyBuiltInAttributes); }
private bool ShouldSendReport(Exception exception, List <string> attachmentPaths, Dictionary <string, string> attributes) { // guess report type var filterType = ReportFilterType.Exception; if (exception is BacktraceUnhandledException) { filterType = (exception as BacktraceUnhandledException).Classifier == "ANRException" ? ReportFilterType.Hang : ReportFilterType.UnhandledException; } if (ShouldSkipReport(filterType, exception, string.Empty)) { return(false); } //check rate limiting bool shouldProcess = _reportLimitWatcher.WatchReport(new DateTime().Timestamp()); if (shouldProcess) { return(true); } if (OnClientReportLimitReached != null) { var report = new BacktraceReport( exception: exception, attachmentPaths: attachmentPaths, attributes: attributes); _onClientReportLimitReached.Invoke(report); } return(false); }
public void TestExceptionTypeAttribute_ShouldSetExceptionTypeHang_ExceptionTypeAttributeIsCorrect() { var report = new BacktraceReport(new BacktraceUnhandledException("ANRException: Blocked thread detected", string.Empty)); var testAttributes = new BacktraceAttributes(report, null); Assert.AreEqual("Hang", testAttributes.Attributes["error.type"]); }
public void TestExceptionTypeAttribute_ShouldSetExceptionTypeUnhandledException_ExceptionTypeAttributeIsCorrect() { var report = new BacktraceReport(new BacktraceUnhandledException("foo", string.Empty)); var testAttributes = new BacktraceAttributes(report, null); Assert.AreEqual("Unhandled exception", testAttributes.Attributes["error.type"]); }
public void TestExceptionTypeAttribute_ShouldSetExceptionTypeException_ExceptionTypeAttributeIsCorrect() { var report = new BacktraceReport(new Exception("foo")); var testAttributes = new BacktraceAttributes(report, null); Assert.AreEqual("Exception", testAttributes.Attributes["error.type"]); }
/// <summary> /// Set attributes from exception /// </summary> internal void SetExceptionAttributes(BacktraceReport report) { //there is no information to analyse if (report == null) { return; } Attributes["error.message"] = report.ExceptionTypeReport ? report.Exception.Message : report.Message; // detect exception type var errorType = "error.type"; if (!report.ExceptionTypeReport) { Attributes[errorType] = "Message"; return; } if (report.Exception is BacktraceUnhandledException) { if ((report.Exception as BacktraceUnhandledException).Classifier == "ANRException") { Attributes[errorType] = "Hang"; } else { Attributes[errorType] = "Unhandled exception"; } } else { Attributes[errorType] = "Exception"; } }
public void TestReportStackTrace_StackTraceShouldIncludeEnvironmentStackTrace_ShouldReturnCorrectStackTrace() { var environmentStackTrace = new StackTrace(true); var report = new BacktraceReport("msg"); Assert.AreEqual(report.DiagnosticStack.Count, environmentStackTrace.FrameCount); }
public IEnumerator TestFingerprintBehaviorForNormalizedExceptionMessage_ShouldUseReportFingerprint_ReportFingerprintInAttributes() { BacktraceClient.Configuration = GetValidClientConfiguration(); BacktraceClient.Configuration.UseNormalizedExceptionMessage = true; BacktraceClient.Refresh(); // exception without stack trace might happened when exception occured because of // invalid game object setting or via weird crash // exception below has empty exception stack trace var exception = new BacktraceUnhandledException("00:00:00 00/00/00 Unhandled exception", string.Empty); var report = new BacktraceReport(exception); const string expectedFingerprint = "foo-bar"; report.Fingerprint = expectedFingerprint; bool eventFired = false; BacktraceClient.BeforeSend = (BacktraceData data) => { eventFired = true; Assert.IsNotNull(data.Attributes.Attributes["_mod_fingerprint"]); Assert.AreEqual(expectedFingerprint, data.Attributes.Attributes["_mod_fingerprint"]); // prevent backtrace data from sending to Backtrace. return(null); }; BacktraceClient.Send(report); yield return(new WaitForEndOfFrame()); Assert.IsTrue(eventFired); }
private bool ShouldSendReport(BacktraceReport report) { if (ShouldSkipReport( report.ExceptionTypeReport ? ReportFilterType.Exception : ReportFilterType.Message, report.Exception, report.Message)) { return(false); } //check rate limiting bool shouldProcess = _reportLimitWatcher.WatchReport(DateTimeHelper.Timestamp()); if (shouldProcess) { // This condition checks if we should send exception from current thread // if comparision result confirm that we're trying to send an exception from different // thread than main, we should add the exception object to the exception list // and let update method send data to Backtrace. if (Thread.CurrentThread.ManagedThreadId != _current.ManagedThreadId) { report.Attributes["exception.thread"] = Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture); BackgroundExceptions.Push(report); return(false); } return(true); } if (OnClientReportLimitReached != null) { _onClientReportLimitReached.Invoke(report); } return(false); }
/// <summary> /// Set library attributes /// </summary> private void SetLibraryAttributes(BacktraceReport report) { if (!string.IsNullOrEmpty(report.Fingerprint)) { Attributes["_mod_fingerprint"] = report.Fingerprint; } if (!string.IsNullOrEmpty(report.Factor)) { Attributes["_mod_factor"] = report.Factor; } //A unique identifier of a machine Attributes["guid"] = GenerateMachineId(); //Base name of application generating the report Attributes["application"] = Application.productName; Attributes["application.url"] = Application.absoluteURL; Attributes["application.company.name"] = Application.companyName; Attributes["application.data_path"] = Application.dataPath; Attributes["application.id"] = Application.identifier; Attributes["application.installer.name"] = Application.installerName; Attributes["application.internet_reachability"] = Application.internetReachability.ToString(); Attributes["application.editor"] = Application.isEditor; Attributes["application.focused"] = Application.isFocused; Attributes["application.mobile"] = Application.isMobilePlatform; Attributes["application.playing"] = Application.isPlaying; Attributes["application.background"] = Application.runInBackground; Attributes["application.sandboxType"] = Application.sandboxType.ToString(); Attributes["application.system.language"] = Application.systemLanguage.ToString(); Attributes["application.unity.version"] = Application.unityVersion; Attributes["application.temporary_cache"] = Application.temporaryCachePath; Attributes["applicaiton.debug"] = Debug.isDebugBuild; }
public IEnumerator TestFingerprintBehaviorForNormalizedExceptionMessage_ShouldntGenerateFingerprintForDisabledOption_FingerprintDoesntExist() { BacktraceClient.Configuration = GetValidClientConfiguration(); BacktraceClient.Configuration.UseNormalizedExceptionMessage = false; BacktraceClient.Refresh(); // exception without stack trace might happened when exception occured because of // invalid game object setting or via weird crash // exception below has empty exception stack trace var exception = new BacktraceUnhandledException("00:00:00 00/00/00 Unhandled exception", string.Empty); var report = new BacktraceReport(exception); bool eventFired = false; BacktraceClient.BeforeSend = (BacktraceData data) => { Assert.IsFalse(data.Attributes.Attributes.ContainsKey("_mod_fingerprint")); eventFired = true; // prevent backtrace data from sending to Backtrace. return(null); }; yield return(BacktraceClient.StartCoroutine(CallBacktraceClientAndWait(report))); Assert.IsTrue(eventFired); }
/// <summary> /// Add new report to BacktraceDatabase /// </summary> public BacktraceDatabaseRecord Add(BacktraceReport backtraceReport, Dictionary <string, object> attributes, MiniDumpType miniDumpType = MiniDumpType.Normal) { if (!_enable || backtraceReport == null) { return(null); } //remove old reports (if database is full) //and check database health state var validationResult = ValidateDatabaseSize(); if (!validationResult) { return(null); } if (miniDumpType != MiniDumpType.None) { string minidumpPath = GenerateMiniDump(backtraceReport, miniDumpType); if (!string.IsNullOrEmpty(minidumpPath)) { backtraceReport.SetMinidumpPath(minidumpPath); } } var data = backtraceReport.ToBacktraceData(attributes); return(BacktraceDatabaseContext.Add(data)); }
/// <summary> /// Convert custom user attributes /// </summary> /// <param name="report">Received report</param> /// <param name="clientAttributes">Client's attributes (report and client)</param> /// <returns>Dictionary of custom user attributes </returns> private void ConvertAttributes(BacktraceReport report, Dictionary <string, object> clientAttributes) { var attributes = BacktraceReport.ConcatAttributes(report, clientAttributes); foreach (var attribute in attributes) { if (attribute.Value == null) { continue; } var type = attribute.Value.GetType(); if (type.IsPrimitive || type == typeof(string) || type.IsEnum) { Attributes.Add(attribute.Key, attribute.Value); } else { ComplexAttributes.Add(attribute.Key, attribute.Value); } } //add exception information to Complex attributes. if (report.ExceptionTypeReport) { ComplexAttributes.Add("Exception Properties", report.Exception); } }
/// <summary> /// Send a report to Backtrace API /// </summary> /// <param name="report">Report to send</param> /// <param name="sendCallback">Send report callback</param> public void Send(BacktraceReport report, Action <BacktraceResult> sendCallback = null) { if (Enabled == false) { Debug.LogWarning("Please enable BacktraceClient first - Please validate Backtrace client initializaiton in Unity IDE."); return; } //check rate limiting bool limitHit = _reportLimitWatcher.WatchReport(report); if (!limitHit) { if (_onClientReportLimitReached != null) { _onClientReportLimitReached.Invoke(report); } if (sendCallback != null) { sendCallback.Invoke(BacktraceResult.OnLimitReached(report)); } _reportLimitWatcher.DisplayReportLimitHitMessage(); return; } SendReport(report, sendCallback); }
public IEnumerator TestCorrectDictionaryGeneration_CreateCorrectAttributesDictionary_WithDiffrentClientAttributes() { var exception = new FileNotFoundException(); var reportAttributeKey = "report_attr"; var reportAttributeValue = string.Format("{0}-value", reportAttributeKey); var reportAttributes = new Dictionary <string, object>() { { reportAttributeKey, reportAttributeValue } }; var exceptionReport = new BacktraceReport(exception, reportAttributes); string clientAttributeKey = "client_attr"; string clientAttributeValue = string.Format("{0}-value", clientAttributeKey); var clientAttributes = new Dictionary <string, object>() { { clientAttributeKey, clientAttributeValue } }; var testObject = new BacktraceAttributes(exceptionReport, clientAttributes); Assert.IsTrue(testObject.Attributes.Keys.Any(n => n == clientAttributeKey)); Assert.IsTrue(testObject.Attributes.Keys.Any(n => n == reportAttributeKey)); Assert.IsTrue(testObject.Attributes[clientAttributeKey] as string == clientAttributeValue); Assert.IsTrue(testObject.Attributes[reportAttributeKey] as string == reportAttributeValue); yield return(null); }
/// <summary> /// Send an exception to Backtrace API /// </summary> /// <param name="exception">Report exception</param> /// <param name="attachmentPaths">List of attachments</param> /// <param name="attributes">List of report attributes</param public void Send(Exception exception, List <string> attachmentPaths = null, Dictionary <string, object> attributes = null) { if (Enabled == false) { Debug.LogWarning("Please enable BacktraceClient first - Please validate Backtrace client initializaiton in Unity IDE."); return; } //check rate limiting bool limitHit = _reportLimitWatcher.WatchReport(new DateTime().Timestamp()); if (limitHit == false && _onClientReportLimitReached == null) { _reportLimitWatcher.DisplayReportLimitHitMessage(); return; } var report = new BacktraceReport( exception: exception, attachmentPaths: attachmentPaths, attributes: attributes); if (!limitHit) { if (_onClientReportLimitReached != null) { _onClientReportLimitReached.Invoke(report); } _reportLimitWatcher.DisplayReportLimitHitMessage(); return; } SendReport(report); }
public void TestReportStackTrace_ShouldGenerateStackTraceForExceptionReport_ExceptionReportHasStackTrace() { //simulate real exception to generate an exception with stack trace. Exception exception = null; try { var arr = new List <int>() { 1, 2, 3, 4 }; arr.ElementAt(arr.Count + 1); } catch (Exception e) { exception = e; } var report = new BacktraceReport( exception: exception, attributes: reportAttributes, attachmentPaths: attachemnts); Assert.IsTrue(report.DiagnosticStack.Any()); }
/// <summary> /// Sending asynchronous Backtrace report to Backtrace API /// </summary> /// <param name="backtraceReport">Current report</param> /// <returns>Server response</returns> public override async Task <BacktraceResult> SendAsync(BacktraceReport backtraceReport) { OnReportStart?.Invoke(backtraceReport); var result = await base.SendAsync(backtraceReport); AfterSend?.Invoke(result); return(result); }
/// <summary> /// Sending a backtrace report to Backtrace API /// </summary> /// <param name="backtraceReport">Current report</param> public override BacktraceResult Send(BacktraceReport backtraceReport) { OnReportStart?.Invoke(backtraceReport); var result = base.Send(backtraceReport); AfterSend?.Invoke(result); return(result); }
public IEnumerator TestReportStackTrace_StackTraceShouldIncludeEnvironmentStackTrace_ShouldReturnCorrectStackTrace() { var environmentStackTrace = new StackTrace(true); var report = new BacktraceReport("msg"); Assert.AreEqual(environmentStackTrace.FrameCount, report.DiagnosticStack.Count); yield return(null); }
public IEnumerator TestReportStackTrace_StackTraceShouldBeTheSameLikeExceptionStackTrace_ShouldReturnCorrectStackTrace() { var exception = new Exception("exception"); var report = new BacktraceReport(exception); Assert.AreEqual(report.DiagnosticStack.Count, exception.StackTrace == null ? 0 : exception.StackTrace.Count()); yield return(null); }
/// <summary> /// Log report to Backtrace API /// </summary> /// <param name="report">Current exception or client message</param> public static void Log(BacktraceReport report) { if (_client == null) { return; } _client.Send(report); }