/// <summary> /// Create new instance of database record /// </summary> /// <param name="data">Diagnostic data</param> /// <param name="path">database path</param> public BacktraceDatabaseRecord(BacktraceData data, string path) { Id = data.Uuid; Record = data; _path = path; RecordWriter = new BacktraceDatabaseRecordWriter(path); }
/// <summary> /// Send a report to Backtrace API /// </summary> /// <param name="report">Report to send</param> public virtual void Send(BacktraceReportBase <T> report) { //check rate limiting bool watcherValidation = _reportWatcher.WatchReport(report); if (!watcherValidation) { OnClientReportLimitReached?.Invoke(); return; } //generate minidump and add minidump to report string minidumpPath = _database.GenerateMiniDump(report, MiniDumpType); if (!string.IsNullOrEmpty(minidumpPath)) { report._attachmentPaths.Add(minidumpPath); } //create a JSON payload instance var data = new BacktraceData <T>(report, Attributes); //valid user custom events data = BeforeSend?.Invoke(data) ?? data; _backtraceApi.Send(data); //clear minidumps generated by app _database.ClearMiniDump(minidumpPath); }
/// <summary> /// Get path to Unity player logs. /// </summary> /// <returns>Path to unity player log</returns> private string GetUnityPlayerLogFile(BacktraceData backtraceData, string dataPrefix) { if (!_settings.AddUnityLogToReport) { return(string.Empty); } var playerLogPath = #if UNITY_STANDALONE_LINUX string.Format("~/.config/unity3d/{0}/{1}/Player.log", Application.companyName, Application.productName); #elif UNITY_STANDALONE_OSX string.Format("~/Library/Logs/{0}/{1}/Player.log", Application.companyName, Application.productName); #elif UNITY_STANDALONE_WIN Path.Combine( Directory.GetParent(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)).FullName, "LocalLow", Application.companyName, Application.productName, "Player.log"); #else string.Empty; #endif if (string.IsNullOrEmpty(playerLogPath) || !File.Exists(playerLogPath)) { return(string.Empty); } var databaseLogPath = Path.Combine(_settings.DatabasePath, string.Format("{0}-lg.log", dataPrefix)); File.Copy(playerLogPath, databaseLogPath); return(databaseLogPath); }
/// <summary> /// Sending a diagnostic report data to server API. /// </summary> /// <param name="data">Diagnostic data</param> /// <returns>Server response</returns> public IEnumerator Send(BacktraceData data, Action <BacktraceResult> callback = null) { //check rate limiting bool watcherValidation = reportLimitWatcher.WatchReport(data.Report); if (!watcherValidation) { yield return(BacktraceResult.OnLimitReached(data.Report)); } if (data == null) { yield return(new BacktraceResult() { Status = Types.BacktraceResultStatus.LimitReached }); } string json = data.ToJson(); if (string.IsNullOrEmpty(json)) { yield return(BacktraceResult.OnLimitReached(data.Report)); } yield return(Send(json, data.Attachments, data.Report, callback)); }
/// <summary> /// Sending a diagnostic report data to server API. /// </summary> /// <param name="data">Diagnostic data</param> /// <returns>False if operation fail or true if API return OK</returns> public async Task <BacktraceServerResponse> Send(BacktraceData <T> data) { var json = JsonConvert.SerializeObject(data, SerializerSettings); var attachments = data.Attachments; var requestId = Guid.NewGuid().ToString("N"); var request = new HttpRequestMessage(HttpMethod.Post, _serverurl); var content = new MultipartFormDataContent(requestId); const string jsonName = "upload_file"; content.Add(new StringContent(json), jsonName, jsonName); foreach (var attachmentPath in attachments) { if (!File.Exists(attachmentPath)) { continue; } var name = "attachment_" + Path.GetFileName(attachmentPath); content.Add(new StreamContent(File.OpenRead(attachmentPath)), name, name); } request.Content = content; var response = await _http.SendAsync(request); var fullResponse = await response.Content.ReadAsStringAsync(); var serverResponse = JsonConvert.DeserializeObject <BacktraceServerResponse>(fullResponse, SerializerSettings); return(serverResponse); }
private string GetMinidumpPath(BacktraceData backtraceData, string dataPrefix) { if (_settings.MinidumpType == MiniDumpType.None) { return(string.Empty); } //note that every minidump file generated by app ends with .dmp extension //its important information if you want to clear minidump file string minidumpDestinationPath = Path.Combine(_settings.DatabasePath, string.Format("{0}-dump.dmp", dataPrefix)); var backtraceReport = backtraceData.Report; if (backtraceReport == null) { return(string.Empty); } MinidumpException minidumpExceptionType = backtraceReport.ExceptionTypeReport ? MinidumpException.Present : MinidumpException.None; bool minidumpSaved = MinidumpHelper.Write( filePath: minidumpDestinationPath, options: _settings.MinidumpType, exceptionType: minidumpExceptionType); return(minidumpSaved ? minidumpDestinationPath : string.Empty); }
public bool SaveReport(BacktraceData <T> backtraceReport) { if (!_enable) { return(true); } string json = JsonConvert.SerializeObject(backtraceReport); byte[] file = Encoding.UTF8.GetBytes(json); string filename = $"Backtrace_{backtraceReport.Timestamp}"; string filePath = Path.Combine(_directoryPath, filename); try { using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { fs.Write(file, 0, file.Length); } } catch (Exception) { return(false); } return(true); }
public IEnumerator TesClientAttributes_ReportShouldntExtendClientAttributes_ClientAttributesWontStoreReportAttributes() { var key = "foo"; var value = "bar"; BacktraceClient[key] = value; var numberOfKeysBeforeSendRequest = BacktraceClient.GetAttributesCount(); BacktraceData data = null; BacktraceClient.BeforeSend = (BacktraceData reportData) => { data = reportData; return(null); }; var exceptionsMessage = new string[] { "foo", "bar" }; foreach (var exceptionMessage in exceptionsMessage) { BacktraceClient.Send(new Exception(exceptionMessage)); yield return(new WaitForEndOfFrame()); } Assert.AreEqual(data.Attributes.Attributes[key], value); Assert.AreEqual(numberOfKeysBeforeSendRequest, BacktraceClient.GetAttributesCount()); yield return(null); }
public IEnumerator TestSourceCodeAssignment_DisabledLogManagerWithMultipleLogMessageAndMessageReport_SourceCodeAvailable() { BacktraceData lastData = null; BacktraceClient.BeforeSend = (BacktraceData data) => { lastData = data; return(data); }; //fake messages var fakeLogMessage = "log"; BacktraceClient.HandleUnityMessage(fakeLogMessage, string.Empty, UnityEngine.LogType.Log); var fakeWarningMessage = "warning message"; BacktraceClient.HandleUnityMessage(fakeWarningMessage, string.Empty, UnityEngine.LogType.Warning); // real exception var expectedExceptionMessage = "Exception message"; BacktraceClient.Send(expectedExceptionMessage); yield return(new WaitForEndOfFrame()); Assert.IsNotNull(lastData.SourceCode); var generatedText = lastData.SourceCode.Text; Assert.IsTrue(generatedText.Contains(expectedExceptionMessage)); Assert.IsFalse(generatedText.Contains(fakeLogMessage)); Assert.IsFalse(generatedText.Contains(fakeWarningMessage)); }
/// <summary> /// Create new instance of database record /// </summary> /// <param name="data">Diagnostic data</param> /// <param name="path">database path</param> public BacktraceDatabaseRecord(BacktraceData data, string path) { Id = data.Uuid; Record = data; _path = path; Attachments = data.Attachments; }
protected override BacktraceDatabaseRecord ConvertToRecord(BacktraceData backtraceData, string hash) { //create new record and return it to AVOID storing data on hard drive return(new BacktraceDatabaseRecord(backtraceData, _settings.DatabasePath) { Hash = hash }); }
public IEnumerator TestDataSerialization_ValidStringReport_ShouldGenerateValidJsonReport() { var report = new BacktraceReport("string"); var data = new BacktraceData(report, new Dictionary <string, string>(), 0); Assert.DoesNotThrow(() => data.ToJson()); yield return(null); }
public IEnumerable <string> GetReportAttachments(BacktraceData data) { return(new List <string>() { GetScreenshotPath(data), GetUnityPlayerLogFile(data), GetMinidumpPath(data) }); }
public IEnumerator TestDataSerialization_ValidReport_DataSerializeAndDeserialize() { var report = new BacktraceReport(new Exception("test")); var data = new BacktraceData(report, null); var json = data.ToJson(); var deserializedData = BacktraceData.Deserialize(json); Assert.IsTrue(deserializedData.Timestamp == data.Timestamp); yield return(null); }
public IEnumerator TestAttributes_ValidReport_AttributesExists() { var report = new BacktraceReport(new Exception("test")); var data = new BacktraceData(report, null); var json = data.Attributes.ToJson(); var deserializedData = BacktraceAttributes.Deserialize(json); Assert.IsTrue(deserializedData.Attributes.Count == data.Attributes.Attributes.Count); yield return(null); }
public IEnumerator TestAnnotations_ValidReport_AnnotationExists() { var report = new BacktraceReport(new Exception("test")); var data = new BacktraceData(report, null); var json = data.Annotation.ToJson(); var deserializedData = Annotations.Deserialize(json); Assert.IsTrue(deserializedData.EnvironmentVariables.Count == data.Annotation.EnvironmentVariables.Count); yield return(null); }
public IEnumerable <string> GetReportAttachments(BacktraceData data) { var attachmentPrefix = data.UuidString; var result = new List <string>(); AddIfPathIsNotEmpty(result, GetScreenshotPath(attachmentPrefix)); AddIfPathIsNotEmpty(result, GetUnityPlayerLogFile(data, attachmentPrefix)); AddIfPathIsNotEmpty(result, GetMinidumpPath(data, attachmentPrefix)); return(result); }
/// <summary> /// Convert Backtrace data to Backtrace record and save it. /// </summary> /// <param name="backtraceData">Backtrace data</param> /// <param name="hash">deduplicaiton hash</param> /// <returns></returns> protected virtual BacktraceDatabaseRecord ConvertToRecord(BacktraceData backtraceData, string hash) { //create new record and save it on hard drive var record = new BacktraceDatabaseRecord(backtraceData, _path) { Hash = hash }; record.Save(); return(record); }
public void MissingStackTraceReport_GenerateNotFaultingStackTrace_ReportShouldntHaveFaultingThread() { var message = "message"; // in this case BacktraceUnhandledException should generate environment stack trace var unhandledException = new BacktraceUnhandledException(message, string.Empty); Assert.IsNotEmpty(unhandledException.StackFrames); var report = new BacktraceReport(unhandledException); var data = new BacktraceData(report, null); Assert.IsFalse(data.ThreadData.ThreadInformations.First().Value.Fault); }
public IEnumerator Send(BacktraceData data, Action <BacktraceResult> callback = null) { LastData = data; if (callback != null) { callback.Invoke(new BacktraceResult() { Status = Types.BacktraceResultStatus.Ok }); } yield return(null); }
private BacktraceData AddAttributes(BacktraceData data) { #if UNITY_EDITOR bool isEditor = true; #else bool isEditor = false; #endif data.Attributes.Add("IsEditor", isEditor); data.Attributes.Add("BuildJob", "buildjob 123"); data.Attributes.Add("ChangeSetId", 12345); data.Attributes.Add("OnlineBackend", "Backend type X"); return(data); }
/// <summary> /// Add new record to database /// </summary> /// <param name="backtraceData">Diagnostic data that should be stored in database</param> /// <returns>New instance of DatabaseRecordy</returns> public BacktraceDatabaseRecord Add(BacktraceData backtraceData) { if (backtraceData == null) { throw new NullReferenceException(nameof(backtraceData)); } //create new record and save it on hard drive var record = new BacktraceDatabaseRecord(backtraceData, _path); record.Save(); //add record to database context return(Add(record)); }
/// <summary> /// Send a report to Backtrace API after first type of report validation rules /// </summary> /// <param name="report">Backtrace report</param> /// <param name="sendCallback">send callback</param> private void SendReport(BacktraceReport report, Action <BacktraceResult> sendCallback = null) { var record = Database != null?Database.Add(report, Attributes, MiniDumpType) : null; //create a JSON payload instance BacktraceData data = null; data = (record != null ? record.BacktraceData : null) ?? report.ToBacktraceData(Attributes); //valid user custom events data = (BeforeSend != null ? BeforeSend.Invoke(data) : null) ?? data; if (BacktraceApi == null) { if (record != null) { record.Dispose(); } Debug.LogWarning("Backtrace API doesn't exist. Please validate client token or server url!"); return; } StartCoroutine(BacktraceApi.Send(data, (BacktraceResult result) => { if (record != null) { record.Dispose(); //Database?.IncrementRecordRetryLimit(record); } if (result != null) { if (result.Status == BacktraceResultStatus.Ok) { if (Database != null) { Database.Delete(record); } } } //check if there is more errors to send //handle inner exception HandleInnerException(report, (BacktraceResult innerResult) => { result.InnerExceptionResult = innerResult; }); if (sendCallback != null) { sendCallback.Invoke(result); } })); }
/// <summary> /// Sending a diagnostic report data to server API. /// </summary> /// <param name="data">Diagnostic data</param> /// <returns>Server response</returns> public IEnumerator Send(BacktraceData data, Action <BacktraceResult> callback = null) { #pragma warning disable CS0618 // Type or member is obsolete if (RequestHandler != null) { yield return(RequestHandler.Invoke(ServerUrl, data)); } else if (data != null) { var json = data.ToJson(); yield return(Send(json, data.Attachments, data.Deduplication, callback)); } #pragma warning restore CS0618 // Type or member is obsolete }
public void TestShaComparision_Fingerprintavailable_DeduplicationReturnsFingerprint() { string fingerprint = "12345"; var exception = new Exception("test message"); var report = new BacktraceReport(exception) { Fingerprint = fingerprint }; var data = new BacktraceData(report, null); var deduplicationModel = new DeduplicationModel(data, DeduplicationStrategy.Default); var sha = deduplicationModel.GetSha(); Assert.AreEqual(sha, fingerprint); }
public void TestShaComparision_OnlyEmptyStackTrace_ReportWithFactorGenerateDifferentSha() { var report = new BacktraceReport(new Exception("test message")); var reportWithFactor = new BacktraceReport(new Exception("test message")) { Factor = "12345" }; var data = new BacktraceData(report, null); var dataWithFactor = new BacktraceData(reportWithFactor, null); var deduplicationModel = new DeduplicationModel(data, DeduplicationStrategy.Default); var comparer = new DeduplicationModel(dataWithFactor, DeduplicationStrategy.Default); Assert.AreNotEqual(deduplicationModel.GetSha(), comparer.GetSha()); }
public void TestShaComparisionWithMultipleOptionsAndReports_MultipleOptions_DeduplicationReturnsFingerprint(DeduplicationStrategy strategy) { var fingerprint = "12345"; var exception = new Exception("testMessage"); var report = new BacktraceReport(exception) { Fingerprint = fingerprint }; var data = new BacktraceData(report, null); var deduplicationModel = new DeduplicationModel(data, strategy); var sha = deduplicationModel.GetSha(); Assert.AreEqual(sha, fingerprint); }
public IEnumerator TestSourceCodeAssignment_DisabledUnhandledException_ShouldStoreUnhandledExceptionInfo() { BacktraceClient.Configuration.HandleUnhandledExceptions = false; BacktraceData lastData = null; BacktraceClient.BeforeSend = (BacktraceData data) => { lastData = data; return(data); }; //fake messages var fakeLogMessage = "log"; BacktraceClient.HandleUnityMessage(fakeLogMessage, string.Empty, LogType.Log); yield return(new WaitForEndOfFrame()); var fakeWarningMessage = "warning message"; BacktraceClient.HandleUnityMessage(fakeWarningMessage, string.Empty, LogType.Warning); yield return(new WaitForEndOfFrame()); // real exception var expectedExceptionMessage = "Exception message"; BacktraceClient.HandleUnityMessage(expectedExceptionMessage, string.Empty, LogType.Exception); yield return(new WaitForEndOfFrame()); Assert.IsNull(api.LastData); var expectedReportMessage = "Report message"; var report = new BacktraceReport(new Exception(expectedReportMessage)); BacktraceClient.Send(report); yield return(new WaitForEndOfFrame()); Assert.IsNotNull(lastData.SourceCode); var generatedText = lastData.SourceCode.Text; Assert.IsTrue(generatedText.Contains(expectedExceptionMessage)); Assert.IsTrue(generatedText.Contains(fakeLogMessage)); Assert.IsTrue(generatedText.Contains(fakeWarningMessage)); Assert.IsTrue(generatedText.Contains(expectedReportMessage)); }
/// <summary> /// Generate hash for current diagnostic data /// </summary> /// <param name="backtraceData">Diagnostic data </param> /// <returns>hash for current backtrace data</returns> private string GetHash(BacktraceData backtraceData) { var fingerprint = backtraceData == null ? string.Empty : backtraceData.Report.Fingerprint ?? string.Empty; if (!string.IsNullOrEmpty(fingerprint)) { return(fingerprint); } if (DeduplicationStrategy == DeduplicationStrategy.None) { return(string.Empty); } var deduplicationModel = new DeduplicationModel(backtraceData, DeduplicationStrategy); return(deduplicationModel.GetSha()); }
/// <summary> /// Get new database record /// </summary> /// <returns>Database record mock</returns> protected BacktraceDatabaseRecord GetRecord() { //mock single record var mockRecord = new Mock <BacktraceDatabaseRecord>(); mockRecord.Setup(n => n.Delete()); mockRecord.Setup(n => n.BacktraceData) .Returns(new BacktraceData(It.IsAny <BacktraceReport>(), It.IsAny <Dictionary <string, object> >())); mockRecord.Setup(n => n.Valid()) .Returns(true); var data = new BacktraceData(null, new Dictionary <string, object>()); mockRecord.SetupProperty(n => n.Record, data); mockRecord.Object.RecordWriter = new MockBacktraceDatabaseWriter(); return(mockRecord.Object); }