Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 74bd1e40

Přidáno uživatelem Pultak před více než 2 roky(ů)

re #9439 logger code refactoring

Zobrazit rozdíly:

ld_client/LDClient/Program.cs
1
using LDClient;
2
using System;
1
using LDClient.utils;
2
using LDClient.utils.loggers;
3 3

  
4
namespace LDClient; 
4 5

  
5
class Program {
6
internal class Program {
6 7

  
7
    public static LDClient.ConfigLoader Config { get; set; }
8
    public static ConfigLoader Config { get; set; } = null!;
8 9

  
9 10
    // Main Method
10
    static public void Main() {
11
        Config = new LDClient.ConfigLoader();
11
    public static void Main() {
12
        Config = new ConfigLoader();
12 13

  
13 14
        while (true) {
14
            Logger.Current.Info("Ok");
15
            Logger.Current.Debug("Debug");
16
            Logger.Current.Error("Error");
15
            ALogger.Current.Info("Ok");
16
            ALogger.Current.Debug("Debug");
17
            ALogger.Current.Error("Error");
17 18
        }
18 19
    }
19 20
}
ld_client/LDClient/appsettings.json
7 7
        "Error": "Red"
8 8
      }
9 9
    },
10
    "LogChunkSize": 1000,
11
    "LogChunkMaxCount": 1,
12
    "LogArchiveMaxCount": 1,
13
    "LogCleanupPeriod": 1000,
10
    "LogChunkSize": 2097152,
11
    "LogChunkMaxCount": 2,
12
    "LogArchiveMaxCount": 10,
13
    "LogCleanupPeriod": 10,
14 14
    "LogVerbosityType": 2,
15 15
    "LogFlowType": 1
16 16
  }
ld_client/LDClient/utils/ConfigLoader.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Configuration;
4
using System.Linq;
5
using System.Text;
6
using System.Threading.Tasks;
1
using LDClient.utils.loggers;
7 2
using Microsoft.Extensions.Configuration;
8
using Microsoft.Extensions.Configuration.Json;
9 3

  
10
namespace LDClient {
11
    internal class ConfigurationLoader {
12

  
13
        private readonly string LOGGING_SECTION = "Logging";
4
namespace LDClient.utils {
5
    internal class ConfigLoader {
6
        private const string LoggingSection = "Logging";
14 7

  
15 8

  
16 9
        public int LogChunkSize { get; set; }
......
19 12

  
20 13
        public int LogCleanupPeriod { get; set; }
21 14

  
22
        private LogVerbosity _logVerbosity = LogVerbosity.Full;
23
        public LogVerbosity LogVerbosityType {
24
            get {
25
                return _logVerbosity;
26
            } 
27
            set 
28
                { _logVerbosity = value; 
29
            } 
30
        }
15
        public LogVerbosity LogVerbosityType { get; set; } = LogVerbosity.Full;
31 16

  
32
        private LogFlow _logFlowType = LogFlow.Console;
33
        public LogFlow LogFlowType { 
34
            get {
35
                return _logFlowType;
36
            } 
37
            set {
38
                _logFlowType = value;
39
            } 
40
        }
17
        public LogFlow LogFlowType { get; set; } = LogFlow.Console;
41 18

  
42
        public ConfigurationLoader() {
19
        public ConfigLoader() {
43 20
            var configuration = new ConfigurationBuilder()
44 21
                .AddJsonFile("appsettings.json")
45 22
                .Build();
46 23
            ReadAllSettings(configuration);
47 24
        }
48 25

  
49
        void ReadAllSettings(IConfigurationRoot configuration) {
26
        private void ReadAllSettings(IConfigurationRoot configuration) {
50 27

  
51 28
            try {
52
                var logging = configuration.GetSection(LOGGING_SECTION);
29
                var logging = configuration.GetSection(LoggingSection);
53 30
                //TODO: Exception handling
54
                LogChunkSize = Int32.Parse(logging["LogChunkSize"]);
55
                LogChunkMaxCount = Int32.Parse(logging["LogChunkMaxCount"]);
56
                LogArchiveMaxCount = Int32.Parse(logging["LogArchiveMaxCount"]);
57
                LogCleanupPeriod = Int32.Parse(logging["LogCleanupPeriod"]);
58
                LogFlowType = (LogFlow)Int32.Parse(logging["LogFlowType"]);
59
                LogVerbosityType = (LogVerbosity)Int32.Parse(logging["LogVerbosityType"]);
31
                LogChunkSize = int.Parse(logging["LogChunkSize"]);
32
                LogChunkMaxCount = int.Parse(logging["LogChunkMaxCount"]);
33
                LogArchiveMaxCount = int.Parse(logging["LogArchiveMaxCount"]);
34
                LogCleanupPeriod = int.Parse(logging["LogCleanupPeriod"]);
35
                LogFlowType = (LogFlow)int.Parse(logging["LogFlowType"]);
36
                LogVerbosityType = (LogVerbosity)int.Parse(logging["LogVerbosityType"]);
60 37

  
61 38

  
62 39
                Console.WriteLine("Configuration successfully loaded!");
63 40
            } catch (FormatException e) {
64 41
                //Console.WriteLine("Error reading app settings");
65 42
                //TODO: remove stacktrace print in production
66
                Console.WriteLine("Error during reading of configuration occured!" + e);
43
                Console.WriteLine("Error during reading of configuration occurred!" + e);
67 44
                throw new IOException("Reading of configuration file failed! " + e);
68 45
            }
69 46
        }
ld_client/LDClient/utils/Logger.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Globalization;
4
using System.IO.Compression;
5
using System.Linq;
6
using System.Text;
7
using System.Threading.Tasks;
8

  
9
namespace LDClient {
10
    enum LogVerbosity {
11
        None = 0,
12
        Exceptions,
13
        Full
14
    }
15

  
16
    public enum LogType {
17
        Info = 0,
18
        Debug,
19
        Error
20
    }
21

  
22
    enum LogFlow {
23
        Console = 0,
24
        File
25
    }
26

  
27

  
28
    public abstract class Logger : IDisposable {
29

  
30
        private LogVerbosity _verbosity;
31
        private LogFlow _logFlow;
32

  
33
        private Queue<Action> _queue = new Queue<Action>();
34
        private ManualResetEvent _hasNewItems = new ManualResetEvent(false);
35
        private ManualResetEvent _terminate = new ManualResetEvent(false);
36
        private ManualResetEvent _waiting = new ManualResetEvent(false);
37
        private Thread _loggingThread;
38

  
39
        private static readonly Lazy<Logger> _lazyLog = new Lazy<Logger>(()
40
            => {
41
                switch (Program.Config.LogFlowType) {
42
                    case LogFlow.File:
43
                        return new FileLogger();
44
                    case LogFlow.Console:
45
                    default:
46
                        return new ConsoleLogger();
47

  
48
                }
49
            }
50
        );
51

  
52
        public static Logger Current => _lazyLog.Value;
53

  
54
        protected Logger() {
55
            _verbosity = (LogVerbosity)Program.Config.LogVerbosityType;
56
            _logFlow = (LogFlow)Program.Config.LogFlowType;
57
            _loggingThread = new Thread(new ThreadStart(ProcessQueue)) { IsBackground = true };
58
            _loggingThread.Start();
59
        }
60

  
61
        public void Info(string message) {
62
            Log(message, LogType.Info);
63
        }
64

  
65
        public void Debug(string message) {
66
            Log(message, LogType.Debug);
67
        }
68

  
69
        public void Error(string message) {
70
            Log(message, LogType.Error);
71
        }
72

  
73
        public void Error(Exception e) {
74
            if (_verbosity != LogVerbosity.None) {
75
                Log(UnwrapExceptionMessages(e), LogType.Error);
76
            }
77
        }
78

  
79
        public override string ToString() => $"Logger settings: [Type: {this.GetType().Name}, Verbosity: {_verbosity}, ";
80

  
81
        protected abstract void CreateLog(string message);
82

  
83
        public void Flush() => _waiting.WaitOne();
84

  
85
        public void Dispose() {
86
            _terminate.Set();
87
            _loggingThread.Join();
88
        }
89

  
90
        protected virtual string ComposeLogRow(string message, LogType logType) =>
91
            $"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff", CultureInfo.InvariantCulture)} - {logType}] - {message}";
92

  
93
        protected virtual string UnwrapExceptionMessages(Exception ex) {
94
            if (ex == null)
95
                return string.Empty;
96

  
97
            return $"{ex}, Inner exception: {UnwrapExceptionMessages(ex.InnerException)} ";
98
        }
99

  
100
        private void ProcessQueue() {
101
            while (true) {
102
                _waiting.Set();
103
                int i = WaitHandle.WaitAny(new WaitHandle[] { _hasNewItems, _terminate });
104
                if (i == 1) return;
105
                _hasNewItems.Reset();
106
                _waiting.Reset();
107

  
108
                Queue<Action> queueCopy;
109
                lock (_queue) {
110
                    queueCopy = new Queue<Action>(_queue);
111
                    _queue.Clear();
112
                }
113

  
114
                foreach (var log in queueCopy) {
115
                    log();
116
                }
117
            }
118
        }
119

  
120
        private void Log(string message, LogType logType) {
121
            if (string.IsNullOrEmpty(message))
122
                return;
123

  
124
            var logRow = ComposeLogRow(message, logType);
125
            System.Diagnostics.Debug.WriteLine(logRow);
126

  
127
            if (_verbosity == LogVerbosity.Full) {
128
                lock (_queue)
129
                    _queue.Enqueue(() => CreateLog(logRow));
130

  
131
                _hasNewItems.Set();
132
            }
133
        }
134
    }
135

  
136
    class ConsoleLogger : Logger {
137
        protected override void CreateLog(string message) {
138
            Console.WriteLine(message);
139
        }
140
    }
141

  
142

  
143
    class FileLogger : Logger {
144

  
145
        private const string LogFolderName = "logs";
146
        private const string LogFileName = "app_info.log";
147
        private readonly int _logChunkSize = Program.Config.LogChunkSize;
148
        private readonly int _logChunkMaxCount = Program.Config.LogChunkMaxCount;
149
        private readonly int _logArchiveMaxCount = Program.Config.LogArchiveMaxCount;
150
        private readonly int _logCleanupPeriod = Program.Config.LogCleanupPeriod;
151

  
152
        private readonly string logFolderPath = Path.Combine(Path.GetTempPath(), $"ldClient\\{LogFolderName}");
153

  
154
        private bool _logDirExists = false;
155

  
156
        protected override void CreateLog(string message) {
157

  
158
            if (!_logDirExists) {
159
                _logDirExists = Directory.Exists(logFolderPath);
160
                if (!_logDirExists) {
161
                    Directory.CreateDirectory(logFolderPath);
162
                    _logDirExists = true;
163
                }
164
            }
165

  
166
            var logFilePath = Path.Combine(logFolderPath, LogFileName);
167

  
168
            Rotate(logFilePath);
169

  
170
            using (var sw = File.AppendText(logFilePath)) {
171
                sw.WriteLine(message);
172
            }
173
        }
174

  
175
        private void Rotate(string filePath) {
176
            if (!File.Exists(filePath))
177
                return;
178

  
179
            var fileInfo = new FileInfo(filePath);
180
            if (fileInfo.Length < _logChunkSize)
181
                return;
182

  
183
            var fileTime = DateTime.Now.ToString("dd-MM-yyyy,hh-mm-ss,fff");
184
            var rotatedPath = filePath.Replace(".log", $".{fileTime}");
185
            File.Move(filePath, rotatedPath);
186

  
187
            var folderPath = Path.GetDirectoryName(rotatedPath);
188
            var logFolderContent = new DirectoryInfo(folderPath).GetFileSystemInfos();
189

  
190
            var chunks = logFolderContent.Where(x => !x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase));
191

  
192
            if (chunks.Count() <= _logChunkMaxCount)
193
                return;
194

  
195
            var archiveFolderInfo = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(rotatedPath), $"{LogFolderName}_{fileTime}"));
196

  
197
            foreach (var chunk in chunks) {
198
                var destination = Path.Combine(archiveFolderInfo.FullName, chunk.Name);
199
                Directory.Move(chunk.FullName, destination);
200
            }
201

  
202
            ZipFile.CreateFromDirectory(archiveFolderInfo.FullName, Path.Combine(folderPath, $"{LogFolderName}_{fileTime}.zip"));
203
            Directory.Delete(archiveFolderInfo.FullName, true);
204

  
205
            var archives = logFolderContent.Where(x => x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)).ToArray();
206

  
207
            if (archives.Count() <= _logArchiveMaxCount)
208
                return;
209

  
210
            var oldestArchive = archives.OrderBy(x => x.CreationTime).First();
211
            var cleanupDate = oldestArchive.CreationTime.AddDays(_logCleanupPeriod);
212
            if (DateTime.Compare(cleanupDate, DateTime.Now) <= 0) {
213
                foreach (var file in logFolderContent) {
214
                    file.Delete();
215
                }
216
            } else
217
                File.Delete(oldestArchive.FullName);
218

  
219
        }
220

  
221
        public override string ToString() => $"{base.ToString()}, Chunk Size: {_logChunkSize}, Max chunk count: {_logChunkMaxCount}, Max log archive count: {_logArchiveMaxCount}, Cleanup period: {_logCleanupPeriod} days]";
222
    }
223

  
224

  
225
}
ld_client/LDClient/utils/loggers/ALogger.cs
1
using System.Globalization;
2

  
3
namespace LDClient.utils.loggers {
4

  
5
    public abstract class ALogger : IDisposable {
6

  
7
        private readonly LogVerbosity _verbosity;
8
        private readonly LogFlow _logFlow;
9

  
10
        private readonly Queue<Action> _queue = new();
11
        private readonly ManualResetEvent _hasNewItems = new(false);
12
        private readonly ManualResetEvent _terminate = new(false);
13
        private readonly ManualResetEvent _waiting = new(false);
14
        private readonly Thread _loggingThread;
15

  
16
        private static readonly Lazy<ALogger> LazyLog = new(()
17
            => {
18
                switch (Program.Config.LogFlowType) {
19
                    case LogFlow.File:
20
                        return new FileLogger();
21
                    case LogFlow.Console:
22
                    default:
23
                        return new ConsoleLogger();
24

  
25
                }
26
            }
27
        );
28

  
29
        public static ALogger Current => LazyLog.Value;
30

  
31
        protected ALogger() {
32
            _verbosity = Program.Config.LogVerbosityType;
33
            _logFlow = Program.Config.LogFlowType;
34
            _loggingThread = new Thread(ProcessQueue) { IsBackground = true };
35
            _loggingThread.Start();
36
        }
37

  
38
        public void Info(string message) {
39
            Log(message, LogType.Info);
40
        }
41

  
42
        public void Debug(string message) {
43
            Log(message, LogType.Debug);
44
        }
45

  
46
        public void Error(string message) {
47
            Log(message, LogType.Error);
48
        }
49

  
50
        public void Error(Exception e) {
51
            if (_verbosity != LogVerbosity.None) {
52
                Log(UnwrapExceptionMessages(e), LogType.Error);
53
            }
54
        }
55

  
56
        public override string ToString() => $"Logger settings: [Type: {this.GetType().Name}, Verbosity: {_verbosity}, ";
57

  
58
        protected abstract void CreateLog(string message);
59

  
60
        public void Flush() => _waiting.WaitOne();
61

  
62
        public void Dispose() {
63
            _terminate.Set();
64
            _loggingThread.Join();
65
        }
66

  
67
        protected virtual string ComposeLogRow(string message, LogType logType) =>
68
            $"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff", CultureInfo.InvariantCulture)} - {logType}] - {message}";
69

  
70
        protected virtual string UnwrapExceptionMessages(Exception? ex) =>
71
            ex == null ? string.Empty : $"{ex}, Inner exception: {UnwrapExceptionMessages(ex.InnerException)} ";
72
        
73

  
74
        private void ProcessQueue() {
75
            while (true) {
76
                _waiting.Set();
77
                var i = WaitHandle.WaitAny(new WaitHandle[] { _hasNewItems, _terminate });
78
                if (i == 1) return;
79
                _hasNewItems.Reset();
80
                _waiting.Reset();
81

  
82
                Queue<Action> queueCopy;
83
                lock (_queue) {
84
                    queueCopy = new Queue<Action>(_queue);
85
                    _queue.Clear();
86
                }
87

  
88
                foreach (var log in queueCopy) {
89
                    log();
90
                }
91
            }
92
        }
93

  
94
        private void Log(string message, LogType logType) {
95
            if (string.IsNullOrEmpty(message))
96
                return;
97

  
98
            var logRow = ComposeLogRow(message, logType);
99
            System.Diagnostics.Debug.WriteLine(logRow);
100

  
101
            if (_verbosity == LogVerbosity.Full) {
102
                lock (_queue)
103
                    _queue.Enqueue(() => CreateLog(logRow));
104

  
105
                _hasNewItems.Set();
106
            }
107
        }
108
    }
109
}
ld_client/LDClient/utils/loggers/ConsoleLogger.cs
1
namespace LDClient.utils.loggers {
2
    public class ConsoleLogger : ALogger {
3
        protected override void CreateLog(string message) {
4
            Console.WriteLine(message);
5
        }
6
    }
7
}
ld_client/LDClient/utils/loggers/FileLogger.cs
1
using System.IO.Compression;
2

  
3
namespace LDClient.utils.loggers; 
4

  
5
public class FileLogger : ALogger {
6

  
7
    private const string LogFolderName = "logs";
8
    private const string LogFileName = "app_info.log";
9
    private readonly int _logChunkSize = Program.Config.LogChunkSize;
10
    private readonly int _logChunkMaxCount = Program.Config.LogChunkMaxCount;
11
    private readonly int _logArchiveMaxCount = Program.Config.LogArchiveMaxCount;
12
    private readonly int _logCleanupPeriod = Program.Config.LogCleanupPeriod;
13

  
14
    private const string LogFolderPath = $"ldClient\\{LogFolderName}";
15

  
16
    private bool _logDirExists;
17

  
18
    protected override void CreateLog(string message) {
19

  
20
        if (!_logDirExists) {
21
            _logDirExists = Directory.Exists(LogFolderPath);
22
            if (!_logDirExists) {
23
                Directory.CreateDirectory(LogFolderPath);
24
                _logDirExists = true;
25
            }
26
        }
27

  
28
        var logFilePath = Path.Combine(LogFolderPath, LogFileName);
29

  
30
        Rotate(logFilePath);
31

  
32
        using var sw = File.AppendText(logFilePath);
33
        sw.WriteLine(message);
34
    }
35

  
36
    private void Rotate(string filePath) {
37
        if (!File.Exists(filePath)) {
38
            return;
39
        }
40

  
41
        var fileInfo = new FileInfo(filePath);
42
        if (fileInfo.Length < _logChunkSize) {
43
            return;
44
        }
45
        var fileTime = DateTime.Now.ToString("dd-MM-yyyy,hh-mm-ss,fff");
46
        var rotatedPath = filePath.Replace(".log", $".{fileTime}");
47
        File.Move(filePath, rotatedPath);
48

  
49
        var folderPath = Path.GetDirectoryName(rotatedPath);
50
        var logFolderContent = new DirectoryInfo(folderPath ?? string.Empty).GetFileSystemInfos();
51

  
52
        var chunks = logFolderContent.Where(x => 
53
            !x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase));
54

  
55
        if (chunks.Count() <= _logChunkMaxCount) {
56
            return;
57
        }
58

  
59
        Archive(chunks, rotatedPath, fileTime, folderPath);
60
        DeleteOldArchives(logFolderContent);
61
    }
62

  
63
    private void Archive(IEnumerable<FileSystemInfo> chunks, string rotatedPath, string fileTime, string? folderPath) {
64

  
65
        var archiveFolderInfo = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(rotatedPath) ?? LogFolderPath, $"{LogFolderName}_{fileTime}"));
66

  
67
        foreach (var chunk in chunks) {
68
            var destination = Path.Combine(archiveFolderInfo.FullName, chunk.Name);
69
            Directory.Move(chunk.FullName, destination);
70
        }
71

  
72
        ZipFile.CreateFromDirectory(archiveFolderInfo.FullName, Path.Combine(folderPath ?? LogFolderPath, $"{LogFolderName}_{fileTime}.zip"));
73
        Directory.Delete(archiveFolderInfo.FullName, true);
74
    }
75

  
76
    private void DeleteOldArchives(FileSystemInfo[] logFolderContent) {
77

  
78
        var archives = logFolderContent.Where(x => x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)).ToArray();
79

  
80
        if (archives.Length <= _logArchiveMaxCount)
81
            return;
82

  
83
        var oldestArchive = archives.OrderBy(x => x.CreationTime).First();
84
        var cleanupDate = oldestArchive.CreationTime.AddDays(_logCleanupPeriod);
85
        if (DateTime.Compare(cleanupDate, DateTime.Now) <= 0) {
86
            foreach (var file in logFolderContent) {
87
                file.Delete();
88
            }
89
        } else {
90
            File.Delete(oldestArchive.FullName);
91
        }
92
    }
93

  
94
    public override string ToString() => $"{base.ToString()}, Chunk Size: {_logChunkSize}, Max chunk count: {_logChunkMaxCount}, Max log archive count: {_logArchiveMaxCount}, Cleanup period: {_logCleanupPeriod} days]";
95
}
ld_client/LDClient/utils/loggers/LogFlow.cs
1
namespace LDClient.utils.loggers {
2
    public enum LogFlow {
3
        Console = 0,
4
        File
5
    }
6
}
ld_client/LDClient/utils/loggers/LogType.cs
1
namespace LDClient.utils.loggers {
2
    public enum LogType {
3
        Info = 0,
4
        Debug,
5
        Error
6
    }
7
}
ld_client/LDClient/utils/loggers/LogVerbosity.cs
1
namespace LDClient.utils.loggers {
2
    public enum LogVerbosity {
3
        None = 0,
4
        Exceptions,
5
        Full
6
    }
7
}

Také k dispozici: Unified diff