1
|
using System.Globalization;
|
2
|
|
3
|
namespace LDClient.utils.loggers {
|
4
|
|
5
|
/// <summary>
|
6
|
/// This class implements all abstract functions of the logger.
|
7
|
/// It contains all functions (error, info, debug) that are present in any other standard logger.
|
8
|
/// Class is used as singleton design pattern
|
9
|
/// </summary>
|
10
|
public abstract class ALogger : IDisposable {
|
11
|
|
12
|
/// <summary>
|
13
|
/// Logger verbosity type identifies how much information should be actually logged
|
14
|
/// </summary>
|
15
|
private readonly LogVerbosity _verbosity;
|
16
|
|
17
|
/// <summary>
|
18
|
/// Current type of logger
|
19
|
/// </summary>
|
20
|
private readonly LogFlow _logFlow;
|
21
|
|
22
|
/// <summary>
|
23
|
/// Queue of all not yet logged messages
|
24
|
/// </summary>
|
25
|
private readonly Queue<Action> _queue = new();
|
26
|
|
27
|
/// <summary>
|
28
|
/// Synchronization prime used to identify if the is anything new in the queue
|
29
|
/// </summary>
|
30
|
private readonly ManualResetEvent _hasNewItems = new(false);
|
31
|
/// <summary>
|
32
|
/// Synchronization prime used to identify if the logger should be terminated
|
33
|
/// </summary>
|
34
|
private readonly ManualResetEvent _terminate = new(false);
|
35
|
/// <summary>
|
36
|
/// Synchronization prime used to identify if the current thread is waiting
|
37
|
/// </summary>
|
38
|
private readonly ManualResetEvent _waiting = new(false);
|
39
|
|
40
|
/// <summary>
|
41
|
/// Thread instance of logging thread
|
42
|
/// </summary>
|
43
|
private readonly Thread _loggingThread;
|
44
|
|
45
|
/// <summary>
|
46
|
/// Instance of the default logger initialized with the lazy binding mechanism
|
47
|
/// </summary>
|
48
|
private static readonly Lazy<ALogger> LazyLog = new(()
|
49
|
=> {
|
50
|
switch (Program.Config.LogFlowType) {
|
51
|
case LogFlow.File:
|
52
|
return new FileLogger();
|
53
|
case LogFlow.Console:
|
54
|
default:
|
55
|
return new ConsoleLogger();
|
56
|
|
57
|
}
|
58
|
}
|
59
|
);
|
60
|
|
61
|
/// <summary>
|
62
|
/// Instance of the current logger type
|
63
|
/// </summary>
|
64
|
public static ALogger Current => LazyLog.Value;
|
65
|
|
66
|
/// <summary>
|
67
|
/// Singleton constructor that initialized and starts the logger with arguments parsed by the config loader
|
68
|
/// </summary>
|
69
|
protected ALogger() {
|
70
|
_verbosity = Program.Config.LogVerbosityType;
|
71
|
_logFlow = Program.Config.LogFlowType;
|
72
|
_loggingThread = new Thread(ProcessQueue) { IsBackground = true };
|
73
|
_loggingThread.Start();
|
74
|
}
|
75
|
|
76
|
/// <summary>
|
77
|
/// Creates new log with Info identifier
|
78
|
/// </summary>
|
79
|
/// <param name="message">Desired message to be logged</param>
|
80
|
public void Info(string message) {
|
81
|
Log(message, LogType.Info);
|
82
|
}
|
83
|
|
84
|
/// <summary>
|
85
|
/// Creates new log with Debug identifier
|
86
|
/// </summary>
|
87
|
/// <param name="message">Desired message to be logged</param>
|
88
|
public void Debug(string message) {
|
89
|
Log(message, LogType.Debug);
|
90
|
}
|
91
|
|
92
|
/// <summary>
|
93
|
/// Creates new log with Error identifier
|
94
|
/// </summary>
|
95
|
/// <param name="message">Desired message to be logged</param>
|
96
|
public void Error(string message) {
|
97
|
Log(message, LogType.Error);
|
98
|
}
|
99
|
|
100
|
/// <summary>
|
101
|
/// Creates new log from the catched exception
|
102
|
/// </summary>
|
103
|
/// <param name="e">catched exception tha should be logged</param>
|
104
|
public void Error(Exception e) {
|
105
|
if (_verbosity != LogVerbosity.None) {
|
106
|
Log(UnwrapExceptionMessages(e), LogType.Error);
|
107
|
}
|
108
|
}
|
109
|
|
110
|
/// <summary>
|
111
|
/// Creates new string info about current logger configuration
|
112
|
/// </summary>
|
113
|
/// <returns></returns>
|
114
|
public override string ToString() => $"Logger settings: [Type: {this.GetType().Name}, Verbosity: {_verbosity}, ";
|
115
|
|
116
|
protected abstract void CreateLog(string message);
|
117
|
|
118
|
/// <summary>
|
119
|
/// Waits for the logger to finish the logging
|
120
|
/// </summary>
|
121
|
public void Flush() => _waiting.WaitOne();
|
122
|
|
123
|
/// <summary>
|
124
|
/// Function stops the logger thread
|
125
|
/// </summary>
|
126
|
public void Dispose() {
|
127
|
_terminate.Set();
|
128
|
_loggingThread.Join();
|
129
|
}
|
130
|
|
131
|
/// <summary>
|
132
|
/// Composes the log with actual timestamp, log type and its main message
|
133
|
/// </summary>
|
134
|
/// <param name="message">main message of the log</param>
|
135
|
/// <param name="logType">Type of the logged message</param>
|
136
|
/// <returns></returns>
|
137
|
protected virtual string ComposeLogRow(string message, LogType logType) =>
|
138
|
$"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff", CultureInfo.InvariantCulture)} - {logType}] - {message}";
|
139
|
|
140
|
/// <summary>
|
141
|
/// Function creates log from the catched exception
|
142
|
/// </summary>
|
143
|
/// <param name="ex">catched exception tha should be logged</param>
|
144
|
/// <returns></returns>
|
145
|
protected virtual string UnwrapExceptionMessages(Exception? ex) =>
|
146
|
ex == null ? string.Empty : $"{ex}, Inner exception: {UnwrapExceptionMessages(ex.InnerException)} ";
|
147
|
|
148
|
/// <summary>
|
149
|
/// Function that periodically processes message queue
|
150
|
/// </summary>
|
151
|
private void ProcessQueue() {
|
152
|
while (true) {
|
153
|
_waiting.Set();
|
154
|
//wait until there is new item in the queue or until termination invoked
|
155
|
var i = WaitHandle.WaitAny(new WaitHandle[] { _hasNewItems, _terminate });
|
156
|
//was the termination invoked?
|
157
|
if (i == 1) return;
|
158
|
_hasNewItems.Reset();
|
159
|
_waiting.Reset();
|
160
|
|
161
|
Queue<Action> queueCopy;
|
162
|
lock (_queue) {
|
163
|
queueCopy = new Queue<Action>(_queue);
|
164
|
_queue.Clear();
|
165
|
}
|
166
|
|
167
|
foreach (var log in queueCopy) {
|
168
|
log();
|
169
|
}
|
170
|
}
|
171
|
}
|
172
|
|
173
|
/// <summary>
|
174
|
/// Creates new log string from the current timestamp
|
175
|
/// </summary>
|
176
|
/// <param name="message"></param>
|
177
|
/// <param name="logType"></param>
|
178
|
private void Log(string message, LogType logType) {
|
179
|
if (string.IsNullOrEmpty(message))
|
180
|
return;
|
181
|
|
182
|
var logRow = ComposeLogRow(message, logType);
|
183
|
System.Diagnostics.Debug.WriteLine(logRow);
|
184
|
|
185
|
if (_verbosity == LogVerbosity.Full) {
|
186
|
lock (_queue)
|
187
|
_queue.Enqueue(() => CreateLog(logRow));
|
188
|
|
189
|
_hasNewItems.Set();
|
190
|
}
|
191
|
}
|
192
|
}
|
193
|
}
|