1+ using System ;
2+ using System . Diagnostics ;
3+ using System . IO ;
4+
5+ namespace GeneralUpdate . Common . Internal ;
6+
7+ public static class GeneralTracer
8+ {
9+ private static readonly object _lockObj = new object ( ) ;
10+ private static string _currentLogDate ;
11+ private static TextWriterTraceListener _fileListener ;
12+
13+ static GeneralTracer ( )
14+ {
15+ Trace . Listeners . Clear ( ) ;
16+ Trace . Listeners . Add ( new TextWriterTraceListener ( Console . Out ) { Name = "ConsoleListener" } ) ;
17+ InitializeFileListener ( ) ;
18+
19+ if ( Debugger . IsAttached )
20+ Trace . Listeners . Add ( new DefaultTraceListener ( ) ) ;
21+
22+ Trace . AutoFlush = true ;
23+ }
24+
25+ private static void InitializeFileListener ( )
26+ {
27+ //Ensure that log files are rotated on a daily basis
28+ lock ( _lockObj )
29+ {
30+ var today = DateTime . Now . ToString ( "yyyy-MM-dd" ) ;
31+ if ( today == _currentLogDate && _fileListener != null )
32+ return ;
33+
34+ if ( _fileListener != null )
35+ {
36+ Trace . Listeners . Remove ( _fileListener ) ;
37+ _fileListener . Flush ( ) ;
38+ _fileListener . Close ( ) ;
39+ _fileListener . Dispose ( ) ;
40+ }
41+
42+ var logDir = Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Logs" ) ;
43+ Directory . CreateDirectory ( logDir ) ;
44+
45+ var logFileName = Path . Combine ( logDir , $ "generalupdate-trace { today } .log") ;
46+ _fileListener = new TextWriterTraceListener ( logFileName ) { Name = "FileListener" } ;
47+
48+ Trace . Listeners . Add ( _fileListener ) ;
49+ _currentLogDate = today ;
50+ }
51+ }
52+
53+ public static void Debug ( string message ) => WriteTraceMessage ( TraceLevel . Verbose , message ) ;
54+
55+ public static void Info ( string message ) => WriteTraceMessage ( TraceLevel . Info , message ) ;
56+
57+ public static void Warn ( string message ) => WriteTraceMessage ( TraceLevel . Warning , message ) ;
58+
59+ public static void Error ( string message ) => WriteTraceMessage ( TraceLevel . Error , message ) ;
60+
61+ public static void Fatal ( string message ) => WriteTraceMessage ( TraceLevel . Off , message ) ;
62+
63+ public static void Error ( string message , Exception ex )
64+ {
65+ var fullMessage = $ "{ message } { Environment . NewLine } Exception Details: { ex } ";
66+ WriteTraceMessage ( TraceLevel . Error , fullMessage ) ;
67+ }
68+
69+ public static void Fatal ( string message , Exception ex )
70+ {
71+ var fullMessage = $ "{ message } { Environment . NewLine } Exception Details: { ex } ";
72+ WriteTraceMessage ( TraceLevel . Off , fullMessage ) ;
73+ }
74+
75+ public static void SetTracingEnabled ( bool enabled )
76+ {
77+ lock ( _lockObj )
78+ {
79+ Trace . AutoFlush = enabled ;
80+ foreach ( TraceListener listener in Trace . Listeners )
81+ {
82+ listener . Filter = enabled ? null : new EventTypeFilter ( SourceLevels . Off ) ;
83+ }
84+ }
85+ }
86+
87+ private static void WriteTraceMessage ( TraceLevel level , string message )
88+ {
89+ InitializeFileListener ( ) ;
90+ var timestamp = DateTime . Now . ToString ( "yyyy-MM-dd HH:mm:ss.fff" ) ;
91+ var levelName = GetLevelName ( level ) ;
92+ var fullMessage = string . Empty ;
93+
94+ #if ! AOT
95+ try
96+ {
97+ var stackFrame = new StackFrame ( 2 , true ) ;
98+ var method = stackFrame . GetMethod ( ) ;
99+ var className = method . DeclaringType ? . Name ?? "UnknownType" ;
100+ var methodName = method . Name ;
101+ var lineNumber = stackFrame . GetFileLineNumber ( ) ;
102+
103+ var lineInfo = lineNumber > 0 ? $ "Line { lineNumber } " : "Line N/A (Line numbers may not be displayed in Release mode)" ;
104+ fullMessage = $ "[{ timestamp } ] [{ levelName } ] { className } .{ methodName } ({ lineInfo } ): { message } ";
105+ }
106+ catch ( Exception ex )
107+ {
108+ fullMessage = $ "[{ timestamp } ] [{ levelName } ] [Failed to obtain stack information: { ex . Message } ] : { message } ";
109+ }
110+ #endif
111+
112+ if ( string . IsNullOrEmpty ( fullMessage ) )
113+ fullMessage = $ "[{ timestamp } ] [{ levelName } ] : { message } ";
114+
115+ lock ( _lockObj )
116+ {
117+ Trace . WriteLine ( fullMessage ) ;
118+ }
119+ }
120+
121+ private static string GetLevelName ( TraceLevel level ) => level switch
122+ {
123+ TraceLevel . Verbose => "DEBUG" ,
124+ TraceLevel . Info => "INFO" ,
125+ TraceLevel . Warning => "WARN" ,
126+ TraceLevel . Error => "ERROR" ,
127+ TraceLevel . Off => "FATAL" ,
128+ _ => "UNKNOWN"
129+ } ;
130+
131+ public static void Dispose ( )
132+ {
133+ lock ( _lockObj )
134+ {
135+ if ( _fileListener is not null )
136+ {
137+ _fileListener . Flush ( ) ;
138+ _fileListener . Close ( ) ;
139+ _fileListener . Dispose ( ) ;
140+ _fileListener = null ;
141+ }
142+
143+ Trace . Listeners . Clear ( ) ;
144+ }
145+ }
146+ }
0 commit comments