/// <summary> /// Delete existing record from database /// </summary> /// <param name="record">Database records to delete</param> public void Delete(BacktraceDatabaseRecord record) { if (record == null) { return; } for (int keyIndex = 0; keyIndex < BatchRetry.Keys.Count; keyIndex++) { var key = BatchRetry.Keys.ElementAt(keyIndex); for (int batchIndex = 0; batchIndex < BatchRetry[key].Count; batchIndex++) { var value = BatchRetry[key].ElementAt(batchIndex); if (value.Id == record.Id) { //delete value from hard drive value.Delete(); //delete value from current batch BatchRetry[key].Remove(value); //decrement all records TotalRecords--; //decrement total size of database TotalSize -= value.Size; System.Diagnostics.Debug.WriteLine($"[Delete] :: Total Size = {TotalSize}"); return; } } } return; }
private void FlushRecord(BacktraceDatabaseRecord record) { if (record == null) { return; } var stopWatch = Configuration.PerformanceStatistics ? new System.Diagnostics.Stopwatch() : System.Diagnostics.Stopwatch.StartNew(); var backtraceData = record.BacktraceDataJson(); Delete(record); var queryAttributes = new Dictionary <string, string>(); if (Configuration.PerformanceStatistics) { stopWatch.Stop(); queryAttributes["performance.database.flush"] = stopWatch.GetMicroseconds(); } if (backtraceData == null) { return; } queryAttributes["_mod_duplicate"] = record.Count.ToString(); StartCoroutine( BacktraceApi.Send(backtraceData, record.Attachments, queryAttributes, (BacktraceResult result) => { record = BacktraceDatabaseContext.FirstOrDefault(); FlushRecord(record); })); }
/// <summary> /// Saves BacktraceDatabaseRecord on the hard drive /// </summary> /// <param name="record">BacktraceDatabaseRecord</param> /// <returns>true if file context was able to save data on the hard drive. Otherwise false</returns> public bool Save(BacktraceDatabaseRecord record) { try { var jsonPrefix = record.BacktraceData.UuidString; record.DiagnosticDataJson = record.BacktraceData.ToJson(); record.DiagnosticDataPath = Path.Combine(_path, string.Format("{0}-attachment.json", jsonPrefix)); record.Size += Save(record.DiagnosticDataJson, record.DiagnosticDataPath); // update record size based on the attachment information if (record.Attachments != null && record.Attachments.Count != 0) { foreach (var attachment in record.Attachments) { if (IsDatabaseDependency(attachment)) { record.Size += new FileInfo(attachment).Length; } } } record.RecordPath = Path.Combine(_path, string.Format("{0}-record.json", jsonPrefix)); var recordJson = record.ToJson(); record.Size += UTF8Encoding.Unicode.GetByteCount(recordJson); Save(recordJson, record.RecordPath); return(true); } catch (Exception e) { Debug.LogWarning(string.Format("Backtrace: Cannot save record on the hard drive. Reason: {0}", e.Message)); Delete(record); return(false); } }
/// <summary> /// Delete single record from database /// </summary> /// <param name="record">Record to delete</param> public void Delete(BacktraceDatabaseRecord record) { if (BacktraceDatabaseContext != null) { BacktraceDatabaseContext.Delete(record); } }
/// <summary> /// Load all records stored in database path /// </summary> protected virtual void LoadReports() { if (!Enable) { return; } var files = BacktraceDatabaseFileContext.GetRecords(); foreach (var file in files) { var record = BacktraceDatabaseRecord.ReadFromFile(file); if (record == null) { continue; } record.DatabasePath(DatabaseSettings.DatabasePath); if (!record.Valid()) { try { Debug.Log("Removing record from Backtrace Database path - invalid record."); record.Delete(); } catch (Exception) { Debug.LogWarning(string.Format("Cannot remove file from database. File name: {0}", file.FullName)); } continue; } BacktraceDatabaseContext.Add(record); ValidateDatabaseSize(); record.Unlock(); } }
/// <summary> /// Delete existing record from database /// </summary> /// <param name="record">Database records to delete</param> public void Delete(BacktraceDatabaseRecord record) { if (record == null) { return; } for (int keyIndex = 0; keyIndex < BatchRetry.Keys.Count; keyIndex++) { var key = BatchRetry.Keys.ElementAt(keyIndex); for (int batchIndex = 0; batchIndex < BatchRetry[key].Count; batchIndex++) { var value = BatchRetry[key].ElementAt(batchIndex); if (value.Id == record.Id) { //delete value from hard drive value.Delete(); //delete value from current batch BatchRetry[key].Remove(value); //decrement all records if (value.Count > 0) { TotalRecords = TotalRecords - value.Count; } else { TotalRecords--; } //decrement total size of database TotalSize -= value.Size; return; } } } }
private void SendData(BacktraceDatabaseRecord record) { var backtraceData = record?.BacktraceData; //meanwhile someone delete data from a disk if (backtraceData == null || backtraceData.Report == null) { Delete(record); } else { StartCoroutine( BacktraceApi.Send(backtraceData, (BacktraceResult sendResult) => { if (sendResult.Status == BacktraceResultStatus.Ok) { Delete(record); } else { record.Dispose(); BacktraceDatabaseContext.IncrementBatchRetry(); } record = BacktraceDatabaseContext.FirstOrDefault(); SendData(record); })); } }
/// <summary> /// Load all records stored in database path /// </summary> protected virtual void LoadReports() { if (!Enable) { return; } var files = BacktraceDatabaseFileContext.GetRecords(); foreach (var file in files) { var record = BacktraceDatabaseRecord.ReadFromFile(file); if (record == null) { continue; } if (!BacktraceDatabaseFileContext.IsValidRecord(record)) { BacktraceDatabaseFileContext.Delete(record); continue; } BacktraceDatabaseContext.Add(record); ValidateDatabaseSize(); record.Unlock(); } }
public void Delete(BacktraceDatabaseRecord record) { if (OnDelete != null) { OnDelete.Invoke(record); } return; }
public bool IsValidRecord(BacktraceDatabaseRecord record) { if (OnValidRecord != null) { return(OnValidRecord.Invoke(record)); } return(false); }
/// <summary> /// Determine if BacktraceDatabaseRecord is valid. /// </summary> /// <param name="record">Database record</param> /// <returns>True, if the record exists. Otherwise false.</returns> public bool IsValidRecord(BacktraceDatabaseRecord record) { if (record == null) { return(false); } return(File.Exists(record.DiagnosticDataPath)); }
public bool Save(BacktraceDatabaseRecord record) { if (OnSave != null) { return(OnSave.Invoke(record)); } return(true); }
/// <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 Setup() { _lastRecord = GetRecord(); //get project path string projectPath = Path.GetTempPath(); //setup credentials var credentials = new BacktraceCredentials("https://validurl.com/", "validToken"); //mock api var serverUrl = $"{credentials.BacktraceHostUri.AbsoluteUri}post?format=json&token={credentials.Token}"; var mockHttp = new MockHttpMessageHandler(); mockHttp.When(serverUrl) .Respond("application/json", "{'object' : 'aaa'}"); var api = new BacktraceApi(credentials, 0) { HttpClient = mockHttp.ToHttpClient() }; //mock file context var mockFileContext = new Mock <IBacktraceDatabaseFileContext>(); mockFileContext.Setup(n => n.GetRecords()) .Returns(new List <FileInfo>()); mockFileContext.Setup(n => n.RemoveOrphaned(It.IsAny <IEnumerable <BacktraceDatabaseRecord> >())); //mock cache var mockCacheContext = new Mock <IBacktraceDatabaseContext>(); mockCacheContext.Setup(n => n.Add(It.IsAny <BacktraceData>(), MiniDumpType.None)) .Callback(() => { mockCacheContext.Object.Add(_lastRecord); _lastRecord = GetRecord(); }) .Returns(_lastRecord); var database = new BacktraceDatabase(new BacktraceDatabaseSettings(projectPath) { RetryBehavior = RetryBehavior.NoRetry }) { BacktraceDatabaseContext = mockCacheContext.Object, BacktraceDatabaseFileContext = mockFileContext.Object, }; //setup new client _backtraceClient = new BacktraceClient(backtraceCredentials: credentials, database: database, reportPerMin: 0) { BacktraceApi = api }; }
/// <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)); }
private void SendData(BacktraceDatabaseRecord record) { if (record == null) { return; } var stopWatch = Configuration.PerformanceStatistics ? System.Diagnostics.Stopwatch.StartNew() : new System.Diagnostics.Stopwatch(); var backtraceData = record != null?record.BacktraceDataJson() : null; //check if report exists on hard drive // to avoid situation when someone manually remove data if (string.IsNullOrEmpty(backtraceData)) { Delete(record); } else { var queryAttributes = new Dictionary <string, string>(); if (Configuration.PerformanceStatistics) { stopWatch.Stop(); queryAttributes["performance.database.send"] = stopWatch.GetMicroseconds(); } queryAttributes["_mod_duplicate"] = record.Count.ToString(CultureInfo.InvariantCulture); StartCoroutine( BacktraceApi.Send(backtraceData, record.Attachments, queryAttributes, (BacktraceResult sendResult) => { record.Unlock(); if (sendResult.Status != BacktraceResultStatus.ServerError && sendResult.Status != BacktraceResultStatus.NetworkError) { Delete(record); } else { IncrementBatchRetry(); return; } bool shouldProcess = _reportLimitWatcher.WatchReport(DateTimeHelper.Timestamp()); if (!shouldProcess) { return; } record = BacktraceDatabaseContext.FirstOrDefault(); SendData(record); })); } }
/// <summary> /// Deletes backtrace database record from persistent data storage /// </summary> /// <param name="record">Database record</param> public void Delete(BacktraceDatabaseRecord record) { // remove json objects Delete(record.DiagnosticDataPath); Delete(record.RecordPath); //remove database attachments if (record.Attachments == null || record.Attachments.Count == 0) { return; } // remove associated attachments foreach (var attachment in record.Attachments) { Delete(attachment); } }
/// <summary> /// Add existing record to database /// </summary> /// <param name="backtraceRecord">Database record</param> public BacktraceDatabaseRecord Add(BacktraceDatabaseRecord backtraceRecord) { if (backtraceRecord == null) { throw new NullReferenceException(nameof(BacktraceDatabaseRecord)); } //lock record, because Add method returns record backtraceRecord.Locked = true; //increment total size of database TotalSize += backtraceRecord.Size; //add record to first batch BatchRetry[0].Add(backtraceRecord); //increment total records TotalRecords++; return(backtraceRecord); }
/// <summary> /// Load all records stored in database path /// </summary> private void LoadReports() { var files = BacktraceDatabaseFileContext.GetRecords(); foreach (var file in files) { var record = BacktraceDatabaseRecord.ReadFromFile(file); if (!record.Valid()) { record.Delete(); continue; } BacktraceDatabaseContext.Add(record); ValidateDatabaseSize(); record.Dispose(); } }
private void FlushRecord(BacktraceDatabaseRecord record) { if (record == null) { return; } var backtraceData = record.BacktraceData; Delete(record); if (backtraceData == null) { return; } StartCoroutine( BacktraceApi.Send(backtraceData, (BacktraceResult result) => { record = BacktraceDatabaseContext.FirstOrDefault(); FlushRecord(record); })); }
/// <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 virtual BacktraceDatabaseRecord Add(BacktraceData backtraceData, MiniDumpType miniDumpType = MiniDumpType.None) { if (backtraceData == null) { throw new NullReferenceException(nameof(backtraceData)); } string hash = GetHash(backtraceData); if (!string.IsNullOrEmpty(hash)) { var existRecord = BatchRetry.SelectMany(n => n.Value) .FirstOrDefault(n => n.Hash == hash); if (existRecord != null) { existRecord.Locked = true; existRecord.Increment(); TotalRecords++; return(existRecord); } } string minidumpPath = GenerateMiniDump(backtraceData.Report, miniDumpType); backtraceData.Report.SetMinidumpPath(minidumpPath); backtraceData.Attachments.Add(minidumpPath); //create new record and save it on hard drive var record = new BacktraceDatabaseRecord(backtraceData, _path) { Hash = hash }; record.Save(); //add record to database context return(Add(record)); }
/// <summary> /// Load all records stored in database path /// </summary> private void LoadReports() { var files = BacktraceDatabaseFileContext.GetRecords(); foreach (var file in files) { var record = BacktraceDatabaseRecord.ReadFromFile(file); if (!record.Valid()) { try { record.Delete(); } catch (Exception) { Debug.LogWarning($"Cannot remove file from database. File name: {file.FullName}"); } continue; } BacktraceDatabaseContext.Add(record); ValidateDatabaseSize(); record.Dispose(); } }
private void SendData(BacktraceDatabaseRecord record) { var backtraceData = record != null ? record.BacktraceData : null; //check if report exists on hard drive // to avoid situation when someone manually remove data if (backtraceData == null || backtraceData.Report == null) { Delete(record); } else { StartCoroutine( BacktraceApi.Send(backtraceData, (BacktraceResult sendResult) => { if (sendResult.Status == BacktraceResultStatus.Ok) { Delete(record); } else { record.Dispose(); BacktraceDatabaseContext.IncrementBatchRetry(); return; } bool limitHit = _reportLimitWatcher.WatchReport(new DateTime().Timestamp()); if (!limitHit) { _reportLimitWatcher.DisplayReportLimitHitMessage(); return; } record = BacktraceDatabaseContext.FirstOrDefault(); SendData(record); })); } }
/// <summary> /// Delete single record from database /// </summary> /// <param name="record">Record to delete</param> public void Delete(BacktraceDatabaseRecord record) { BacktraceDatabaseContext?.Delete(record); }
/// <summary> /// Add new report to BacktraceDatabase /// </summary> public BacktraceDatabaseRecord Add(BacktraceData data, bool @lock = true) { if (data == null || !Enable) { return(null); } //remove old reports (if database is full) //and check database health state var validationResult = ValidateDatabaseSize(); if (!validationResult) { return(null); } // validate if record already exists in the database object var hash = BacktraceDatabaseContext.GetHash(data); if (!string.IsNullOrEmpty(hash)) { var existingRecord = BacktraceDatabaseContext.GetRecordByHash(hash); if (existingRecord != null) { BacktraceDatabaseContext.AddDuplicate(existingRecord); return(existingRecord); } } //add built-in attachments var attachments = BacktraceDatabaseFileContext.GenerateRecordAttachments(data); for (int attachmentIndex = 0; attachmentIndex < attachments.Count(); attachmentIndex++) { if (!string.IsNullOrEmpty(attachments.ElementAt(attachmentIndex))) { data.Attachments.Add(attachments.ElementAt(attachmentIndex)); } } // add to fresh new record breadcrumb attachment if (Breadcrumbs != null) { data.Attachments.Add(Breadcrumbs.GetBreadcrumbLogPath()); data.Attributes.Attributes["breadcrumbs.lastId"] = Breadcrumbs.BreadcrumbId().ToString("F0", CultureInfo.InvariantCulture); } // now we now we're adding new unique report to database var record = new BacktraceDatabaseRecord(data) { Hash = hash }; // save record on the hard drive and add it to database context var saveResult = BacktraceDatabaseFileContext.Save(record); if (!saveResult) { // file context won't remove json object that wasn't stored in the previous method // but will clean up attachments associated with this record. BacktraceDatabaseFileContext.Delete(record); return(null); } BacktraceDatabaseContext.Add(record); if (!@lock) { record.Unlock(); } return(record); }
/// <summary> /// Collect diagnostic data and send to API /// </summary> /// <param name="report">Backtrace Report</param> /// <param name="sendCallback">Coroutine callback</param> /// <returns>IEnumerator</returns> private IEnumerator CollectDataAndSend(BacktraceReport report, Action <BacktraceResult> sendCallback) { var queryAttributes = new Dictionary <string, string>(); var stopWatch = EnablePerformanceStatistics ? System.Diagnostics.Stopwatch.StartNew() : new System.Diagnostics.Stopwatch(); BacktraceData data = SetupBacktraceData(report); if (EnablePerformanceStatistics) { stopWatch.Stop(); queryAttributes["performance.report"] = stopWatch.GetMicroseconds(); } if (BeforeSend != null) { data = BeforeSend.Invoke(data); if (data == null) { yield break; } } BacktraceDatabaseRecord record = null; if (Database != null && Database.Enabled()) { yield return(new WaitForEndOfFrame()); if (EnablePerformanceStatistics) { stopWatch.Restart(); } record = Database.Add(data); // handle situation when database refuse to store report. if (record != null) { //Extend backtrace data with additional attachments from backtrace database data = record.BacktraceData; if (EnablePerformanceStatistics) { stopWatch.Stop(); queryAttributes["performance.database"] = stopWatch.GetMicroseconds(); } if (record.Duplicated) { record.Unlock(); yield break; } } else { yield break; } } if (EnablePerformanceStatistics) { stopWatch.Restart(); } // avoid serializing data twice // if record is here we should try to send json data that are available in record // otherwise we can still use BacktraceData.ToJson(). string json = record != null ? record.BacktraceDataJson() : data.ToJson(); if (EnablePerformanceStatistics) { stopWatch.Stop(); queryAttributes["performance.json"] = stopWatch.GetMicroseconds(); } yield return(new WaitForEndOfFrame()); if (string.IsNullOrEmpty(json)) { yield break; } //backward compatibility if (RequestHandler != null) { yield return(RequestHandler.Invoke(BacktraceApi.ServerUrl, data)); yield break; } if (data.Deduplication != 0) { queryAttributes["_mod_duplicate"] = data.Deduplication.ToString(); } StartCoroutine(BacktraceApi.Send(json, data.Attachments, queryAttributes, (BacktraceResult result) => { if (record != null) { record.Unlock(); if (Database != null && result.Status != BacktraceResultStatus.ServerError && result.Status != BacktraceResultStatus.NetworkError) { Database.Delete(record); } } //check if there is more errors to send //handle inner exception HandleInnerException(report); if (sendCallback != null) { sendCallback.Invoke(result); } })); }
/// <summary> /// Check if any record exists /// </summary> /// <param name="record"></param> /// <returns></returns> public bool Any(BacktraceDatabaseRecord record) { return(BatchRetry.SelectMany(n => n.Value).Any(n => n.Id == record.Id)); }
public void Delete(BacktraceDatabaseRecord record) { OnDelete?.Invoke(record); return; }
public bool IsValidRecord(BacktraceDatabaseRecord record) { return(OnValidRecord?.Invoke(record) ?? true); }
public bool Save(BacktraceDatabaseRecord record) { return(OnSave?.Invoke(record) ?? true); }