Projekt

Obecné

Profil

Stáhnout (6.75 KB) Statistiky
| Větev: | Tag: | Revize:
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
}
(1-1/6)