@@ -30,6 +30,68 @@ public void Process(Exception exception, SentryEvent sentryEvent)
3030 sentryEvent . SentryExceptions = sentryExceptions ;
3131 }
3232
33+ // Sentry exceptions are sorted oldest to newest.
34+ // See https://develop.sentry.dev/sdk/event-payloads/exception
35+ internal IReadOnlyList < SentryException > CreateSentryExceptions ( Exception exception )
36+ {
37+ var exceptions = WalkExceptions ( exception ) . Reverse ( ) . ToList ( ) ;
38+
39+ // In the case of only one exception, ExceptionId and ParentId are useless.
40+ if ( exceptions . Count == 1 && exceptions [ 0 ] . Mechanism is { } mechanism )
41+ {
42+ mechanism . ExceptionId = null ;
43+ mechanism . ParentId = null ;
44+ if ( mechanism . IsDefaultOrEmpty ( ) )
45+ {
46+ // No need to convey an empty mechanism.
47+ exceptions [ 0 ] . Mechanism = null ;
48+ }
49+ }
50+
51+ return exceptions ;
52+ }
53+
54+ private class Counter
55+ {
56+ private int _value ;
57+
58+ public int GetNextValue ( ) => _value ++ ;
59+ }
60+
61+ private IEnumerable < SentryException > WalkExceptions ( Exception exception ) =>
62+ WalkExceptions ( exception , new Counter ( ) , null , null ) ;
63+
64+ private IEnumerable < SentryException > WalkExceptions ( Exception exception , Counter counter , int ? parentId , string ? source )
65+ {
66+ var ex = exception ;
67+ while ( ex is not null )
68+ {
69+ var id = counter . GetNextValue ( ) ;
70+
71+ yield return BuildSentryException ( ex , id , parentId , source ) ;
72+
73+ if ( ex is AggregateException aex )
74+ {
75+ for ( var i = 0 ; i < aex . InnerExceptions . Count ; i ++ )
76+ {
77+ ex = aex . InnerExceptions [ i ] ;
78+ source = $ "{ nameof ( AggregateException . InnerExceptions ) } [{ i } ]";
79+ var sentryExceptions = WalkExceptions ( ex , counter , id , source ) ;
80+ foreach ( var sentryException in sentryExceptions )
81+ {
82+ yield return sentryException ;
83+ }
84+ }
85+
86+ break ;
87+ }
88+
89+ ex = ex . InnerException ;
90+ parentId = id ;
91+ source = nameof ( AggregateException . InnerException ) ;
92+ }
93+ }
94+
3395 private static void MoveExceptionDataToEvent ( SentryEvent sentryEvent , IEnumerable < SentryException > sentryExceptions )
3496 {
3597 var keysToRemove = new List < string > ( ) ;
@@ -77,41 +139,17 @@ value is string stringValue &&
77139 }
78140 }
79141
80- internal List < SentryException > CreateSentryExceptions ( Exception exception )
81- {
82- var exceptions = exception
83- . EnumerateChainedExceptions ( _options )
84- . Select ( BuildSentryException )
85- . ToList ( ) ;
86-
87- // If we've filtered out the aggregate exception, we'll need to copy over details from it.
88- if ( exception is AggregateException && ! _options . KeepAggregateException )
89- {
90- var original = BuildSentryException ( exception ) ;
91-
92- // Exceptions are sent from oldest to newest, so the details belong on the LAST exception.
93- var last = exceptions . Last ( ) ;
94- last . Mechanism = original . Mechanism ;
95-
96- // In some cases the stack trace is already positioned on the inner exception.
97- // Only copy it over when it is missing.
98- last . Stacktrace ??= original . Stacktrace ;
99- }
100-
101- return exceptions ;
102- }
103-
104- private SentryException BuildSentryException ( Exception exception )
142+ private SentryException BuildSentryException ( Exception exception , int id , int ? parentId , string ? source )
105143 {
106144 var sentryEx = new SentryException
107145 {
108146 Type = exception . GetType ( ) . FullName ,
109147 Module = exception . GetType ( ) . Assembly . FullName ,
110- Value = exception . Message ,
148+ Value = exception is AggregateException agg ? agg . GetRawMessage ( ) : exception . Message ,
111149 ThreadId = Environment . CurrentManagedThreadId
112150 } ;
113151
114- var mechanism = GetMechanism ( exception ) ;
152+ var mechanism = GetMechanism ( exception , id , parentId , source ) ;
115153 if ( ! mechanism . IsDefaultOrEmpty ( ) )
116154 {
117155 sentryEx . Mechanism = mechanism ;
@@ -121,7 +159,7 @@ private SentryException BuildSentryException(Exception exception)
121159 return sentryEx ;
122160 }
123161
124- private static Mechanism GetMechanism ( Exception exception )
162+ private static Mechanism GetMechanism ( Exception exception , int id , int ? parentId , string ? source )
125163 {
126164 var mechanism = new Mechanism ( ) ;
127165
@@ -167,6 +205,16 @@ private static Mechanism GetMechanism(Exception exception)
167205 mechanism . Data [ key ] = exception . Data [ key ] ! ;
168206 }
169207
208+ mechanism . ExceptionId = id ;
209+ mechanism . ParentId = parentId ;
210+ mechanism . Source = source ;
211+ mechanism . IsExceptionGroup = exception is AggregateException ;
212+
213+ if ( source != null )
214+ {
215+ mechanism . Type = "chained" ;
216+ }
217+
170218 return mechanism ;
171219 }
172220}
0 commit comments