Skip to content

Commit c95d8cc

Browse files
Mobile Ads Developer Relationscopybara-github
authored andcommitted
Refactor exception handler to a RCS client.
PiperOrigin-RevId: 822217283
1 parent 1c881a0 commit c95d8cc

File tree

2 files changed

+218
-195
lines changed

2 files changed

+218
-195
lines changed

source/plugin/Assets/GoogleMobileAds/Common/GlobalExceptionHandler.cs

Lines changed: 50 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,52 @@
88

99
namespace GoogleMobileAds.Common
1010
{
11+
#region Exception payload definition
12+
[Serializable]
13+
public struct ExceptionLoggablePayload
14+
{
15+
public ExceptionReport unity_gma_sdk_exception_message;
16+
}
17+
18+
/// <summary>
19+
/// A data structure to hold all relevant info for a single exception event.
20+
/// </summary>
21+
[Serializable]
22+
public class ExceptionReport
23+
{
24+
// JSPB compatibility: 64-bit integers must be sent as strings to avoid precision loss.
25+
public string time_msec;
26+
public bool trapped;
27+
public string name;
28+
public string exception_class;
29+
public string top_exception;
30+
public string stacktrace;
31+
public string stacktrace_hash;
32+
33+
// Static metadata.
34+
public string session_id;
35+
public string app_id;
36+
public string app_version_name;
37+
public string platform;
38+
public string unity_version;
39+
public string os_version;
40+
public string device_model;
41+
public string country;
42+
public int total_cpu;
43+
public string total_memory_bytes;
44+
45+
// Dynamic metadata.
46+
public string network_type;
47+
public string orientation;
48+
}
49+
#endregion
50+
1151
/// <summary>
1252
/// A persistent singleton that captures all trapped and untrapped C# exceptions.
1353
/// It enriches them with device metadata and sends them in batches to a backend service (RCS)
1454
/// based on either a count or time threshold.
1555
/// </summary>
16-
public class GlobalExceptionHandler : MonoBehaviour
56+
public class GlobalExceptionHandler : RcsClient<ExceptionReport>
1757
{
1858
private static GlobalExceptionHandler _instance;
1959
public static GlobalExceptionHandler Instance
@@ -37,74 +77,6 @@ private set
3777
}
3878
}
3979

40-
// Batching triggers are hardcoded constants. We don't need to expose them in Unity Editor.
41-
// If any trigger fires, a batch of exceptions will get sent.
42-
private const int CountThreshold = 50;
43-
private const float TimeThresholdInSeconds = 30.0f;
44-
45-
// RCS endpoint for exception reporting. The `e=1` URL parameter defines JSPB encoding.
46-
// The `f=1` URL parameter indicates that this client is forward compatible with unplanned
47-
// changes to the service's response format.
48-
private const string ProdRcsUrl = "https://pagead2.googlesyndication.com/pagead/ping?e=1&f=1";
49-
50-
internal static readonly Queue<ExceptionReport> _exceptionQueue =
51-
new Queue<ExceptionReport>();
52-
private static readonly object _queueLock = new object();
53-
private float _timeOfNextBatch;
54-
55-
#region Exception payload definition
56-
[Serializable]
57-
public struct LoggableRemoteCaptureRequest
58-
{
59-
public List<LoggablePayload> payloads;
60-
public ClientPingMetadata client_ping_metadata;
61-
}
62-
63-
[Serializable]
64-
public struct LoggablePayload
65-
{
66-
public ExceptionReport unity_gma_sdk_exception_message;
67-
}
68-
69-
/// <summary>
70-
/// A data structure to hold all relevant info for a single exception event.
71-
/// </summary>
72-
[Serializable]
73-
public class ExceptionReport
74-
{
75-
// JSPB compatibility: 64-bit integers must be sent as strings to avoid precision loss.
76-
public string time_msec;
77-
public bool trapped;
78-
public string name;
79-
public string exception_class;
80-
public string top_exception;
81-
public string stacktrace;
82-
public string stacktrace_hash;
83-
84-
// Static metadata.
85-
public string session_id;
86-
public string app_id;
87-
public string app_version_name;
88-
public string platform;
89-
public string unity_version;
90-
public string os_version;
91-
public string device_model;
92-
public string country;
93-
public int total_cpu;
94-
public string total_memory_bytes;
95-
96-
// Dynamic metadata.
97-
public string network_type;
98-
public string orientation;
99-
}
100-
101-
[Serializable]
102-
public struct ClientPingMetadata
103-
{
104-
public int binary_name;
105-
}
106-
#endregion
107-
10880
#region Unity lifecycle methods
10981
private void Awake()
11082
{
@@ -118,12 +90,6 @@ private void Awake()
11890
DontDestroyOnLoad(gameObject);
11991
}
12092

121-
private void Start()
122-
{
123-
RcsPayload.InitializeStaticMetadata();
124-
ResetBatchTimer();
125-
}
126-
12793
private void OnEnable()
12894
{
12995
Application.logMessageReceivedThreaded += OnLogMessageReceivedThreaded;
@@ -133,32 +99,6 @@ private void OnDisable()
13399
{
134100
Application.logMessageReceivedThreaded -= OnLogMessageReceivedThreaded;
135101
}
136-
137-
/// <summary>
138-
/// Runs every frame to check if either of our batching triggers has been met.
139-
/// </summary>
140-
private void Update()
141-
{
142-
int count;
143-
lock (_queueLock)
144-
{
145-
count = _exceptionQueue.Count;
146-
}
147-
bool isCountThresholdMet = count >= CountThreshold;
148-
bool isTimeThresholdMet = Time.time >= _timeOfNextBatch;
149-
if (isCountThresholdMet || isTimeThresholdMet)
150-
{
151-
ProcessAndSendBatch();
152-
}
153-
}
154-
155-
/// <summary>
156-
/// Sends pending exceptions before the application quits.
157-
/// </summary>
158-
private void OnApplicationQuit()
159-
{
160-
ProcessAndSendBatch();
161-
}
162102
#endregion
163103

164104
#region Public reporting method
@@ -180,16 +120,11 @@ public void ReportTrappedException(Exception e, string name = null)
180120
stacktrace = e.StackTrace ?? "",
181121
stacktrace_hash = Sha256Hash(e.StackTrace ?? ""),
182122
};
183-
lock (_queueLock)
184-
{
185-
_exceptionQueue.Enqueue(report);
186-
}
187-
188-
Debug.Log("Trapped exception queued for batch.");
123+
Enqueue(report);
189124
}
190125
#endregion
191126

192-
#region Private core logic
127+
#region Core logic
193128
/// <summary>
194129
/// This callback handles UNTRAPPED exceptions from *any* thread.
195130
/// It must be thread-safe and very fast.
@@ -211,12 +146,7 @@ internal void OnLogMessageReceivedThreaded(string logString, string stackTrace,
211146
stacktrace = stackTrace ?? "",
212147
stacktrace_hash = Sha256Hash(stackTrace ?? ""),
213148
};
214-
lock (_queueLock)
215-
{
216-
_exceptionQueue.Enqueue(report);
217-
}
218-
219-
Debug.Log("Untrapped exception queued for batch.");
149+
Enqueue(report);
220150
}
221151

222152
private string Sha256Hash(string rawData)
@@ -243,24 +173,10 @@ private string Sha256Hash(string rawData)
243173
}
244174

245175
/// <summary>
246-
/// Drains the queue and passes the resulting batch to be sent.
176+
/// Builds and sends a batch of exception reports.
247177
/// </summary>
248-
internal void ProcessAndSendBatch()
178+
protected override void SendBatch(List<ExceptionReport> batch)
249179
{
250-
Debug.Log("Processing and sending a batch of exceptions...");
251-
252-
ResetBatchTimer();
253-
254-
List<ExceptionReport> batch = new List<ExceptionReport>();
255-
lock (_queueLock)
256-
{
257-
if (_exceptionQueue.Count == 0) return;
258-
while(_exceptionQueue.Count > 0)
259-
{
260-
batch.Add(_exceptionQueue.Dequeue());
261-
}
262-
}
263-
264180
var staticMetadata = RcsPayload.GetStaticMetadata();
265181
var dynamicMetadata = RcsPayload.GetDynamicMetadata();
266182

@@ -280,37 +196,16 @@ internal void ProcessAndSendBatch()
280196
report.orientation = dynamicMetadata.orientation;
281197
}
282198

283-
if (batch.Count > 0)
284-
{
285-
SendToRcs(batch);
286-
}
287-
}
288-
289-
/// <summary>
290-
/// Resets the batch timer to the current time plus the threshold.
291-
/// </summary>
292-
private void ResetBatchTimer()
293-
{
294-
_timeOfNextBatch = Time.time + TimeThresholdInSeconds;
295-
}
296-
297-
/// <summary>
298-
/// Builds the final JSON payload (conforming to JSPB rules) and sends it to RCS.
299-
/// </summary>
300-
protected virtual void SendToRcs(List<ExceptionReport> batch)
301-
{
302-
Debug.Log(string.Format("Sending a batch of {0} exception(s)...", batch.Count));
303-
304-
var payloads = new List<LoggablePayload>();
199+
var payloads = new List<ExceptionLoggablePayload>();
305200
foreach (var report in batch)
306201
{
307-
payloads.Add(new LoggablePayload
202+
payloads.Add(new ExceptionLoggablePayload
308203
{
309204
unity_gma_sdk_exception_message = report
310205
});
311206
}
312207

313-
var request = new LoggableRemoteCaptureRequest
208+
var request = new LoggableRemoteCaptureRequest<ExceptionLoggablePayload>
314209
{
315210
payloads = payloads,
316211
client_ping_metadata = new ClientPingMetadata
@@ -320,47 +215,7 @@ protected virtual void SendToRcs(List<ExceptionReport> batch)
320215
};
321216
// TODO(jochac): Use http://go/jspb-wireformat#message-layout instead.
322217
string jsonPayload = JsonUtility.ToJson(request);
323-
Debug.Log("RCS JSON payload: " + jsonPayload);
324-
325-
StartCoroutine(PostRequest(ProdRcsUrl, jsonPayload));
326-
}
327-
328-
/// <summary>
329-
/// Coroutine to send a JSON payload via HTTP POST.
330-
/// </summary>
331-
private IEnumerator PostRequest(string url, string jsonPayload)
332-
{
333-
using (UnityWebRequest uwr = new UnityWebRequest(url, "POST"))
334-
{
335-
byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonPayload);
336-
uwr.uploadHandler = new UploadHandlerRaw(bodyRaw);
337-
uwr.downloadHandler = new DownloadHandlerBuffer();
338-
uwr.SetRequestHeader("Content-Type", "application/json");
339-
340-
yield return uwr.SendWebRequest();
341-
342-
#if UNITY_2020_2_OR_NEWER
343-
if (uwr.result != UnityWebRequest.Result.Success)
344-
#else
345-
if (uwr.isHttpError || uwr.isNetworkError)
346-
#endif
347-
{
348-
Debug.LogError(string.Format(
349-
"Error sending exception batch: {0} | Response code: {1}.",
350-
uwr.error, uwr.responseCode));
351-
}
352-
else
353-
{
354-
Debug.Log("Exception batch sent successfully.");
355-
}
356-
}
357-
}
358-
359-
private string GetEpochMillis()
360-
{
361-
return ((long)DateTime.UtcNow
362-
.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))
363-
.TotalMilliseconds).ToString();
218+
SendToRcs(jsonPayload);
364219
}
365220
#endregion
366221
}

0 commit comments

Comments
 (0)