77using Microsoft . Xrm . Sdk ;
88using System . Collections . Generic ;
99using System . Collections . Concurrent ;
10+ using System . Linq ;
1011using Microsoft . Rest ;
1112using Newtonsoft . Json . Linq ;
1213using Microsoft . PowerPlatform . Dataverse . Client . Utils ;
14+ using Microsoft . Extensions . Logging ;
1315
1416namespace Microsoft . PowerPlatform . Dataverse . Client
1517{
@@ -20,15 +22,17 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
2022 internal sealed class DataverseTraceLogger : TraceLoggerBase
2123 {
2224 // Internal connection of exceptions since last clear.
23- private List < Exception > _ActiveExceptionsList ;
25+ private List < Exception > _ActiveExceptionsList ;
26+
27+ private ILogger _logger ;
2428
2529 #region Properties
2630 /// <summary>
2731 /// Last Error from CRM
2832 /// </summary>
2933 public new string LastError
3034 {
31- get { return base . LastError . ToString ( ) ; }
35+ get { return base . LastError ; }
3236 }
3337
3438 /// <summary>
@@ -80,6 +84,15 @@ public DataverseTraceLogger(string traceSourceName = "")
8084 base . Initialize ( ) ;
8185 }
8286
87+ public DataverseTraceLogger ( ILogger logger )
88+ {
89+ _logger = logger ;
90+ TraceSourceName = DefaultTraceSourceName ;
91+ _ActiveExceptionsList = new List < Exception > ( ) ;
92+ base . Initialize ( ) ;
93+ }
94+
95+
8396 public override void ResetLastError ( )
8497 {
8598 if ( base . LastError . Length > 0 )
@@ -105,7 +118,7 @@ public void ClearLogCache()
105118 /// <param name="message"></param>
106119 public override void Log ( string message )
107120 {
108- Source . TraceEvent ( TraceEventType . Information , ( int ) TraceEventType . Information , message ) ;
121+ TraceEvent ( TraceEventType . Information , ( int ) TraceEventType . Information , message , null ) ;
109122 }
110123
111124 /// <summary>
@@ -115,11 +128,14 @@ public override void Log(string message)
115128 /// <param name="eventType"></param>
116129 public override void Log ( string message , TraceEventType eventType )
117130 {
118- TraceEvent ( eventType , ( int ) eventType , message ) ;
119131 if ( eventType == TraceEventType . Error )
120132 {
121133 Log ( message , eventType , new Exception ( message ) ) ;
122134 }
135+ else
136+ {
137+ TraceEvent ( eventType , ( int ) eventType , message , null ) ;
138+ }
123139 }
124140
125141 /// <summary>
@@ -144,10 +160,10 @@ public override void Log(string message, TraceEventType eventType, Exception exc
144160 if ( ! ( exception != null && _ActiveExceptionsList . Contains ( exception ) ) ) // Skip this line if its already been done.
145161 GetExceptionDetail ( exception , detailedDump , 0 , lastMessage ) ;
146162
147- TraceEvent ( eventType , ( int ) eventType , detailedDump . ToString ( ) ) ;
163+ TraceEvent ( eventType , ( int ) eventType , detailedDump . ToString ( ) , exception ) ;
148164 if ( eventType == TraceEventType . Error )
149165 {
150- base . LastError . Append ( lastMessage . ToString ( ) ) ;
166+ base . LastError += lastMessage . ToString ( ) ;
151167 if ( ! ( exception != null && _ActiveExceptionsList . Contains ( exception ) ) ) // Skip this line if its already been done.
152168 {
153169 // check and or alter the exception is its and HTTPOperationExecption.
@@ -175,13 +191,13 @@ public override void Log(string message, TraceEventType eventType, Exception exc
175191 public override void Log ( Exception exception )
176192 {
177193 if ( exception != null && _ActiveExceptionsList . Contains ( exception ) )
178- return ; // allready logged this one .
194+ return ; // already logged this one .
179195
180196 StringBuilder detailedDump = new StringBuilder ( ) ;
181197 StringBuilder lastMessage = new StringBuilder ( ) ;
182198 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 ( ) ;
185201 LastException = exception ;
186202
187203 _ActiveExceptionsList . Add ( exception ) ;
@@ -196,10 +212,17 @@ public override void Log(Exception exception)
196212 /// <param name="eventType"></param>
197213 /// <param name="id"></param>
198214 /// <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 )
200217 {
201218 Source . TraceEvent ( eventType , id , message ) ;
202219
220+ LogLevel logLevel = TranslateTraceEventType ( eventType ) ;
221+ if ( _logger != null && _logger . IsEnabled ( logLevel ) )
222+ {
223+ _logger . Log ( logLevel , id , ex , message ) ;
224+ }
225+
203226 if ( EnabledInMemoryLogCapture )
204227 {
205228 Logs . Enqueue ( Tuple . Create < DateTime , string > ( DateTime . UtcNow ,
@@ -253,7 +276,7 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
253276 FormatExceptionMessage (
254277 OrgFault . Source != null ? OrgFault . Source . ToString ( ) . Trim ( ) : "Not Provided" ,
255278 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 } ") :
257280 string . IsNullOrEmpty ( OrgFault . Message ) ? "Not Provided" : OrgFault . Message . ToString ( ) . Trim ( ) ,
258281 string . IsNullOrEmpty ( OrgFault . HelpLink ) ? "Not Provided" : OrgFault . HelpLink . ToString ( ) . Trim ( ) ,
259282 string . IsNullOrEmpty ( OrgFault . StackTrace ) ? "Not Provided" : OrgFault . StackTrace . ToString ( ) . Trim ( )
@@ -280,7 +303,7 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
280303 OrganizationServiceFault oFault = ( OrganizationServiceFault ) objException ;
281304 string ErrorDetail = GenerateOrgErrorDetailsInfo ( oFault . ErrorDetails ) ;
282305 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 } ") ,
284307 oFault . Timestamp . ToString ( ) ,
285308 oFault . ErrorCode . ToString ( ) ,
286309 string . IsNullOrEmpty ( oFault . HelpLink ) ? "Not Provided" : oFault . HelpLink . ToString ( ) . Trim ( ) ,
@@ -302,39 +325,41 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
302325 {
303326 if ( objException is HttpOperationException httpOperationException )
304327 {
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 ) ;
306331
307- var ErrorBlock = contentBody [ "error" ] ;
332+ var ErrorBlock = contentBody ? [ "error" ] ;
308333 FormatExceptionMessage (
309334 httpOperationException . Source != null ? httpOperationException . Source . ToString ( ) . Trim ( ) : "Not Provided" ,
310335 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" ] ) } " : "" ) ,
312337 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 ( )
314339 , sw , level ) ;
315340
316341 lastErrorMsg . Append ( string . IsNullOrEmpty ( httpOperationException . Message ) ? "Not Provided" : httpOperationException . Message . ToString ( ) . Trim ( ) ) ;
317342
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" ] ;
320345 if ( lastErrorMsg . Length > 0 && InnerError != null )
321346 {
322347 level ++ ;
323348 lastErrorMsg . Append ( " => " ) ;
324349 FormatExceptionMessage (
325350 httpOperationException . Source != null ? httpOperationException . Source . ToString ( ) . Trim ( ) : "Not Provided" ,
326351 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 ( )
330355 , sw , level ) ;
331356 }
332357 }
333358 else
334359 {
335360 if ( objException is DataverseOperationException cdsOpExecp )
336361 {
337- FormatCdsSvcFaultMessage (
362+ FormatSvcFaultMessage (
338363 string . IsNullOrEmpty ( cdsOpExecp . Message ) ? "Not Provided" : cdsOpExecp . Message . ToString ( ) . Trim ( ) ,
339364 string . IsNullOrEmpty ( cdsOpExecp . Source ) ? "Not Provided" : cdsOpExecp . Source . ToString ( ) . Trim ( ) ,
340365 cdsOpExecp . HResult == - 1 ? "Not Provided" : cdsOpExecp . HResult . ToString ( ) . Trim ( ) ,
@@ -382,12 +407,30 @@ private void GetExceptionDetail(object objException, StringBuilder sw, int level
382407 return ;
383408 }
384409
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 )
391434 {
392435 if ( ! string . IsNullOrEmpty ( textBlock ) )
393436 {
@@ -477,11 +520,11 @@ private static void FormatOrgFaultMessage(string message, string timeOfEvent, st
477520 /// <param name="helpLink">Help Link</param>
478521 /// <param name="sw">Writer to write too</param>
479522 /// <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 )
481524 {
482525 if ( level != 0 )
483526 sw . AppendLine ( $ "Inner Exception Level { level } \t : ") ;
484- sw . AppendLine ( "==CdsClientOperationException Info=======================================================================================" ) ;
527+ sw . AppendLine ( "==DataverseOperationException Info=======================================================================================" ) ;
485528 sw . AppendLine ( $ "Source: { source } ") ;
486529 sw . AppendLine ( "Error: " + message ) ;
487530 sw . AppendLine ( "ErrorCode: " + errorCode ) ;
@@ -490,14 +533,34 @@ private static void FormatCdsSvcFaultMessage(string message, string source, stri
490533 sw . AppendLine ( $ "HelpLink Url: { helpLink } ") ;
491534 if ( dataItems != null && dataItems . Count > 0 )
492535 {
493- sw . AppendLine ( "CdsErrorDetail :" ) ;
536+ sw . AppendLine ( "DataverseErrorDetail :" ) ;
494537 foreach ( System . Collections . DictionaryEntry itm in dataItems )
495538 {
496539 sw . AppendLine ( $ "\t { itm . Key } : { itm . Value } ") ;
497540 }
498541 }
499542 sw . AppendLine ( "======================================================================================================================" ) ;
500543 }
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+
501564 }
502565
503566 /// <summary>
0 commit comments