Skip to content

Commit 571deee

Browse files
konraddysputjasoncdavis0
and
jasoncdavis0
authored
Feature/metrics (#38)
* Database improvements * Invalid meta file info * Updated for 3.0.2 * Version update * Store temporary json in memory * Basic metrics implementation * Backtrace API implementation * Use microsends + change helper to extension method * Replace http utility with stringBuilder to avoid compilation problems in older version of .NET scripting backend * Changelog + readme updates * Changelog udpate * yield return improvement + better comment * Report filter improvement * better return Co-authored-by: jasoncdavis0 <jason.davis@enterprisedb.com>
1 parent e8577b8 commit 571deee

14 files changed

+321
-64
lines changed

CHANGELOG.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ Sample code:
2323
For example, to only get error reporting for hangs or crashes then only return false for Hang or UnhandledException or set the corresponding options in the user interface as shown below.
2424

2525
![Sample report filter](./Documentation~/images/report-filter.PNG)
26-
26+
- Support for backtrace-unity timing observability. To enable sending performance information to Backtrace set the`Enable performance statistics` option in the UI. Attributes are created under the performance.* namespace, time unit is microseconds:
27+
* Report creation time (`performance.report`),
28+
* JSON serialization time (`performance.json`),
29+
* Database add operation time (`performance.database`),
30+
* Database single send method time (`performance.send`),
31+
* Database single flush method time (`performance.flush`)
32+
2733

2834
## Version 3.0.2
2935
- `BacktraceDatabase` now provides a new `Send` method. This method will try to send all objects from the database respecting the client side deduplication and retry setting. This can be used as an alternative to the `Flush` method which will try to send all objects from the database ignoring any client side deduplication and retry settings.

Editor/BacktraceConfigurationEditor.cs

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public override void OnInspectorGUI()
6363
serializedObject.FindProperty("NumberOfLogs"),
6464
new GUIContent(BacktraceConfigurationLabels.LABEL_NUMBER_OF_LOGS));
6565

66+
EditorGUILayout.PropertyField(
67+
serializedObject.FindProperty("PerformanceStatistics"),
68+
new GUIContent(BacktraceConfigurationLabels.LABEL_PERFORMANCE_STATISTICS));
69+
6670
EditorGUILayout.PropertyField(
6771
serializedObject.FindProperty("DestroyOnLoad"),
6872
new GUIContent(BacktraceConfigurationLabels.LABEL_DESTROY_CLIENT_ON_SCENE_LOAD));

Editor/BacktraceConfigurationLabels.cs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal static class BacktraceConfigurationLabels
1515
internal static string LABEL_IGNORE_SSL_VALIDATION = "Ignore SSL validation";
1616
internal static string LABEL_SEND_UNHANDLED_GAME_CRASHES_ON_STARTUP= "Send unhandled native game crashes on startup";
1717
internal static string LABEL_USE_NORMALIZED_EXCEPTION_MESSAGE = "Use normalized exception message";
18+
internal static string LABEL_PERFORMANCE_STATISTICS = "Enable performance statistics";
1819

1920
// database labels
2021
internal static string LABEL_ENABLE_DATABASE = "Enable Database";

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ The following is a reference guide to the Backtrace Client fields:
7272
- Handle unhandled exceptions: Toggle this on or off to set the library to handle unhandled exceptions that are not captured by try-catch blocks.
7373
- Game Object Depth Limit: Allows developer to filter number of game object childrens in Backtrace report.
7474
- Collect last n game logs: Collect last n number of logs generated by game.
75+
- Enabled performance statistics: Allows `BacktraceClient` to measure execution time and include performance information as report attributes.
7576
- Ignore SSL validation: Unity by default will validate ssl certificates. By using this option you can avoid ssl certificates validation. However, if you don't need to ignore ssl validation, please set this option to false.
7677
- Handle ANR (Application not responding) - this options is available only in Android build. It allows to catch ANR (application not responding) events happened to your game in Android devices. In this release, ANR is set to detect after 5 seconds. This will be configurable in a future release.
7778
- Enable Database: When this setting is toggled, the backtrace-unity plugin will configure an offline database that will store reports if they can't be submitted do to being offline or not finding a network. When toggled on, there are a number of Database settings to configure.

Runtime/BacktraceClient.cs

+76-22
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,24 @@ public Action<BacktraceReport> OnClientReportLimitReached
155155

156156
private INativeClient _nativeClient;
157157

158+
public bool EnablePerformanceStatistics
159+
{
160+
get
161+
{
162+
return Configuration.PerformanceStatistics;
163+
}
164+
}
165+
166+
public int GameObjectDepth
167+
{
168+
get
169+
{
170+
return Configuration.GameObjectDepth == 0
171+
? 16 // default maximum game object size
172+
: Configuration.GameObjectDepth;
173+
}
174+
}
175+
158176

159177
/// <summary>
160178
/// Instance of BacktraceApi that allows to send data to Backtrace API
@@ -193,8 +211,6 @@ internal ReportLimitWatcher ReportLimitWatcher
193211
}
194212
}
195213

196-
private int _gameObjectDepth = 0;
197-
198214
private BacktraceLogManager _backtraceLogManager;
199215

200216
public void OnDisable()
@@ -211,22 +227,21 @@ public void Refresh()
211227

212228
Enabled = true;
213229

214-
// set maximum game object depth
215-
_gameObjectDepth = Configuration.GameObjectDepth == 0
216-
? 16 // default maximum game object size
217-
: Configuration.GameObjectDepth;
218-
219230
CaptureUnityMessages();
220231
_reportLimitWatcher = new ReportLimitWatcher(Convert.ToUInt32(Configuration.ReportPerMin));
221232

222-
#if UNITY_2018_4_OR_NEWER
223233

224234
BacktraceApi = new BacktraceApi(
225235
credentials: new BacktraceCredentials(Configuration.GetValidServerUrl()),
226-
ignoreSslValidation: Configuration.IgnoreSslValidation);
236+
237+
#if UNITY_2018_4_OR_NEWER
238+
ignoreSslValidation: Configuration.IgnoreSslValidation
227239
#else
228-
BacktraceApi = new BacktraceApi(new BacktraceCredentials(Configuration.GetValidServerUrl()));
240+
ignoreSslValidation: false
229241
#endif
242+
);
243+
BacktraceApi.EnablePerformanceStatistics = Configuration.PerformanceStatistics;
244+
230245
if (!Configuration.DestroyOnLoad)
231246
{
232247
DontDestroyOnLoad(gameObject);
@@ -286,6 +301,7 @@ public void Send(string message, List<string> attachmentPaths = null, Dictionary
286301
attachmentPaths: attachmentPaths,
287302
attributes: attributes);
288303
_backtraceLogManager.Enqueue(report);
304+
289305
SendReport(report);
290306
}
291307

@@ -301,6 +317,7 @@ public void Send(Exception exception, List<string> attachmentPaths = null, Dicti
301317
{
302318
return;
303319
}
320+
304321
var report = new BacktraceReport(exception, attributes, attachmentPaths);
305322
_backtraceLogManager.Enqueue(report);
306323
SendReport(report);
@@ -344,8 +361,19 @@ private void SendReport(BacktraceReport report, Action<BacktraceResult> sendCall
344361
/// <returns>IEnumerator</returns>
345362
private IEnumerator CollectDataAndSend(BacktraceReport report, Action<BacktraceResult> sendCallback = null)
346363
{
364+
var queryAttributes = new Dictionary<string, string>();
365+
var stopWatch = EnablePerformanceStatistics
366+
? System.Diagnostics.Stopwatch.StartNew()
367+
: new System.Diagnostics.Stopwatch();
368+
347369
BacktraceData data = SetupBacktraceData(report);
348-
yield return new WaitForEndOfFrame();
370+
371+
if (EnablePerformanceStatistics)
372+
{
373+
stopWatch.Stop();
374+
queryAttributes["performance.report"] = stopWatch.GetMicroseconds();
375+
}
376+
349377
if (BeforeSend != null)
350378
{
351379
data = BeforeSend.Invoke(data);
@@ -356,38 +384,64 @@ private IEnumerator CollectDataAndSend(BacktraceReport report, Action<BacktraceR
356384
}
357385
BacktraceDatabaseRecord record = null;
358386

359-
// avoid serializing data twice
360-
// if record is here we should try to send json data that are available in record
361-
// otherwise we can still use BacktraceData.ToJson().
362-
string json = string.Empty;
363387
if (Database != null)
364388
{
365389
yield return new WaitForEndOfFrame();
390+
if (EnablePerformanceStatistics)
391+
{
392+
stopWatch.Restart();
393+
}
366394
record = Database.Add(data);
367395
// handle situation when database refuse to store report.
368396
if (record != null)
369397
{
370398
//Extend backtrace data with additional attachments from backtrace database
371399
data = record.BacktraceData;
400+
if (EnablePerformanceStatistics)
401+
{
402+
stopWatch.Stop();
403+
queryAttributes["performance.database"] = stopWatch.GetMicroseconds();
404+
}
405+
406+
372407
if (record.Duplicated)
373408
{
374409
yield break;
375410
}
376-
json = record.BacktraceDataJson();
377411
}
378412
}
379-
if (string.IsNullOrEmpty(json))
413+
414+
yield return new WaitForEndOfFrame();
415+
if (EnablePerformanceStatistics)
416+
{
417+
stopWatch.Restart();
418+
}
419+
// avoid serializing data twice
420+
// if record is here we should try to send json data that are available in record
421+
// otherwise we can still use BacktraceData.ToJson().
422+
string json = record != null
423+
? record.BacktraceDataJson()
424+
: data.ToJson();
425+
426+
if (EnablePerformanceStatistics)
380427
{
381-
json = data.ToJson();
428+
stopWatch.Stop();
429+
queryAttributes["performance.json"] = stopWatch.GetMicroseconds();
382430
}
431+
383432
//backward compatibility
384433
if (RequestHandler != null)
385434
{
386435
yield return RequestHandler.Invoke(BacktraceApi.ServerUrl, data);
387436
yield break;
388437
}
389438

390-
StartCoroutine(BacktraceApi.Send(json, data.Attachments, data.Deduplication, (BacktraceResult result) =>
439+
if (data.Deduplication != 0)
440+
{
441+
queryAttributes["_mod_duplicate"] = data.Deduplication.ToString();
442+
}
443+
444+
StartCoroutine(BacktraceApi.Send(json, data.Attachments, queryAttributes, (BacktraceResult result) =>
391445
{
392446
if (record != null)
393447
{
@@ -418,6 +472,7 @@ record = Database.Add(data);
418472
/// <returns>Backtrace data</returns>
419473
private BacktraceData SetupBacktraceData(BacktraceReport report)
420474
{
475+
421476
// apply _mod fingerprint attribute when client should use
422477
// normalized exception message instead environment stack trace
423478
// for exceptions without stack trace.
@@ -440,8 +495,7 @@ private BacktraceData SetupBacktraceData(BacktraceReport report)
440495
reportAttributes = _nativeClient.GetAttributes();
441496
}
442497

443-
var data = report.ToBacktraceData(reportAttributes, _gameObjectDepth);
444-
return data;
498+
return report.ToBacktraceData(reportAttributes, GameObjectDepth);
445499
}
446500

447501
#if UNITY_ANDROID
@@ -619,7 +673,7 @@ private bool ValidClientConfiguration()
619673
/// <returns>true if client should skip report. Otherwise false.</returns>
620674
private bool ShouldSkipReport(ReportFilterType type, Exception exception, string message)
621675
{
622-
return Configuration.ReportFilterType == type
676+
return Configuration.ReportFilterType.HasFlag(type)
623677
|| (SkipReport != null && SkipReport.Invoke(type, exception, message));
624678

625679
}

Runtime/BacktraceDatabase.cs

+31-5
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public DeduplicationStrategy DeduplicationStrategy
106106
/// </summary>
107107
public void Reload()
108108
{
109-
109+
110110
// validate configuration
111111
if (Configuration == null)
112112
{
@@ -257,7 +257,7 @@ public BacktraceDatabaseRecord Add(BacktraceData data, bool @lock = true)
257257
return null;
258258
}
259259
var record = BacktraceDatabaseContext.Add(data);
260-
if(!@lock)
260+
if (!@lock)
261261
{
262262
record.Dispose();
263263
}
@@ -325,7 +325,7 @@ public void Flush()
325325
/// </summary>
326326
public void Send()
327327
{
328-
if(!Enable || !BacktraceDatabaseContext.Any())
328+
if (!Enable || !BacktraceDatabaseContext.Any())
329329
{
330330
return;
331331
}
@@ -339,14 +339,28 @@ private void FlushRecord(BacktraceDatabaseRecord record)
339339
{
340340
return;
341341
}
342+
var stopWatch = Configuration.PerformanceStatistics
343+
? new System.Diagnostics.Stopwatch()
344+
: System.Diagnostics.Stopwatch.StartNew();
345+
342346
var backtraceData = record.BacktraceDataJson();
343347
Delete(record);
348+
var queryAttributes = new Dictionary<string, string>();
349+
if (Configuration.PerformanceStatistics)
350+
{
351+
stopWatch.Stop();
352+
queryAttributes["performance.database.flush"] = stopWatch.GetMicroseconds();
353+
}
354+
344355
if (backtraceData == null)
345356
{
346357
return;
347358
}
359+
360+
queryAttributes["_mod_duplicate"] = record.Count.ToString();
361+
348362
StartCoroutine(
349-
BacktraceApi.Send(backtraceData, record.Attachments, record.Count, (BacktraceResult result) =>
363+
BacktraceApi.Send(backtraceData, record.Attachments, queryAttributes, (BacktraceResult result) =>
350364
{
351365
record = BacktraceDatabaseContext.FirstOrDefault();
352366
FlushRecord(record);
@@ -355,6 +369,10 @@ record = BacktraceDatabaseContext.FirstOrDefault();
355369

356370
private void SendData(BacktraceDatabaseRecord record)
357371
{
372+
var stopWatch = Configuration.PerformanceStatistics
373+
? System.Diagnostics.Stopwatch.StartNew()
374+
: new System.Diagnostics.Stopwatch();
375+
358376
var backtraceData = record != null ? record.BacktraceDataJson() : null;
359377
//check if report exists on hard drive
360378
// to avoid situation when someone manually remove data
@@ -364,8 +382,16 @@ private void SendData(BacktraceDatabaseRecord record)
364382
}
365383
else
366384
{
385+
var queryAttributes = new Dictionary<string, string>();
386+
if (Configuration.PerformanceStatistics)
387+
{
388+
stopWatch.Stop();
389+
queryAttributes["performance.database.send"] = stopWatch.GetMicroseconds();
390+
}
391+
queryAttributes["_mod_duplicate"] = record.Count.ToString();
392+
367393
StartCoroutine(
368-
BacktraceApi.Send(backtraceData, record.Attachments, record.Count, (BacktraceResult sendResult) =>
394+
BacktraceApi.Send(backtraceData, record.Attachments, queryAttributes, (BacktraceResult sendResult) =>
369395
{
370396
if (sendResult.Status == BacktraceResultStatus.Ok)
371397
{

Runtime/Common/MetricsHelper.cs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Diagnostics;
3+
4+
namespace Backtrace.Unity.Common
5+
{
6+
internal static class MetricsHelper
7+
{
8+
/// <summary>
9+
/// Get performance info from stopwatch in micros
10+
/// </summary>
11+
/// <param name="stopwatch">Stop watch</param>
12+
/// <returns>Elapsed time in μs</returns>
13+
public static string GetMicroseconds(this Stopwatch stopwatch)
14+
{
15+
var elapsedTime = ((stopwatch.ElapsedTicks * 1000000) / Stopwatch.Frequency);
16+
return Math.Max(1, elapsedTime).ToString();
17+
}
18+
}
19+
}

Runtime/Common/MetricsHelper.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)