88
99namespace 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