Skip to content

Commit 89bec22

Browse files
committed
Add GeneralTracer for logging and tracing
Introduces the GeneralTracer static class to provide thread-safe logging and tracing functionality with daily log file rotation, multiple log levels, and stack trace information. Includes methods for debug, info, warning, error, and fatal logging, as well as support for enabling/disabling tracing and proper resource disposal.
1 parent 896063d commit 89bec22

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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

Comments
 (0)