/// <summary> /// Reload Backtrace database configuration. Reloading configuration is required, when you change /// BacktraceDatabase configuration options. /// </summary> public void Reload() { // validate configuration if (Configuration == null) { Configuration = GetComponent <BacktraceClient>().Configuration; } if (Configuration == null || !Configuration.IsValid()) { Debug.LogWarning("Configuration doesn't exists or provided serverurl/token are invalid"); Enable = false; return; } //setup database object DatabaseSettings = new BacktraceDatabaseSettings(Configuration); Enable = Configuration.Enabled && BacktraceConfiguration.ValidateDatabasePath(Configuration.DatabasePath); if (!Enable) { Debug.LogWarning("Cannot initialize database - invalid database configuration. Database is disabled"); return; } CreateDatabaseDirectory(); SetupMultisceneSupport(); _lastConnection = Time.time; //Setup database context BacktraceDatabaseContext = new BacktraceDatabaseContext(DatabasePath, DatabaseSettings.RetryLimit, DatabaseSettings.RetryOrder, DatabaseSettings.DeduplicationStrategy); BacktraceDatabaseFileContext = new BacktraceDatabaseFileContext(DatabasePath, DatabaseSettings.MaxDatabaseSize, DatabaseSettings.MaxRecordCount); BacktraceApi = new BacktraceApi(Configuration.ToCredentials()); _reportLimitWatcher = new ReportLimitWatcher(Convert.ToUInt32(Configuration.ReportPerMin)); }
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); })); }
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); })); } }
public void Refresh() { if (Configuration == null || !Configuration.IsValid()) { return; } Enabled = true; Annotations.GameObjectDepth = Configuration.GameObjectDepth; HandleUnhandledExceptions(); _reportLimitWatcher = new ReportLimitWatcher(Convert.ToUInt32(Configuration.ReportPerMin)); #if UNITY_2018_4_OR_NEWER BacktraceApi = new BacktraceApi( credentials: new BacktraceCredentials(Configuration.GetValidServerUrl()), ignoreSslValidation: Configuration.IgnoreSslValidation); #else BacktraceApi = new BacktraceApi(new BacktraceCredentials(Configuration.GetValidServerUrl())); #endif if (Configuration.DestroyOnLoad == false) { DontDestroyOnLoad(gameObject); _instance = this; } Database = GetComponent <BacktraceDatabase>(); if (Database == null) { return; } Database.Reload(); Database.SetApi(BacktraceApi); Database.SetReportWatcher(_reportLimitWatcher); }
private void Awake() { Configuration = GetComponent <BacktraceClient>().Configuration; if (Configuration == null || !Configuration.IsValid()) { Debug.LogWarning("Configuration doesn't exists or provided serverurl/token are invalid"); _enable = false; return; } DatabaseSettings = new BacktraceDatabaseSettings(Configuration); if (DatabaseSettings == null) { _enable = false; return; } if (Configuration.CreateDatabase) { Directory.CreateDirectory(Configuration.DatabasePath); } _enable = Configuration.Enabled && BacktraceConfiguration.ValidateDatabasePath(Configuration.DatabasePath); if (!_enable) { return; } _lastConnection = Time.time; BacktraceDatabaseContext = new BacktraceDatabaseContext(DatabasePath, DatabaseSettings.RetryLimit, DatabaseSettings.RetryOrder); BacktraceDatabaseFileContext = new BacktraceDatabaseFileContext(DatabasePath, DatabaseSettings.MaxDatabaseSize, DatabaseSettings.MaxRecordCount); BacktraceApi = new BacktraceApi(Configuration.ToCredentials(), Convert.ToUInt32(Configuration.ReportPerMin)); }
public void Setup() { //prepare mock object //mock database var database = new Mock <IBacktraceDatabase>(); database.Setup(n => n.Add(It.IsAny <BacktraceReport>(), It.IsAny <Dictionary <string, object> >(), It.IsAny <MiniDumpType>())); database.Setup(n => n.Delete(It.IsAny <BacktraceDatabaseRecord>())); 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() }; //setup new client _backtraceClient = new BacktraceClient(credentials, database: database.Object) { BacktraceApi = api }; //Add new scoped attributes _backtraceClient.Attributes["ClientAttributeNumber"] = 1; _backtraceClient.Attributes["ClientAttributeString"] = "string attribute"; _backtraceClient.Attributes["ClientAttributeCustomClass"] = new { Name = "BacktraceIntegrationTest", Type = "Library" }; _backtraceClient.Attributes["ComplexObject"] = new Dictionary <string, Uri>() { { "backtrace.io", new Uri("http://backtrace.io") }, { "Google url", new Uri("http://google.com") } }; //to check if client report limit reached use OnClientReportLimitReached _backtraceClient.OnClientReportLimitReached = (BacktraceReport report) => { clientReportLimitReached = true; }; }
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> /// 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); } })); }
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); })); } }
protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { #if !NET35 BacktraceApi?.Dispose(); _timer?.Dispose(); #endif } _disposed = true; } }
public void Setup() { var credentials = new BacktraceCredentials("https://validurl.com/", "validToken"); var invalidCredentials = new BacktraceCredentials("https://validurl.com/", "invalidToken"); //mock API var serverUrl = $"{credentials.BacktraceHostUri.AbsoluteUri}post?format=json&token={credentials.Token}"; var invalidUrl = $"{credentials.BacktraceHostUri.AbsoluteUri}post?format=json&token={credentials.Token}"; var mockHttp = new MockHttpMessageHandler(); mockHttp.When(serverUrl) .Respond("application/json", "{'object' : 'aaa'}"); mockHttp.When(invalidUrl) .Respond("application/json", "{'message': 'invalid data'}"); var api = new BacktraceApi(credentials, 0) { HttpClient = mockHttp.ToHttpClient() }; var apiWithInvalidUrl = new BacktraceApi(invalidCredentials, 100) { HttpClient = mockHttp.ToHttpClient() }; //mock database var database = new Mock <IBacktraceDatabase>(); database.Setup(n => n.Add(It.IsAny <BacktraceReport>(), It.IsAny <Dictionary <string, object> >(), It.IsAny <MiniDumpType>())); database.Setup(n => n.Delete(It.IsAny <BacktraceDatabaseRecord>())); //setup new client _backtraceClient = new BacktraceClient(credentials, database: database.Object, reportPerMin: 0) { BacktraceApi = api, Database = database.Object }; _clientWithInvalidParameters = new BacktraceClient(invalidCredentials, database: database.Object, reportPerMin: 0) { BacktraceApi = apiWithInvalidUrl, Database = database.Object }; }
/// <summary> /// Send and asynchronous delete all records from database /// </summary> public async Task FlushAsync() { if (BacktraceApi == null) { throw new ArgumentException("BacktraceApi is required if you want to use Flush method"); } var record = BacktraceDatabaseContext?.FirstOrDefault(); while (record != null) { var backtraceData = record.BacktraceData; Delete(record); record = BacktraceDatabaseContext.FirstOrDefault(); if (backtraceData != null) { await BacktraceApi.SendAsync(backtraceData); } } }
/// <summary> /// Reload Backtrace database configuration. Reloading configuration is required, when you change /// BacktraceDatabase configuration options. /// </summary> public void Reload() { // validate configuration if (Configuration == null) { Configuration = GetComponent <BacktraceClient>().Configuration; } if (Instance != null) { return; } if (Configuration == null || !Configuration.IsValid()) { Enable = false; return; } #if UNITY_SWITCH Enable = false; #else Enable = Configuration.Enabled && InitializeDatabasePaths(); #endif if (!Enable) { if (Configuration.Enabled) { Debug.LogWarning("Cannot initialize database - invalid path to database. Database is disabled"); } return; } //setup database object DatabaseSettings = new BacktraceDatabaseSettings(DatabasePath, Configuration); SetupMultisceneSupport(); _lastConnection = Time.time; LastFrameTime = Time.time; //Setup database context BacktraceDatabaseContext = new BacktraceDatabaseContext(DatabaseSettings); BacktraceDatabaseFileContext = new BacktraceDatabaseFileContext(DatabaseSettings.DatabasePath, DatabaseSettings.MaxDatabaseSize, DatabaseSettings.MaxRecordCount); BacktraceApi = new BacktraceApi(Configuration.ToCredentials()); _reportLimitWatcher = new ReportLimitWatcher(Convert.ToUInt32(Configuration.ReportPerMin)); }
private async void OnTimedEventAsync(object source, ElapsedEventArgs e) { if (!BacktraceDatabaseContext.Any() || _timerBackgroundWork) { return; } _timerBackgroundWork = true; _timer.Stop(); //read first record (keep in mind LIFO and FIFO settings) from memory database var record = BacktraceDatabaseContext.FirstOrDefault(); while (record != null) { var backtraceData = record.BacktraceData; //meanwhile someone delete data from a disk if (backtraceData == null || backtraceData.Report == null) { Delete(record); } else { //send record from database to API var result = await BacktraceApi.SendAsync(backtraceData); if (result.Status == BacktraceResultStatus.Ok) { Delete(record); } else { record.Dispose(); BacktraceDatabaseContext.IncrementBatchRetry(); break; } } record = BacktraceDatabaseContext.FirstOrDefault(); } _timer.Start(); _timerBackgroundWork = false; }
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); })); }
public void Setup() { 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() }; //setup new client _backtraceClient = new BacktraceClient(credentials) { BacktraceApi = api }; }
public void Setup() { //prepare mock object 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(), //avoid real submission RequestHandler = (string host, string boundaryId, BacktraceData data) => { return(new BacktraceResult()); } }; //mock database var database = new Mock <IBacktraceDatabase>(); database.Setup(n => n.Add(It.IsAny <BacktraceReport>(), It.IsAny <Dictionary <string, object> >(), It.IsAny <MiniDumpType>())); database.Setup(n => n.Delete(It.IsAny <BacktraceDatabaseRecord>())); //setup new client _backtraceClient = new BacktraceClient(credentials, database: database.Object, reportPerMin: 0) { BacktraceApi = api }; }
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> /// 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); } })); }