22// Licensed under the MIT License.
33
44using System ;
5+ using System . Collections . Generic ;
56using System . Diagnostics ;
7+ using System . Globalization ;
8+ using System . Linq ;
69using System . Reflection ;
710using System . Runtime . CompilerServices ;
811using Azure . Monitor . OpenTelemetry . Exporter . Models ;
12+ using Microsoft . Extensions . Logging ;
913using Moq ;
14+ using OpenTelemetry . Logs ;
1015using Xunit ;
1116
1217namespace Azure . Monitor . OpenTelemetry . Exporter . Tests
@@ -104,8 +109,18 @@ public void CheckThatFileNameAndLineAreCorrectIfAvailable()
104109 var exceptionDetails = new TelemetryExceptionDetails ( exception , exception . Message , null ) ;
105110 var stack = exceptionDetails . ParsedStack ;
106111
107- Assert . Equal ( line , stack [ 0 ] . Line ) ;
108- Assert . Equal ( fileName , stack [ 0 ] . FileName ) ;
112+ // Behavior may vary in some cases and line may return 0
113+ // In this case Line will be null
114+ if ( line != 0 )
115+ {
116+ Assert . Equal ( line , stack [ 0 ] . Line ) ;
117+ Assert . Equal ( fileName , stack [ 0 ] . FileName ) ;
118+ }
119+ else
120+ {
121+ Assert . Null ( stack [ 0 ] . Line ) ;
122+ Assert . Null ( stack [ 0 ] . FileName ) ;
123+ }
109124 }
110125
111126 [ Fact ]
@@ -149,7 +164,7 @@ public void SizeOfParsedStackFrameIsLessThanMaxAllowedValue()
149164 }
150165
151166 [ Fact ]
152- public void ParentIdISetAsOuterId ( )
167+ public void ParentIdIsSetAsOuterId ( )
153168 {
154169 var parentException = new Exception ( "Parent Exception" ) ;
155170 var parentExceptionDetails = new TelemetryExceptionDetails ( parentException , parentException . Message , null ) ;
@@ -159,6 +174,147 @@ public void ParentIdISetAsOuterId()
159174 Assert . Equal ( parentExceptionDetails . Id , exceptionDetails . OuterId ) ;
160175 }
161176
177+ [ Fact ]
178+ public void ExceptionDataContainsExceptionDetails ( )
179+ {
180+ var logRecords = new List < LogRecord > ( ) ;
181+ using var loggerFactory = LoggerFactory . Create ( builder =>
182+ {
183+ builder . AddOpenTelemetry ( options =>
184+ {
185+ options . AddInMemoryExporter ( logRecords ) ;
186+ } ) ;
187+ builder . AddFilter ( typeof ( LogsHelperTests ) . FullName , LogLevel . Trace ) ;
188+ } ) ;
189+
190+ var logger = loggerFactory . CreateLogger < LogsHelperTests > ( ) ;
191+
192+ var exception = CreateException ( 1 ) ;
193+ logger . LogWarning ( exception , exception . Message ) ;
194+
195+ var exceptionData = new TelemetryExceptionData ( 2 , logRecords [ 0 ] ) ;
196+
197+ Assert . Equal ( 1 , exceptionData . Exceptions . Count ) ;
198+ }
199+
200+ [ Fact ]
201+ public void ExceptionDataContainsExceptionDetailsofAllInnerExceptionsOfAggregateException ( )
202+ {
203+ var logRecords = new List < LogRecord > ( ) ;
204+ using var loggerFactory = LoggerFactory . Create ( builder =>
205+ {
206+ builder . AddOpenTelemetry ( options =>
207+ {
208+ options . AddInMemoryExporter ( logRecords ) ;
209+ } ) ;
210+ builder . AddFilter ( typeof ( LogsHelperTests ) . FullName , LogLevel . Trace ) ;
211+ } ) ;
212+
213+ var logger = loggerFactory . CreateLogger < LogsHelperTests > ( ) ;
214+
215+ Exception innerException1 = new ArgumentException ( "Inner1" ) ;
216+ Exception innerException2 = new ArgumentException ( "Inner2" ) ;
217+
218+ AggregateException aggregateException = new AggregateException ( "AggregateException" , new [ ] { innerException1 , innerException2 } ) ;
219+
220+ // Passing "AggregateException" explicitly here instead of using aggregateException.Message
221+ // aggregateException.Message will return different value in case of net461 compared to netcore
222+ logger . LogWarning ( aggregateException , "AggregateException" ) ;
223+
224+ var exceptionData = new TelemetryExceptionData ( 2 , logRecords [ 0 ] ) ;
225+
226+ Assert . Equal ( 3 , exceptionData . Exceptions . Count ) ;
227+ Assert . Equal ( "AggregateException" , exceptionData . Exceptions [ 0 ] . Message ) ;
228+ Assert . Equal ( "Inner1" , exceptionData . Exceptions [ 1 ] . Message ) ;
229+ Assert . Equal ( "Inner2" , exceptionData . Exceptions [ 2 ] . Message ) ;
230+ }
231+
232+ [ Fact ]
233+ public void ExceptionDataContainsExceptionDetailsWithAllInnerExceptions ( )
234+ {
235+ var logRecords = new List < LogRecord > ( ) ;
236+ using var loggerFactory = LoggerFactory . Create ( builder =>
237+ {
238+ builder . AddOpenTelemetry ( options =>
239+ {
240+ options . AddInMemoryExporter ( logRecords ) ;
241+ } ) ;
242+ builder . AddFilter ( typeof ( LogsHelperTests ) . FullName , LogLevel . Trace ) ;
243+ } ) ;
244+
245+ var logger = loggerFactory . CreateLogger < LogsHelperTests > ( ) ;
246+
247+ var innerException1 = new Exception ( "Inner1" ) ;
248+
249+ var innerexception2 = new Exception ( "Inner2" , innerException1 ) ;
250+
251+ var exception = new Exception ( "Exception" , innerexception2 ) ;
252+
253+ // Passing "Exception" explicitly here instead of using exception.Message
254+ // exception.Message will return different value in case of net461 compared to netcore
255+ logger . LogWarning ( exception , "Exception" ) ;
256+
257+ var exceptionData = new TelemetryExceptionData ( 2 , logRecords [ 0 ] ) ;
258+
259+ Assert . Equal ( 3 , exceptionData . Exceptions . Count ) ;
260+ Assert . Equal ( "Exception" , exceptionData . Exceptions [ 0 ] . Message ) ;
261+ Assert . Equal ( "Inner2" , exceptionData . Exceptions [ 1 ] . Message ) ;
262+ Assert . Equal ( "Inner1" , exceptionData . Exceptions [ 2 ] . Message ) ;
263+ }
264+
265+ [ Fact ]
266+ public void AggregateExceptionsWithMultipleNestedExceptionsAreTrimmedAfterReachingMaxCount ( )
267+ {
268+ var logRecords = new List < LogRecord > ( ) ;
269+ using var loggerFactory = LoggerFactory . Create ( builder =>
270+ {
271+ builder . AddOpenTelemetry ( options =>
272+ {
273+ options . AddInMemoryExporter ( logRecords ) ;
274+ } ) ;
275+ builder . AddFilter ( typeof ( LogsHelperTests ) . FullName , LogLevel . Trace ) ;
276+ } ) ;
277+
278+ var logger = loggerFactory . CreateLogger < LogsHelperTests > ( ) ;
279+
280+ int numberOfExceptions = 15 ;
281+ int maxNumberOfExceptionsAllowed = 10 ;
282+ List < Exception > innerExceptions = new List < Exception > ( ) ;
283+ for ( int i = 0 ; i < numberOfExceptions ; i ++ )
284+ {
285+ innerExceptions . Add ( new Exception ( ( i + 1 ) . ToString ( CultureInfo . InvariantCulture ) ) ) ;
286+ }
287+
288+ AggregateException rootLevelException = new AggregateException ( "0" , innerExceptions ) ;
289+
290+ // Passing "0" explicitly here instead of using rootLevelException.Message
291+ // rootLevelException.Message will return different value in case of net461 compared to netcore
292+ logger . LogWarning ( rootLevelException , "0" ) ;
293+
294+ var exceptionData = new TelemetryExceptionData ( 2 , logRecords [ 0 ] ) ;
295+
296+ Assert . Equal ( maxNumberOfExceptionsAllowed + 1 , exceptionData . Exceptions . Count ) ;
297+
298+ for ( int counter = 0 ; counter < maxNumberOfExceptionsAllowed ; counter ++ )
299+ {
300+ var details = exceptionData . Exceptions [ counter ] ;
301+
302+ Assert . Equal ( counter . ToString ( CultureInfo . InvariantCulture ) , details . Message ) ;
303+ }
304+
305+ var firstExceptionInList = exceptionData . Exceptions . First ( ) ;
306+ var lastExceptionInList = exceptionData . Exceptions . Last ( ) ;
307+ Assert . Equal ( firstExceptionInList . Id , lastExceptionInList . OuterId ) ;
308+ Assert . Equal ( typeof ( InnerExceptionCountExceededException ) . FullName , lastExceptionInList . TypeName ) ;
309+ Assert . Equal (
310+ string . Format (
311+ CultureInfo . InvariantCulture ,
312+ "The number of inner exceptions was {0} which is larger than {1}, the maximum number allowed during transmission. All but the first {1} have been dropped." ,
313+ numberOfExceptions + 1 ,
314+ maxNumberOfExceptionsAllowed ) ,
315+ lastExceptionInList . Message ) ;
316+ }
317+
162318 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
163319 private Exception CreateException ( int stackDepth )
164320 {
0 commit comments