1- using System ;
1+ using System ;
22using System . Collections . Generic ;
3+ using System . Linq ;
34using CoreHelpers . TaskLogging ;
45using Microsoft . Extensions . Logging ;
6+ using Newtonsoft . Json ;
57
68namespace CoreHelpers . Extensions . Logging . Tasks
79{
810 internal class TaskLogger : ILogger
911 {
10- private ITaskLoggerFactory _taskLoggerFactory ;
12+ private readonly string _categoryName ;
13+ private readonly IExternalScopeProvider _scopeProvider ;
14+ private readonly ITaskLoggerFactory _taskLoggerFactory ;
1115
12- private ITaskLogger ? _currentTaskLogger = null ;
13- private bool _lastLogWasAnError = false ;
14-
15- public TaskLogger ( ITaskLoggerFactory taskLoggerFactory )
16+ public TaskLogger ( string categoryName , IExternalScopeProvider scopeProvider , ITaskLoggerFactory taskLoggerFactory )
1617 {
18+ _categoryName = categoryName ;
19+ _scopeProvider = scopeProvider ;
1720 _taskLoggerFactory = taskLoggerFactory ;
1821 }
19-
20- // By default the task logging framework does not care about
21- // unknown scopes
22- public IDisposable ? BeginScope < TState > ( TState state ) where TState : notnull
23- {
24- // check if we are interested
25- var castedState = state as TaskLoggerState ;
26- if ( castedState == null )
27- return null ;
28-
29- // just in case a task logger is active flush
30- if ( _currentTaskLogger != null )
22+
23+ public void Log < TState > ( LogLevel logLevel , EventId eventId , TState state , Exception ? exception , Func < TState , Exception ? , string > formatter )
24+ {
25+ // check if the log level is enabled
26+ if ( ! IsEnabled ( logLevel ) )
27+ return ;
28+
29+ // collect ITaskLoggerTypedScope from the scope provider
30+ var scopes = new List < object > ( ) ;
31+ _scopeProvider . ForEachScope ( ( scope , list ) => list . Add ( scope ) , scopes ) ;
32+
33+ // filter for the task logger scope, if not in the list nothing todo
34+ var taskLoggerState = scopes . FirstOrDefault ( scope => scope is TaskLoggerState typed ) as TaskLoggerState ;
35+ if ( taskLoggerState == null )
36+ return ;
37+
38+ // check if we need to announce the task as it was not announced before
39+ if ( eventId is { Id : 0 , Name : "TaskScopeInitPending" } )
3140 {
32- _currentTaskLogger . Dispose ( ) ;
33- _lastLogWasAnError = false ;
34- }
41+ if ( taskLoggerState . IsTaskAnnounced && ! string . IsNullOrEmpty ( taskLoggerState . TaskId ) )
42+ return ;
3543
36- // check if we need to announce the task
37- if ( ! castedState . IsTaskAnnounced )
38- {
39- if ( String . IsNullOrEmpty ( castedState . MetaData ) )
44+ if ( String . IsNullOrEmpty ( taskLoggerState . MetaData ) )
4045 {
41- castedState . TaskId = _taskLoggerFactory
42- . AnnounceTask ( castedState . TaskType , castedState . TaskSource , castedState . TaskWorker ) . GetAwaiter ( )
46+ taskLoggerState . TaskId = _taskLoggerFactory
47+ . AnnounceTask ( taskLoggerState . TaskType , taskLoggerState . TaskSource , taskLoggerState . TaskWorker ) . GetAwaiter ( )
4348 . GetResult ( ) ;
4449 }
4550 else
4651 {
47- castedState . TaskId = _taskLoggerFactory
48- . AnnounceTask ( castedState . TaskType , castedState . TaskSource , castedState . TaskWorker , castedState . MetaData ) . GetAwaiter ( )
52+ taskLoggerState . TaskId = _taskLoggerFactory
53+ . AnnounceTask ( taskLoggerState . TaskType , taskLoggerState . TaskSource , taskLoggerState . TaskWorker , taskLoggerState . MetaData ) . GetAwaiter ( )
4954 . GetResult ( ) ;
5055 }
5156
52- castedState . IsTaskAnnounced = true ;
57+ taskLoggerState . IsTaskAnnounced = true ;
58+
59+ return ;
5360 }
5461
55- // set a new task logger
56- _currentTaskLogger = _taskLoggerFactory . CreateTaskLogger ( castedState . TaskId ) ;
57-
58- // ensure the task is running now
59- _taskLoggerFactory . UpdateTaskStatus ( castedState . TaskId , TaskStatus . Running , castedState . TaskWorker ) . GetAwaiter ( ) . GetResult ( ) ;
60-
61- // done
62- return new TaskLoggerScope ( castedState , this ) ;
62+ // check if we need to set the task as running
63+ if ( eventId is { Id : 0 , Name : "TaskScopeStarted" } )
64+ {
65+ _taskLoggerFactory . UpdateTaskStatus ( taskLoggerState . TaskId , TaskStatus . Running ) . Wait ( ) ;
66+ return ;
67+ }
68+
69+ // in case of exception, we need to log it more in details
70+ if ( exception != null )
71+ {
72+ // set the last log as error so that the task will be marked as failed
73+ taskLoggerState . LastLogWasAnError = true ;
74+
75+ // render the exception
76+ LogException ( logLevel , exception , formatter ) ;
77+ return ;
78+ }
79+
80+ // lock the messages to avoid concurrency issues
81+ lock ( taskLoggerState . PendingMessages )
82+ {
83+ // check if we received the dispose message
84+ if ( eventId is { Id : 0 , Name : "TaskScopeDisposed" } )
85+ {
86+ // at this point we need to flush if needed
87+ taskLoggerState . PendingMessages = new List < string > ( _taskLoggerFactory . MergePendingMessagesIfNeeded ( DateTimeOffset . Now , true , taskLoggerState . TaskId , taskLoggerState . PendingMessages . ToArray ( ) ) . GetAwaiter ( ) . GetResult ( ) ) ;
88+
89+ // ensure the task is finished now
90+ _taskLoggerFactory . UpdateTaskStatus ( taskLoggerState . TaskId , taskLoggerState . LastLogWasAnError ? TaskStatus . Failed : TaskStatus . Succeed ) . GetAwaiter ( ) . GetResult ( ) ;
91+ }
92+ // check if we need to flush the messages
93+ else if ( eventId is { Id : 0 , Name : "TaskScopeFlushRequired" } )
94+ {
95+ // at this point we need to flush if needed
96+ taskLoggerState . PendingMessages = new List < string > ( _taskLoggerFactory . MergePendingMessagesIfNeeded ( DateTimeOffset . Now , true , taskLoggerState . TaskId , taskLoggerState . PendingMessages . ToArray ( ) ) . GetAwaiter ( ) . GetResult ( ) ) ;
97+ }
98+ else
99+ {
100+ // get the formated message
101+ var msg = formatter ( state , exception ) ;
102+ if ( msg == null )
103+ return ;
104+
105+ // at this point we need to add the message to the scope
106+ taskLoggerState . PendingMessages . Add ( msg ) ;
107+
108+ // at this point we need to flush if needed
109+ taskLoggerState . PendingMessages = new List < string > ( _taskLoggerFactory . MergePendingMessagesIfNeeded ( DateTimeOffset . Now , false , taskLoggerState . TaskId , taskLoggerState . PendingMessages . ToArray ( ) ) . GetAwaiter ( ) . GetResult ( ) ) ;
110+ }
111+ }
63112 }
64-
65- // All levels are eanbled
66- public bool IsEnabled ( LogLevel logLevel ) => true ;
67-
68- // allow to log
69- public void Log < TState > ( LogLevel logLevel , EventId eventId , TState state , Exception ? exception , Func < TState , Exception ? , string > formatter )
113+
114+ private void LogException < TState > ( LogLevel logLevel , Exception exception , Func < TState , Exception ? , string > formatter , bool innerException = false )
70115 {
71- if ( _currentTaskLogger == null )
72- return ;
73-
74- // adjust the log level
75- var taskLogLevel = TaskLogLevel . Information ;
76- if ( logLevel == LogLevel . Error || logLevel == LogLevel . Critical )
77- taskLogLevel = TaskLogLevel . Error ;
78-
79- // log
80- _currentTaskLogger . Log ( taskLogLevel , formatter ( state , exception ) , exception ) ;
81-
82- // remember the exception
83- if ( logLevel == LogLevel . Error )
84- _lastLogWasAnError = true ;
116+ if ( innerException )
117+ this . Log ( logLevel , $ "Inner Exception: { exception . Message } ", null , formatter ) ;
85118 else
86- _lastLogWasAnError = false ;
119+ {
120+ this . Log ( logLevel , $ "Error with exception: { exception . Message } ", null , formatter ) ;
121+ try
122+ {
123+ this . Log ( logLevel , "Dumping Raw-JSON-Export of the exception" , null , formatter ) ;
124+ this . Log ( logLevel , JsonConvert . SerializeObject ( exception ) , null , formatter ) ;
125+ }
126+ catch ( Exception )
127+ {
128+ // The exception is catched without handling to prevent
129+ // crashed just because of invalid JSON message we try to log
130+ }
131+ }
87132
88- }
133+ if ( exception . StackTrace != null )
134+ {
135+ var splittedError = exception . StackTrace . Split ( '\n ' ) ;
136+ foreach ( var el in splittedError )
137+ this . Log ( logLevel , el , null , formatter ) ;
138+ }
89139
90- // ensure we write all backe
91- public void DisposeScope ( TaskLoggerState state , TaskLoggerScope scope )
92- {
93- if ( _currentTaskLogger != null )
94- {
95- // write all messages back
96- _currentTaskLogger . Dispose ( ) ;
97- _currentTaskLogger = null ;
140+ if ( exception . InnerException != null )
141+ LogException ( logLevel , exception . InnerException , formatter , true ) ;
142+ }
98143
99- // ensure the task is finished now
100- _taskLoggerFactory . UpdateTaskStatus ( state . TaskId , _lastLogWasAnError ? TaskStatus . Failed : TaskStatus . Succeed ) . GetAwaiter ( ) . GetResult ( ) ;
144+ public bool IsEnabled ( LogLevel logLevel )
145+ => true ;
101146
102- // reset the exception
103- _lastLogWasAnError = false ;
104- }
105- }
147+ public IDisposable ? BeginScope < TState > ( TState state ) where TState : notnull
148+ {
149+ return _scopeProvider . Push ( state ) ;
150+ }
106151 }
107- }
108-
152+ }
0 commit comments