7
7
using Microsoft . Xrm . Sdk ;
8
8
using System . Collections . Generic ;
9
9
using System . Collections . Concurrent ;
10
+ using System . Linq ;
10
11
using Microsoft . Rest ;
11
12
using Newtonsoft . Json . Linq ;
12
13
using Microsoft . PowerPlatform . Dataverse . Client . Utils ;
14
+ using Microsoft . Extensions . Logging ;
13
15
14
16
namespace Microsoft . PowerPlatform . Dataverse . Client
15
17
{
@@ -20,15 +22,17 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
20
22
internal sealed class DataverseTraceLogger : TraceLoggerBase
21
23
{
22
24
// Internal connection of exceptions since last clear.
23
- private List < Exception > _ActiveExceptionsList ;
25
+ private List < Exception > _ActiveExceptionsList ;
26
+
27
+ private ILogger _logger ;
24
28
25
29
#region Properties
26
30
/// <summary>
27
31
/// Last Error from CRM
28
32
/// </summary>
29
33
public new string LastError
30
34
{
31
- get { return base . LastError . ToString ( ) ; }
35
+ get { return base . LastError ; }
32
36
}
33
37
34
38
/// <summary>
@@ -80,6 +84,15 @@ public DataverseTraceLogger(string traceSourceName = "")
80
84
base . Initialize ( ) ;
81
85
}
82
86
87
+ public DataverseTraceLogger ( ILogger logger )
88
+ {
89
+ _logger = logger ;
90
+ TraceSourceName = DefaultTraceSourceName ;
91
+ _ActiveExceptionsList = new List < Exception > ( ) ;
92
+ base . Initialize ( ) ;
93
+ }
94
+
95
+
83
96
public override void ResetLastError ( )
84
97
{
85
98
if ( base . LastError . Length > 0 )
@@ -105,7 +118,7 @@ public void ClearLogCache()
105
118
/// <param name="message"></param>
106
119
public override void Log ( string message )
107
120
{
108
- Source . TraceEvent ( TraceEventType . Information , ( int ) TraceEventType . Information , message ) ;
121
+ TraceEvent ( TraceEventType . Information , ( int ) TraceEventType . Information , message , null ) ;
109
122
}
110
123
111
124
/// <summary>
@@ -115,11 +128,14 @@ public override void Log(string message)
115
128
/// <param name="eventType"></param>
116
129
public override void Log ( string message , TraceEventType eventType )
117
130
{
118
- TraceEvent ( eventType , ( int ) eventType , message ) ;
119
131
if ( eventType == TraceEventType . Error )
120
132
{
121
133
Log ( message , eventType , new Exception ( message ) ) ;
122
134
}
135
+ else
136
+ {
137
+ TraceEvent ( eventType , ( int ) eventType , message , null ) ;
138
+ }
123
139
}
124
140
125
141
/// <summary>
@@ -144,10 +160,10 @@ public override void Log(string message, TraceEventType eventType, Exception exc
144
160
if ( ! ( exception != null && _ActiveExceptionsList . Contains ( exception ) ) ) // Skip this line if its already been done.
145
161
GetExceptionDetail ( exception , detailedDump , 0 , lastMessage ) ;
146
162
147
- TraceEvent ( eventType , ( int ) eventType , detailedDump . ToString ( ) ) ;
163
+ TraceEvent ( eventType , ( int ) eventType , detailedDump . ToString ( ) , exception ) ;
148
164
if ( eventType == TraceEventType . Error )
149
165
{
150
- base . LastError . Append ( lastMessage . ToString ( ) ) ;
166
+ base . LastError += lastMessage . ToString ( ) ;
151
167
if ( ! ( exception != null && _ActiveExceptionsList . Contains ( exception ) ) ) // Skip this line if its already been done.
152
168
{
153
169
// check and or alter the exception is its and HTTPOperationExecption.
@@ -175,13 +191,13 @@ public override void Log(string message, TraceEventType eventType, Exception exc
175
191
public override void Log ( Exception exception )
176
192
{
177
193
if ( exception != null && _ActiveExceptionsList . Contains ( exception ) )
178
- return ; // allready logged this one .
194
+ return ; // already logged this one .
179
195
180
196
StringBuilder detailedDump = new StringBuilder ( ) ;
181
197
StringBuilder lastMessage = new StringBuilder ( ) ;
182
198
GetExceptionDetail ( exception , detailedDump , 0 , lastMessage ) ;
183
- TraceEvent ( TraceEventType . Error , ( int ) TraceEventType . Error , detailedDump . ToString ( ) ) ;
184
- base . LastError . Append ( lastMessage . ToString ( ) ) ;
199
+ TraceEvent ( TraceEventType . Error , ( int ) TraceEventType . Error , detailedDump . ToString ( ) , exception ) ;
200
+ base . LastError += lastMessage . ToString ( ) ;
185
201
LastException = exception ;
186
202
187
203
_ActiveExceptionsList . Add ( exception ) ;
@@ -196,10 +212,17 @@ public override void Log(Exception exception)
196
212
/// <param name="eventType"></param>
197
213
/// <param name="id"></param>
198
214
/// <param name="message"></param>
199
- private void TraceEvent ( TraceEventType eventType , int id , string message )
215
+ /// <param name="ex"></param>
216
+ private void TraceEvent ( TraceEventType eventType , int id , string message , Exception ex )
200
217
{
201
218
Source . TraceEvent ( eventType , id , message ) ;
202
219
220
+ LogLevel logLevel = TranslateTraceEventType ( eventType ) ;
221
+ if ( _logger != null && _logger . IsEnabled ( logLevel ) )
222
+ {
223
+ _logger . Log ( logLevel , id , ex , message ) ;
224
+ }
225
+
203
226
if ( EnabledInMemoryLogCapture )
204
227
{
205
228
Logs . Enqueue ( Tuple . Create < DateTime , string > ( DateTime . UtcNow ,
@@ -253,7 +276,7 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
253
276
FormatExceptionMessage (
254
277
OrgFault . Source != null ? OrgFault . Source . ToString ( ) . Trim ( ) : "Not Provided" ,
255
278
OrgFault . TargetSite != null ? OrgFault . TargetSite . Name . ToString ( ) : "Not Provided" ,
256
- OrgFault . Detail != null ? string . Format ( CultureInfo . InvariantCulture , "Message: {0}\n ErrorCode: {1}\n Trace: {2}{3}" , OrgFault . Detail . Message , OrgFault . Detail . ErrorCode , OrgFault . Detail . TraceText , string . IsNullOrEmpty ( ErrorDetail ) ? "" : $ "\n { ErrorDetail } ") :
279
+ OrgFault . Detail != null ? string . Format ( CultureInfo . InvariantCulture , "Message: {0}\n ErrorCode: {1}{4} \n Trace: {2}{3}" , OrgFault . Detail . Message , OrgFault . Detail . ErrorCode , OrgFault . Detail . TraceText , string . IsNullOrEmpty ( ErrorDetail ) ? "" : $ "\n { ErrorDetail } " , OrgFault . Detail . ActivityId == null ? "" : $ " \n ActivityId: { OrgFault . Detail . ActivityId } ") :
257
280
string . IsNullOrEmpty ( OrgFault . Message ) ? "Not Provided" : OrgFault . Message . ToString ( ) . Trim ( ) ,
258
281
string . IsNullOrEmpty ( OrgFault . HelpLink ) ? "Not Provided" : OrgFault . HelpLink . ToString ( ) . Trim ( ) ,
259
282
string . IsNullOrEmpty ( OrgFault . StackTrace ) ? "Not Provided" : OrgFault . StackTrace . ToString ( ) . Trim ( )
@@ -280,7 +303,7 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
280
303
OrganizationServiceFault oFault = ( OrganizationServiceFault ) objException ;
281
304
string ErrorDetail = GenerateOrgErrorDetailsInfo ( oFault . ErrorDetails ) ;
282
305
FormatOrgFaultMessage (
283
- string . Format ( CultureInfo . InvariantCulture , "Message: {0}\n ErrorCode: {1}\n Trace: {2}{3}" , oFault . Message , oFault . ErrorCode , oFault . TraceText , string . IsNullOrEmpty ( ErrorDetail ) ? "" : $ "\n { ErrorDetail } ") ,
306
+ string . Format ( CultureInfo . InvariantCulture , "Message: {0}\n ErrorCode: {1}{4} \n Trace: {2}{3}" , oFault . Message , oFault . ErrorCode , oFault . TraceText , string . IsNullOrEmpty ( ErrorDetail ) ? "" : $ "\n { ErrorDetail } " , oFault . ActivityId == null ? "" : $ " \n ActivityId: { oFault . ActivityId } ") ,
284
307
oFault . Timestamp . ToString ( ) ,
285
308
oFault . ErrorCode . ToString ( ) ,
286
309
string . IsNullOrEmpty ( oFault . HelpLink ) ? "Not Provided" : oFault . HelpLink . ToString ( ) . Trim ( ) ,
@@ -302,39 +325,41 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
302
325
{
303
326
if ( objException is HttpOperationException httpOperationException )
304
327
{
305
- JObject contentBody = JObject . Parse ( httpOperationException . Response . Content ) ;
328
+ JObject contentBody = null ;
329
+ if ( ! string . IsNullOrEmpty ( httpOperationException . Response . Content ) )
330
+ contentBody = JObject . Parse ( httpOperationException . Response . Content ) ;
306
331
307
- var ErrorBlock = contentBody [ "error" ] ;
332
+ var ErrorBlock = contentBody ? [ "error" ] ;
308
333
FormatExceptionMessage (
309
334
httpOperationException . Source != null ? httpOperationException . Source . ToString ( ) . Trim ( ) : "Not Provided" ,
310
335
httpOperationException . TargetSite != null ? httpOperationException . TargetSite . Name ? . ToString ( ) : "Not Provided" ,
311
- string . IsNullOrEmpty ( ErrorBlock [ "message" ] ? . ToString ( ) ) ? "Not Provided" : GetFirstLineFromString ( ErrorBlock [ "message" ] ? . ToString ( ) ) . Trim ( ) ,
336
+ string . IsNullOrEmpty ( ErrorBlock ? [ "message" ] ? . ToString ( ) ) ? "Not Provided" : string . Format ( "Message: {0}{1} \n " , GetFirstLineFromString ( ErrorBlock ? [ "message" ] ? . ToString ( ) ) . Trim ( ) , httpOperationException . Response != null && httpOperationException . Response . Headers . ContainsKey ( "REQ_ID" ) ? $ " \n ActivityId: { ExtractString ( httpOperationException . Response . Headers [ "REQ_ID" ] ) } " : "" ) ,
312
337
string . IsNullOrEmpty ( httpOperationException . HelpLink ) ? "Not Provided" : httpOperationException . HelpLink . ToString ( ) . Trim ( ) ,
313
- string . IsNullOrEmpty ( ErrorBlock [ "stacktrace" ] ? . ToString ( ) ) ? "Not Provided" : ErrorBlock [ "stacktrace" ] ? . ToString ( ) . Trim ( )
338
+ string . IsNullOrEmpty ( ErrorBlock ? [ "stacktrace" ] ? . ToString ( ) ) ? "Not Provided" : ErrorBlock [ "stacktrace" ] ? . ToString ( ) . Trim ( )
314
339
, sw , level ) ;
315
340
316
341
lastErrorMsg . Append ( string . IsNullOrEmpty ( httpOperationException . Message ) ? "Not Provided" : httpOperationException . Message . ToString ( ) . Trim ( ) ) ;
317
342
318
- // WebEx currently only returns 1 leve of error.
319
- var InnerError = contentBody [ "error" ] [ "innererror" ] ;
343
+ // WebEx currently only returns 1 level of error.
344
+ var InnerError = contentBody ? [ "error" ] [ "innererror" ] ;
320
345
if ( lastErrorMsg . Length > 0 && InnerError != null )
321
346
{
322
347
level ++ ;
323
348
lastErrorMsg . Append ( " => " ) ;
324
349
FormatExceptionMessage (
325
350
httpOperationException . Source != null ? httpOperationException . Source . ToString ( ) . Trim ( ) : "Not Provided" ,
326
351
httpOperationException . TargetSite != null ? httpOperationException . TargetSite . Name ? . ToString ( ) : "Not Provided" ,
327
- string . IsNullOrEmpty ( InnerError [ "message" ] ? . ToString ( ) ) ? "Not Provided" : GetFirstLineFromString ( InnerError [ "message" ] ? . ToString ( ) ) . Trim ( ) ,
328
- string . IsNullOrEmpty ( InnerError [ "@Microsoft.PowerApps.CDS.HelpLink" ] ? . ToString ( ) ) ? "Not Provided" : GetFirstLineFromString ( InnerError [ "@Microsoft.PowerApps.CDS.HelpLink" ] ? . ToString ( ) ) . Trim ( ) ,
329
- string . IsNullOrEmpty ( InnerError [ "stacktrace" ] ? . ToString ( ) ) ? "Not Provided" : InnerError [ "stacktrace" ] ? . ToString ( ) . Trim ( )
352
+ string . IsNullOrEmpty ( InnerError ? [ "message" ] ? . ToString ( ) ) ? "Not Provided" : GetFirstLineFromString ( InnerError ? [ "message" ] ? . ToString ( ) ) . Trim ( ) ,
353
+ string . IsNullOrEmpty ( InnerError ? [ "@Microsoft.PowerApps.CDS.HelpLink" ] ? . ToString ( ) ) ? "Not Provided" : GetFirstLineFromString ( InnerError ? [ "@Microsoft.PowerApps.CDS.HelpLink" ] ? . ToString ( ) ) . Trim ( ) ,
354
+ string . IsNullOrEmpty ( InnerError ? [ "stacktrace" ] ? . ToString ( ) ) ? "Not Provided" : InnerError ? [ "stacktrace" ] ? . ToString ( ) . Trim ( )
330
355
, sw , level ) ;
331
356
}
332
357
}
333
358
else
334
359
{
335
360
if ( objException is DataverseOperationException cdsOpExecp )
336
361
{
337
- FormatCdsSvcFaultMessage (
362
+ FormatSvcFaultMessage (
338
363
string . IsNullOrEmpty ( cdsOpExecp . Message ) ? "Not Provided" : cdsOpExecp . Message . ToString ( ) . Trim ( ) ,
339
364
string . IsNullOrEmpty ( cdsOpExecp . Source ) ? "Not Provided" : cdsOpExecp . Source . ToString ( ) . Trim ( ) ,
340
365
cdsOpExecp . HResult == - 1 ? "Not Provided" : cdsOpExecp . HResult . ToString ( ) . Trim ( ) ,
@@ -382,12 +407,30 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
382
407
return ;
383
408
}
384
409
385
- /// <summary>
386
- /// returns the first line from the text block.
387
- /// </summary>
388
- /// <param name="textBlock"></param>
389
- /// <returns></returns>
390
- internal static string GetFirstLineFromString ( string textBlock )
410
+ private static string ExtractString ( IEnumerable < string > enumerable )
411
+ {
412
+ string sOut = string . Empty ;
413
+ if ( enumerable != null )
414
+ {
415
+ List < string > lst = new List < string > ( enumerable ) ;
416
+
417
+ foreach ( var itm in lst . Distinct ( ) )
418
+ {
419
+ if ( string . IsNullOrEmpty ( sOut ) )
420
+ sOut += $ "{ itm } ";
421
+ else
422
+ sOut += $ "|{ itm } ";
423
+ }
424
+ }
425
+ return sOut ;
426
+ }
427
+
428
+ /// <summary>
429
+ /// returns the first line from the text block.
430
+ /// </summary>
431
+ /// <param name="textBlock"></param>
432
+ /// <returns></returns>
433
+ internal static string GetFirstLineFromString ( string textBlock )
391
434
{
392
435
if ( ! string . IsNullOrEmpty ( textBlock ) )
393
436
{
@@ -477,11 +520,11 @@ private static void FormatOrgFaultMessage(string message, string timeOfEvent, st
477
520
/// <param name="helpLink">Help Link</param>
478
521
/// <param name="sw">Writer to write too</param>
479
522
/// <param name="level">Depth</param>
480
- private static void FormatCdsSvcFaultMessage ( string message , string source , string errorCode , System . Collections . IDictionary dataItems , string helpLink , StringBuilder sw , int level )
523
+ private static void FormatSvcFaultMessage ( string message , string source , string errorCode , System . Collections . IDictionary dataItems , string helpLink , StringBuilder sw , int level )
481
524
{
482
525
if ( level != 0 )
483
526
sw . AppendLine ( $ "Inner Exception Level { level } \t : ") ;
484
- sw . AppendLine ( "==CdsClientOperationException Info=======================================================================================" ) ;
527
+ sw . AppendLine ( "==DataverseOperationException Info=======================================================================================" ) ;
485
528
sw . AppendLine ( $ "Source: { source } ") ;
486
529
sw . AppendLine ( "Error: " + message ) ;
487
530
sw . AppendLine ( "ErrorCode: " + errorCode ) ;
@@ -490,14 +533,34 @@ private static void FormatCdsSvcFaultMessage(string message, string source, stri
490
533
sw . AppendLine ( $ "HelpLink Url: { helpLink } ") ;
491
534
if ( dataItems != null && dataItems . Count > 0 )
492
535
{
493
- sw . AppendLine ( "CdsErrorDetail :" ) ;
536
+ sw . AppendLine ( "DataverseErrorDetail :" ) ;
494
537
foreach ( System . Collections . DictionaryEntry itm in dataItems )
495
538
{
496
539
sw . AppendLine ( $ "\t { itm . Key } : { itm . Value } ") ;
497
540
}
498
541
}
499
542
sw . AppendLine ( "======================================================================================================================" ) ;
500
543
}
544
+
545
+ private static LogLevel TranslateTraceEventType ( TraceEventType traceLevel )
546
+ {
547
+ switch ( traceLevel )
548
+ {
549
+ case TraceEventType . Critical :
550
+ return LogLevel . Critical ;
551
+ case TraceEventType . Error :
552
+ return LogLevel . Error ;
553
+ case TraceEventType . Warning :
554
+ return LogLevel . Warning ;
555
+ case TraceEventType . Information :
556
+ return LogLevel . Information ;
557
+ case TraceEventType . Verbose :
558
+ return LogLevel . Trace ;
559
+ default :
560
+ return LogLevel . None ;
561
+ }
562
+ }
563
+
501
564
}
502
565
503
566
/// <summary>
0 commit comments