From 715205760dd3f78b9f7056f92f1d102cc6f736c6 Mon Sep 17 00:00:00 2001
From: Pultak <pultak5@gmail.com>
Date: Sat, 2 Apr 2022 15:07:38 +0200
Subject: [PATCH 1/3] re #9439 Implementation of console and rotating file
 logger

---
 ld_client/LDClient/Program.cs      |  12 +-
 ld_client/LDClient/utils/Logger.cs | 225 +++++++++++++++++++++++++++++
 2 files changed, 235 insertions(+), 2 deletions(-)
 create mode 100644 ld_client/LDClient/utils/Logger.cs

diff --git a/ld_client/LDClient/Program.cs b/ld_client/LDClient/Program.cs
index 22069e5..2d372d9 100644
--- a/ld_client/LDClient/Program.cs
+++ b/ld_client/LDClient/Program.cs
@@ -1,11 +1,19 @@
-using System;
+using LDClient;
+using System;
 
 
 class Program {
 
+    public static LDClient.ConfigLoader Config { get; set; }
+
     // Main Method
     static public void Main() {
+        Config = new LDClient.ConfigLoader();
 
-        Console.WriteLine("Main Method");
+        while (true) {
+            Logger.Current.Info("Ok");
+            Logger.Current.Debug("Debug");
+            Logger.Current.Error("Error");
+        }
     }
 }
\ No newline at end of file
diff --git a/ld_client/LDClient/utils/Logger.cs b/ld_client/LDClient/utils/Logger.cs
new file mode 100644
index 0000000..aaf0ea1
--- /dev/null
+++ b/ld_client/LDClient/utils/Logger.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LDClient {
+    enum LogVerbosity {
+        None = 0,
+        Exceptions,
+        Full
+    }
+
+    public enum LogType {
+        Info = 0,
+        Debug,
+        Error
+    }
+
+    enum LogFlow {
+        Console = 0,
+        File
+    }
+
+
+    public abstract class Logger : IDisposable {
+
+        private LogVerbosity _verbosity;
+        private LogFlow _logFlow;
+
+        private Queue<Action> _queue = new Queue<Action>();
+        private ManualResetEvent _hasNewItems = new ManualResetEvent(false);
+        private ManualResetEvent _terminate = new ManualResetEvent(false);
+        private ManualResetEvent _waiting = new ManualResetEvent(false);
+        private Thread _loggingThread;
+
+        private static readonly Lazy<Logger> _lazyLog = new Lazy<Logger>(()
+            => {
+                switch (Program.Config.LogFlowType) {
+                    case LogFlow.File:
+                        return new FileLogger();
+                    case LogFlow.Console:
+                    default:
+                        return new ConsoleLogger();
+
+                }
+            }
+        );
+
+        public static Logger Current => _lazyLog.Value;
+
+        protected Logger() {
+            _verbosity = (LogVerbosity)Program.Config.LogVerbosityType;
+            _logFlow = (LogFlow)Program.Config.LogFlowType;
+            _loggingThread = new Thread(new ThreadStart(ProcessQueue)) { IsBackground = true };
+            _loggingThread.Start();
+        }
+
+        public void Info(string message) {
+            Log(message, LogType.Info);
+        }
+
+        public void Debug(string message) {
+            Log(message, LogType.Debug);
+        }
+
+        public void Error(string message) {
+            Log(message, LogType.Error);
+        }
+
+        public void Error(Exception e) {
+            if (_verbosity != LogVerbosity.None) {
+                Log(UnwrapExceptionMessages(e), LogType.Error);
+            }
+        }
+
+        public override string ToString() => $"Logger settings: [Type: {this.GetType().Name}, Verbosity: {_verbosity}, ";
+
+        protected abstract void CreateLog(string message);
+
+        public void Flush() => _waiting.WaitOne();
+
+        public void Dispose() {
+            _terminate.Set();
+            _loggingThread.Join();
+        }
+
+        protected virtual string ComposeLogRow(string message, LogType logType) =>
+            $"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff", CultureInfo.InvariantCulture)} - {logType}] - {message}";
+
+        protected virtual string UnwrapExceptionMessages(Exception ex) {
+            if (ex == null)
+                return string.Empty;
+
+            return $"{ex}, Inner exception: {UnwrapExceptionMessages(ex.InnerException)} ";
+        }
+
+        private void ProcessQueue() {
+            while (true) {
+                _waiting.Set();
+                int i = WaitHandle.WaitAny(new WaitHandle[] { _hasNewItems, _terminate });
+                if (i == 1) return;
+                _hasNewItems.Reset();
+                _waiting.Reset();
+
+                Queue<Action> queueCopy;
+                lock (_queue) {
+                    queueCopy = new Queue<Action>(_queue);
+                    _queue.Clear();
+                }
+
+                foreach (var log in queueCopy) {
+                    log();
+                }
+            }
+        }
+
+        private void Log(string message, LogType logType) {
+            if (string.IsNullOrEmpty(message))
+                return;
+
+            var logRow = ComposeLogRow(message, logType);
+            System.Diagnostics.Debug.WriteLine(logRow);
+
+            if (_verbosity == LogVerbosity.Full) {
+                lock (_queue)
+                    _queue.Enqueue(() => CreateLog(logRow));
+
+                _hasNewItems.Set();
+            }
+        }
+    }
+
+    class ConsoleLogger : Logger {
+        protected override void CreateLog(string message) {
+            Console.WriteLine(message);
+        }
+    }
+
+
+    class FileLogger : Logger {
+
+        private const string LogFolderName = "logs";
+        private const string LogFileName = "app_info.log";
+        private readonly int _logChunkSize = Program.Config.LogChunkSize;
+        private readonly int _logChunkMaxCount = Program.Config.LogChunkMaxCount;
+        private readonly int _logArchiveMaxCount = Program.Config.LogArchiveMaxCount;
+        private readonly int _logCleanupPeriod = Program.Config.LogCleanupPeriod;
+
+        private readonly string logFolderPath = Path.Combine(Path.GetTempPath(), $"ldClient\\{LogFolderName}");
+
+        private bool _logDirExists = false;
+
+        protected override void CreateLog(string message) {
+
+            if (!_logDirExists) {
+                _logDirExists = Directory.Exists(logFolderPath);
+                if (!_logDirExists) {
+                    Directory.CreateDirectory(logFolderPath);
+                    _logDirExists = true;
+                }
+            }
+
+            var logFilePath = Path.Combine(logFolderPath, LogFileName);
+
+            Rotate(logFilePath);
+
+            using (var sw = File.AppendText(logFilePath)) {
+                sw.WriteLine(message);
+            }
+        }
+
+        private void Rotate(string filePath) {
+            if (!File.Exists(filePath))
+                return;
+
+            var fileInfo = new FileInfo(filePath);
+            if (fileInfo.Length < _logChunkSize)
+                return;
+
+            var fileTime = DateTime.Now.ToString("dd-MM-yyyy,hh-mm-ss,fff");
+            var rotatedPath = filePath.Replace(".log", $".{fileTime}");
+            File.Move(filePath, rotatedPath);
+
+            var folderPath = Path.GetDirectoryName(rotatedPath);
+            var logFolderContent = new DirectoryInfo(folderPath).GetFileSystemInfos();
+
+            var chunks = logFolderContent.Where(x => !x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase));
+
+            if (chunks.Count() <= _logChunkMaxCount)
+                return;
+
+            var archiveFolderInfo = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(rotatedPath), $"{LogFolderName}_{fileTime}"));
+
+            foreach (var chunk in chunks) {
+                var destination = Path.Combine(archiveFolderInfo.FullName, chunk.Name);
+                Directory.Move(chunk.FullName, destination);
+            }
+
+            ZipFile.CreateFromDirectory(archiveFolderInfo.FullName, Path.Combine(folderPath, $"{LogFolderName}_{fileTime}.zip"));
+            Directory.Delete(archiveFolderInfo.FullName, true);
+
+            var archives = logFolderContent.Where(x => x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)).ToArray();
+
+            if (archives.Count() <= _logArchiveMaxCount)
+                return;
+
+            var oldestArchive = archives.OrderBy(x => x.CreationTime).First();
+            var cleanupDate = oldestArchive.CreationTime.AddDays(_logCleanupPeriod);
+            if (DateTime.Compare(cleanupDate, DateTime.Now) <= 0) {
+                foreach (var file in logFolderContent) {
+                    file.Delete();
+                }
+            } else
+                File.Delete(oldestArchive.FullName);
+
+        }
+
+        public override string ToString() => $"{base.ToString()}, Chunk Size: {_logChunkSize}, Max chunk count: {_logChunkMaxCount}, Max log archive count: {_logArchiveMaxCount}, Cleanup period: {_logCleanupPeriod} days]";
+    }
+
+
+}
-- 
GitLab


From d0cf94768067a25abd98e78c3775d418c154774e Mon Sep 17 00:00:00 2001
From: Pultak <pultak5@gmail.com>
Date: Sat, 2 Apr 2022 01:27:27 +0200
Subject: [PATCH 2/3] re #9440 initial config manager definition

---
 ld_client/LDClient/LDClient.csproj       | 13 ++++-
 ld_client/LDClient/appsettings.json      | 17 ++++++
 ld_client/LDClient/utils/ConfigLoader.cs | 72 ++++++++++++++++++++++++
 ld_client/LauterbachDebuggerClient.sln   |  2 +-
 4 files changed, 102 insertions(+), 2 deletions(-)
 create mode 100644 ld_client/LDClient/appsettings.json
 create mode 100644 ld_client/LDClient/utils/ConfigLoader.cs

diff --git a/ld_client/LDClient/LDClient.csproj b/ld_client/LDClient/LDClient.csproj
index 9b3c77f..3db4860 100644
--- a/ld_client/LDClient/LDClient.csproj
+++ b/ld_client/LDClient/LDClient.csproj
@@ -7,6 +7,16 @@
     <Nullable>enable</Nullable>
   </PropertyGroup>
 
+  <ItemGroup>
+    <None Remove="appsettings.json" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <EmbeddedResource Include="appsettings.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </EmbeddedResource>
+  </ItemGroup>
+
   <ItemGroup>
     <Reference Include="t32apinet">
       <HintPath>..\dotnet\t32apinet\bin\Release\t32apinet.dll</HintPath>
@@ -14,7 +24,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Folder Include="NewFolder\" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
+    <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
   </ItemGroup>
 
 </Project>
diff --git a/ld_client/LDClient/appsettings.json b/ld_client/LDClient/appsettings.json
new file mode 100644
index 0000000..505db61
--- /dev/null
+++ b/ld_client/LDClient/appsettings.json
@@ -0,0 +1,17 @@
+{
+  "Logging": {
+    "ColorConsole": {
+      "LogLevels": {
+        "Information": "DarkGreen",
+        "Warning": "Cyan",
+        "Error": "Red"
+      }
+    },
+    "LogChunkSize": 1000,
+    "LogChunkMaxCount": 1,
+    "LogArchiveMaxCount": 1,
+    "LogCleanupPeriod": 1000,
+    "LogVerbosityType": 2,
+    "LogFlowType": 1
+  }
+}
\ No newline at end of file
diff --git a/ld_client/LDClient/utils/ConfigLoader.cs b/ld_client/LDClient/utils/ConfigLoader.cs
new file mode 100644
index 0000000..d6502ac
--- /dev/null
+++ b/ld_client/LDClient/utils/ConfigLoader.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Configuration.Json;
+
+namespace LDClient {
+    internal class ConfigurationLoader {
+
+        private readonly string LOGGING_SECTION = "Logging";
+
+
+        public int LogChunkSize { get; set; }
+        public int LogChunkMaxCount { get; set; }
+        public int LogArchiveMaxCount { get; set; }
+
+        public int LogCleanupPeriod { get; set; }
+
+        private LogVerbosity _logVerbosity = LogVerbosity.Full;
+        public LogVerbosity LogVerbosityType {
+            get {
+                return _logVerbosity;
+            } 
+            set 
+                { _logVerbosity = value; 
+            } 
+        }
+
+        private LogFlow _logFlowType = LogFlow.Console;
+        public LogFlow LogFlowType { 
+            get {
+                return _logFlowType;
+            } 
+            set {
+                _logFlowType = value;
+            } 
+        }
+
+        public ConfigurationLoader() {
+            var configuration = new ConfigurationBuilder()
+                .AddJsonFile("appsettings.json")
+                .Build();
+            ReadAllSettings(configuration);
+        }
+
+        void ReadAllSettings(IConfigurationRoot configuration) {
+
+            try {
+                var logging = configuration.GetSection(LOGGING_SECTION);
+                //TODO: Exception handling
+                LogChunkSize = Int32.Parse(logging["LogChunkSize"]);
+                LogChunkMaxCount = Int32.Parse(logging["LogChunkMaxCount"]);
+                LogArchiveMaxCount = Int32.Parse(logging["LogArchiveMaxCount"]);
+                LogCleanupPeriod = Int32.Parse(logging["LogCleanupPeriod"]);
+                LogFlowType = (LogFlow)Int32.Parse(logging["LogFlowType"]);
+                LogVerbosityType = (LogVerbosity)Int32.Parse(logging["LogVerbosityType"]);
+
+
+                Console.WriteLine("Configuration successfully loaded!");
+            } catch (FormatException e) {
+                //Console.WriteLine("Error reading app settings");
+                //TODO: remove stacktrace print in production
+                Console.WriteLine("Error during reading of configuration occured!" + e);
+                throw new IOException("Reading of configuration file failed! " + e);
+            }
+        }
+
+    }
+}
diff --git a/ld_client/LauterbachDebuggerClient.sln b/ld_client/LauterbachDebuggerClient.sln
index d62277c..6e2f143 100644
--- a/ld_client/LauterbachDebuggerClient.sln
+++ b/ld_client/LauterbachDebuggerClient.sln
@@ -5,7 +5,7 @@ VisualStudioVersion = 17.1.32210.238
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LDClient", "LDClient\LDClient.csproj", "{26616C14-A61B-4611-8CD0-D29837FA34E7}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LDClientTests", "LDClientTests\LDClientTests.csproj", "{5A1DB888-70E5-41E8-A900-FC22B51727F8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LDClientTests", "LDClientTests\LDClientTests.csproj", "{5A1DB888-70E5-41E8-A900-FC22B51727F8}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-- 
GitLab


From 74bd1e40820cf9bf2c808f4946a0dec8f2013d0c Mon Sep 17 00:00:00 2001
From: Pultak <pultak5@gmail.com>
Date: Sat, 2 Apr 2022 18:19:27 +0200
Subject: [PATCH 3/3] re #9439 logger code refactoring

---
 ld_client/LDClient/Program.cs                 |  19 +-
 ld_client/LDClient/appsettings.json           |   8 +-
 ld_client/LDClient/utils/ConfigLoader.cs      |  55 ++---
 ld_client/LDClient/utils/Logger.cs            | 225 ------------------
 ld_client/LDClient/utils/loggers/ALogger.cs   | 109 +++++++++
 .../LDClient/utils/loggers/ConsoleLogger.cs   |   7 +
 .../LDClient/utils/loggers/FileLogger.cs      |  95 ++++++++
 ld_client/LDClient/utils/loggers/LogFlow.cs   |   6 +
 ld_client/LDClient/utils/loggers/LogType.cs   |   7 +
 .../LDClient/utils/loggers/LogVerbosity.cs    |   7 +
 10 files changed, 261 insertions(+), 277 deletions(-)
 delete mode 100644 ld_client/LDClient/utils/Logger.cs
 create mode 100644 ld_client/LDClient/utils/loggers/ALogger.cs
 create mode 100644 ld_client/LDClient/utils/loggers/ConsoleLogger.cs
 create mode 100644 ld_client/LDClient/utils/loggers/FileLogger.cs
 create mode 100644 ld_client/LDClient/utils/loggers/LogFlow.cs
 create mode 100644 ld_client/LDClient/utils/loggers/LogType.cs
 create mode 100644 ld_client/LDClient/utils/loggers/LogVerbosity.cs

diff --git a/ld_client/LDClient/Program.cs b/ld_client/LDClient/Program.cs
index 2d372d9..4030157 100644
--- a/ld_client/LDClient/Program.cs
+++ b/ld_client/LDClient/Program.cs
@@ -1,19 +1,20 @@
-using LDClient;
-using System;
+using LDClient.utils;
+using LDClient.utils.loggers;
 
+namespace LDClient; 
 
-class Program {
+internal class Program {
 
-    public static LDClient.ConfigLoader Config { get; set; }
+    public static ConfigLoader Config { get; set; } = null!;
 
     // Main Method
-    static public void Main() {
-        Config = new LDClient.ConfigLoader();
+    public static void Main() {
+        Config = new ConfigLoader();
 
         while (true) {
-            Logger.Current.Info("Ok");
-            Logger.Current.Debug("Debug");
-            Logger.Current.Error("Error");
+            ALogger.Current.Info("Ok");
+            ALogger.Current.Debug("Debug");
+            ALogger.Current.Error("Error");
         }
     }
 }
\ No newline at end of file
diff --git a/ld_client/LDClient/appsettings.json b/ld_client/LDClient/appsettings.json
index 505db61..01b0de4 100644
--- a/ld_client/LDClient/appsettings.json
+++ b/ld_client/LDClient/appsettings.json
@@ -7,10 +7,10 @@
         "Error": "Red"
       }
     },
-    "LogChunkSize": 1000,
-    "LogChunkMaxCount": 1,
-    "LogArchiveMaxCount": 1,
-    "LogCleanupPeriod": 1000,
+    "LogChunkSize": 2097152,
+    "LogChunkMaxCount": 2,
+    "LogArchiveMaxCount": 10,
+    "LogCleanupPeriod": 10,
     "LogVerbosityType": 2,
     "LogFlowType": 1
   }
diff --git a/ld_client/LDClient/utils/ConfigLoader.cs b/ld_client/LDClient/utils/ConfigLoader.cs
index d6502ac..8005130 100644
--- a/ld_client/LDClient/utils/ConfigLoader.cs
+++ b/ld_client/LDClient/utils/ConfigLoader.cs
@@ -1,16 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Configuration;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using LDClient.utils.loggers;
 using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Configuration.Json;
 
-namespace LDClient {
-    internal class ConfigurationLoader {
-
-        private readonly string LOGGING_SECTION = "Logging";
+namespace LDClient.utils {
+    internal class ConfigLoader {
+        private const string LoggingSection = "Logging";
 
 
         public int LogChunkSize { get; set; }
@@ -19,51 +12,35 @@ namespace LDClient {
 
         public int LogCleanupPeriod { get; set; }
 
-        private LogVerbosity _logVerbosity = LogVerbosity.Full;
-        public LogVerbosity LogVerbosityType {
-            get {
-                return _logVerbosity;
-            } 
-            set 
-                { _logVerbosity = value; 
-            } 
-        }
+        public LogVerbosity LogVerbosityType { get; set; } = LogVerbosity.Full;
 
-        private LogFlow _logFlowType = LogFlow.Console;
-        public LogFlow LogFlowType { 
-            get {
-                return _logFlowType;
-            } 
-            set {
-                _logFlowType = value;
-            } 
-        }
+        public LogFlow LogFlowType { get; set; } = LogFlow.Console;
 
-        public ConfigurationLoader() {
+        public ConfigLoader() {
             var configuration = new ConfigurationBuilder()
                 .AddJsonFile("appsettings.json")
                 .Build();
             ReadAllSettings(configuration);
         }
 
-        void ReadAllSettings(IConfigurationRoot configuration) {
+        private void ReadAllSettings(IConfigurationRoot configuration) {
 
             try {
-                var logging = configuration.GetSection(LOGGING_SECTION);
+                var logging = configuration.GetSection(LoggingSection);
                 //TODO: Exception handling
-                LogChunkSize = Int32.Parse(logging["LogChunkSize"]);
-                LogChunkMaxCount = Int32.Parse(logging["LogChunkMaxCount"]);
-                LogArchiveMaxCount = Int32.Parse(logging["LogArchiveMaxCount"]);
-                LogCleanupPeriod = Int32.Parse(logging["LogCleanupPeriod"]);
-                LogFlowType = (LogFlow)Int32.Parse(logging["LogFlowType"]);
-                LogVerbosityType = (LogVerbosity)Int32.Parse(logging["LogVerbosityType"]);
+                LogChunkSize = int.Parse(logging["LogChunkSize"]);
+                LogChunkMaxCount = int.Parse(logging["LogChunkMaxCount"]);
+                LogArchiveMaxCount = int.Parse(logging["LogArchiveMaxCount"]);
+                LogCleanupPeriod = int.Parse(logging["LogCleanupPeriod"]);
+                LogFlowType = (LogFlow)int.Parse(logging["LogFlowType"]);
+                LogVerbosityType = (LogVerbosity)int.Parse(logging["LogVerbosityType"]);
 
 
                 Console.WriteLine("Configuration successfully loaded!");
             } catch (FormatException e) {
                 //Console.WriteLine("Error reading app settings");
                 //TODO: remove stacktrace print in production
-                Console.WriteLine("Error during reading of configuration occured!" + e);
+                Console.WriteLine("Error during reading of configuration occurred!" + e);
                 throw new IOException("Reading of configuration file failed! " + e);
             }
         }
diff --git a/ld_client/LDClient/utils/Logger.cs b/ld_client/LDClient/utils/Logger.cs
deleted file mode 100644
index aaf0ea1..0000000
--- a/ld_client/LDClient/utils/Logger.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO.Compression;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace LDClient {
-    enum LogVerbosity {
-        None = 0,
-        Exceptions,
-        Full
-    }
-
-    public enum LogType {
-        Info = 0,
-        Debug,
-        Error
-    }
-
-    enum LogFlow {
-        Console = 0,
-        File
-    }
-
-
-    public abstract class Logger : IDisposable {
-
-        private LogVerbosity _verbosity;
-        private LogFlow _logFlow;
-
-        private Queue<Action> _queue = new Queue<Action>();
-        private ManualResetEvent _hasNewItems = new ManualResetEvent(false);
-        private ManualResetEvent _terminate = new ManualResetEvent(false);
-        private ManualResetEvent _waiting = new ManualResetEvent(false);
-        private Thread _loggingThread;
-
-        private static readonly Lazy<Logger> _lazyLog = new Lazy<Logger>(()
-            => {
-                switch (Program.Config.LogFlowType) {
-                    case LogFlow.File:
-                        return new FileLogger();
-                    case LogFlow.Console:
-                    default:
-                        return new ConsoleLogger();
-
-                }
-            }
-        );
-
-        public static Logger Current => _lazyLog.Value;
-
-        protected Logger() {
-            _verbosity = (LogVerbosity)Program.Config.LogVerbosityType;
-            _logFlow = (LogFlow)Program.Config.LogFlowType;
-            _loggingThread = new Thread(new ThreadStart(ProcessQueue)) { IsBackground = true };
-            _loggingThread.Start();
-        }
-
-        public void Info(string message) {
-            Log(message, LogType.Info);
-        }
-
-        public void Debug(string message) {
-            Log(message, LogType.Debug);
-        }
-
-        public void Error(string message) {
-            Log(message, LogType.Error);
-        }
-
-        public void Error(Exception e) {
-            if (_verbosity != LogVerbosity.None) {
-                Log(UnwrapExceptionMessages(e), LogType.Error);
-            }
-        }
-
-        public override string ToString() => $"Logger settings: [Type: {this.GetType().Name}, Verbosity: {_verbosity}, ";
-
-        protected abstract void CreateLog(string message);
-
-        public void Flush() => _waiting.WaitOne();
-
-        public void Dispose() {
-            _terminate.Set();
-            _loggingThread.Join();
-        }
-
-        protected virtual string ComposeLogRow(string message, LogType logType) =>
-            $"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff", CultureInfo.InvariantCulture)} - {logType}] - {message}";
-
-        protected virtual string UnwrapExceptionMessages(Exception ex) {
-            if (ex == null)
-                return string.Empty;
-
-            return $"{ex}, Inner exception: {UnwrapExceptionMessages(ex.InnerException)} ";
-        }
-
-        private void ProcessQueue() {
-            while (true) {
-                _waiting.Set();
-                int i = WaitHandle.WaitAny(new WaitHandle[] { _hasNewItems, _terminate });
-                if (i == 1) return;
-                _hasNewItems.Reset();
-                _waiting.Reset();
-
-                Queue<Action> queueCopy;
-                lock (_queue) {
-                    queueCopy = new Queue<Action>(_queue);
-                    _queue.Clear();
-                }
-
-                foreach (var log in queueCopy) {
-                    log();
-                }
-            }
-        }
-
-        private void Log(string message, LogType logType) {
-            if (string.IsNullOrEmpty(message))
-                return;
-
-            var logRow = ComposeLogRow(message, logType);
-            System.Diagnostics.Debug.WriteLine(logRow);
-
-            if (_verbosity == LogVerbosity.Full) {
-                lock (_queue)
-                    _queue.Enqueue(() => CreateLog(logRow));
-
-                _hasNewItems.Set();
-            }
-        }
-    }
-
-    class ConsoleLogger : Logger {
-        protected override void CreateLog(string message) {
-            Console.WriteLine(message);
-        }
-    }
-
-
-    class FileLogger : Logger {
-
-        private const string LogFolderName = "logs";
-        private const string LogFileName = "app_info.log";
-        private readonly int _logChunkSize = Program.Config.LogChunkSize;
-        private readonly int _logChunkMaxCount = Program.Config.LogChunkMaxCount;
-        private readonly int _logArchiveMaxCount = Program.Config.LogArchiveMaxCount;
-        private readonly int _logCleanupPeriod = Program.Config.LogCleanupPeriod;
-
-        private readonly string logFolderPath = Path.Combine(Path.GetTempPath(), $"ldClient\\{LogFolderName}");
-
-        private bool _logDirExists = false;
-
-        protected override void CreateLog(string message) {
-
-            if (!_logDirExists) {
-                _logDirExists = Directory.Exists(logFolderPath);
-                if (!_logDirExists) {
-                    Directory.CreateDirectory(logFolderPath);
-                    _logDirExists = true;
-                }
-            }
-
-            var logFilePath = Path.Combine(logFolderPath, LogFileName);
-
-            Rotate(logFilePath);
-
-            using (var sw = File.AppendText(logFilePath)) {
-                sw.WriteLine(message);
-            }
-        }
-
-        private void Rotate(string filePath) {
-            if (!File.Exists(filePath))
-                return;
-
-            var fileInfo = new FileInfo(filePath);
-            if (fileInfo.Length < _logChunkSize)
-                return;
-
-            var fileTime = DateTime.Now.ToString("dd-MM-yyyy,hh-mm-ss,fff");
-            var rotatedPath = filePath.Replace(".log", $".{fileTime}");
-            File.Move(filePath, rotatedPath);
-
-            var folderPath = Path.GetDirectoryName(rotatedPath);
-            var logFolderContent = new DirectoryInfo(folderPath).GetFileSystemInfos();
-
-            var chunks = logFolderContent.Where(x => !x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase));
-
-            if (chunks.Count() <= _logChunkMaxCount)
-                return;
-
-            var archiveFolderInfo = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(rotatedPath), $"{LogFolderName}_{fileTime}"));
-
-            foreach (var chunk in chunks) {
-                var destination = Path.Combine(archiveFolderInfo.FullName, chunk.Name);
-                Directory.Move(chunk.FullName, destination);
-            }
-
-            ZipFile.CreateFromDirectory(archiveFolderInfo.FullName, Path.Combine(folderPath, $"{LogFolderName}_{fileTime}.zip"));
-            Directory.Delete(archiveFolderInfo.FullName, true);
-
-            var archives = logFolderContent.Where(x => x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)).ToArray();
-
-            if (archives.Count() <= _logArchiveMaxCount)
-                return;
-
-            var oldestArchive = archives.OrderBy(x => x.CreationTime).First();
-            var cleanupDate = oldestArchive.CreationTime.AddDays(_logCleanupPeriod);
-            if (DateTime.Compare(cleanupDate, DateTime.Now) <= 0) {
-                foreach (var file in logFolderContent) {
-                    file.Delete();
-                }
-            } else
-                File.Delete(oldestArchive.FullName);
-
-        }
-
-        public override string ToString() => $"{base.ToString()}, Chunk Size: {_logChunkSize}, Max chunk count: {_logChunkMaxCount}, Max log archive count: {_logArchiveMaxCount}, Cleanup period: {_logCleanupPeriod} days]";
-    }
-
-
-}
diff --git a/ld_client/LDClient/utils/loggers/ALogger.cs b/ld_client/LDClient/utils/loggers/ALogger.cs
new file mode 100644
index 0000000..260a03d
--- /dev/null
+++ b/ld_client/LDClient/utils/loggers/ALogger.cs
@@ -0,0 +1,109 @@
+using System.Globalization;
+
+namespace LDClient.utils.loggers {
+
+    public abstract class ALogger : IDisposable {
+
+        private readonly LogVerbosity _verbosity;
+        private readonly LogFlow _logFlow;
+
+        private readonly Queue<Action> _queue = new();
+        private readonly ManualResetEvent _hasNewItems = new(false);
+        private readonly ManualResetEvent _terminate = new(false);
+        private readonly ManualResetEvent _waiting = new(false);
+        private readonly Thread _loggingThread;
+
+        private static readonly Lazy<ALogger> LazyLog = new(()
+            => {
+                switch (Program.Config.LogFlowType) {
+                    case LogFlow.File:
+                        return new FileLogger();
+                    case LogFlow.Console:
+                    default:
+                        return new ConsoleLogger();
+
+                }
+            }
+        );
+
+        public static ALogger Current => LazyLog.Value;
+
+        protected ALogger() {
+            _verbosity = Program.Config.LogVerbosityType;
+            _logFlow = Program.Config.LogFlowType;
+            _loggingThread = new Thread(ProcessQueue) { IsBackground = true };
+            _loggingThread.Start();
+        }
+
+        public void Info(string message) {
+            Log(message, LogType.Info);
+        }
+
+        public void Debug(string message) {
+            Log(message, LogType.Debug);
+        }
+
+        public void Error(string message) {
+            Log(message, LogType.Error);
+        }
+
+        public void Error(Exception e) {
+            if (_verbosity != LogVerbosity.None) {
+                Log(UnwrapExceptionMessages(e), LogType.Error);
+            }
+        }
+
+        public override string ToString() => $"Logger settings: [Type: {this.GetType().Name}, Verbosity: {_verbosity}, ";
+
+        protected abstract void CreateLog(string message);
+
+        public void Flush() => _waiting.WaitOne();
+
+        public void Dispose() {
+            _terminate.Set();
+            _loggingThread.Join();
+        }
+
+        protected virtual string ComposeLogRow(string message, LogType logType) =>
+            $"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff", CultureInfo.InvariantCulture)} - {logType}] - {message}";
+
+        protected virtual string UnwrapExceptionMessages(Exception? ex) =>
+            ex == null ? string.Empty : $"{ex}, Inner exception: {UnwrapExceptionMessages(ex.InnerException)} ";
+        
+
+        private void ProcessQueue() {
+            while (true) {
+                _waiting.Set();
+                var i = WaitHandle.WaitAny(new WaitHandle[] { _hasNewItems, _terminate });
+                if (i == 1) return;
+                _hasNewItems.Reset();
+                _waiting.Reset();
+
+                Queue<Action> queueCopy;
+                lock (_queue) {
+                    queueCopy = new Queue<Action>(_queue);
+                    _queue.Clear();
+                }
+
+                foreach (var log in queueCopy) {
+                    log();
+                }
+            }
+        }
+
+        private void Log(string message, LogType logType) {
+            if (string.IsNullOrEmpty(message))
+                return;
+
+            var logRow = ComposeLogRow(message, logType);
+            System.Diagnostics.Debug.WriteLine(logRow);
+
+            if (_verbosity == LogVerbosity.Full) {
+                lock (_queue)
+                    _queue.Enqueue(() => CreateLog(logRow));
+
+                _hasNewItems.Set();
+            }
+        }
+    }
+}
diff --git a/ld_client/LDClient/utils/loggers/ConsoleLogger.cs b/ld_client/LDClient/utils/loggers/ConsoleLogger.cs
new file mode 100644
index 0000000..b108ed6
--- /dev/null
+++ b/ld_client/LDClient/utils/loggers/ConsoleLogger.cs
@@ -0,0 +1,7 @@
+namespace LDClient.utils.loggers {
+    public class ConsoleLogger : ALogger {
+        protected override void CreateLog(string message) {
+            Console.WriteLine(message);
+        }
+    }
+}
diff --git a/ld_client/LDClient/utils/loggers/FileLogger.cs b/ld_client/LDClient/utils/loggers/FileLogger.cs
new file mode 100644
index 0000000..d63ab05
--- /dev/null
+++ b/ld_client/LDClient/utils/loggers/FileLogger.cs
@@ -0,0 +1,95 @@
+using System.IO.Compression;
+
+namespace LDClient.utils.loggers; 
+
+public class FileLogger : ALogger {
+
+    private const string LogFolderName = "logs";
+    private const string LogFileName = "app_info.log";
+    private readonly int _logChunkSize = Program.Config.LogChunkSize;
+    private readonly int _logChunkMaxCount = Program.Config.LogChunkMaxCount;
+    private readonly int _logArchiveMaxCount = Program.Config.LogArchiveMaxCount;
+    private readonly int _logCleanupPeriod = Program.Config.LogCleanupPeriod;
+
+    private const string LogFolderPath = $"ldClient\\{LogFolderName}";
+
+    private bool _logDirExists;
+
+    protected override void CreateLog(string message) {
+
+        if (!_logDirExists) {
+            _logDirExists = Directory.Exists(LogFolderPath);
+            if (!_logDirExists) {
+                Directory.CreateDirectory(LogFolderPath);
+                _logDirExists = true;
+            }
+        }
+
+        var logFilePath = Path.Combine(LogFolderPath, LogFileName);
+
+        Rotate(logFilePath);
+
+        using var sw = File.AppendText(logFilePath);
+        sw.WriteLine(message);
+    }
+
+    private void Rotate(string filePath) {
+        if (!File.Exists(filePath)) {
+            return;
+        }
+
+        var fileInfo = new FileInfo(filePath);
+        if (fileInfo.Length < _logChunkSize) {
+            return;
+        }
+        var fileTime = DateTime.Now.ToString("dd-MM-yyyy,hh-mm-ss,fff");
+        var rotatedPath = filePath.Replace(".log", $".{fileTime}");
+        File.Move(filePath, rotatedPath);
+
+        var folderPath = Path.GetDirectoryName(rotatedPath);
+        var logFolderContent = new DirectoryInfo(folderPath ?? string.Empty).GetFileSystemInfos();
+
+        var chunks = logFolderContent.Where(x => 
+            !x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase));
+
+        if (chunks.Count() <= _logChunkMaxCount) {
+            return;
+        }
+
+        Archive(chunks, rotatedPath, fileTime, folderPath);
+        DeleteOldArchives(logFolderContent);
+    }
+
+    private void Archive(IEnumerable<FileSystemInfo> chunks, string rotatedPath, string fileTime, string? folderPath) {
+
+        var archiveFolderInfo = Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(rotatedPath) ?? LogFolderPath, $"{LogFolderName}_{fileTime}"));
+
+        foreach (var chunk in chunks) {
+            var destination = Path.Combine(archiveFolderInfo.FullName, chunk.Name);
+            Directory.Move(chunk.FullName, destination);
+        }
+
+        ZipFile.CreateFromDirectory(archiveFolderInfo.FullName, Path.Combine(folderPath ?? LogFolderPath, $"{LogFolderName}_{fileTime}.zip"));
+        Directory.Delete(archiveFolderInfo.FullName, true);
+    }
+
+    private void DeleteOldArchives(FileSystemInfo[] logFolderContent) {
+
+        var archives = logFolderContent.Where(x => x.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)).ToArray();
+
+        if (archives.Length <= _logArchiveMaxCount)
+            return;
+
+        var oldestArchive = archives.OrderBy(x => x.CreationTime).First();
+        var cleanupDate = oldestArchive.CreationTime.AddDays(_logCleanupPeriod);
+        if (DateTime.Compare(cleanupDate, DateTime.Now) <= 0) {
+            foreach (var file in logFolderContent) {
+                file.Delete();
+            }
+        } else {
+            File.Delete(oldestArchive.FullName);
+        }
+    }
+
+    public override string ToString() => $"{base.ToString()}, Chunk Size: {_logChunkSize}, Max chunk count: {_logChunkMaxCount}, Max log archive count: {_logArchiveMaxCount}, Cleanup period: {_logCleanupPeriod} days]";
+}
\ No newline at end of file
diff --git a/ld_client/LDClient/utils/loggers/LogFlow.cs b/ld_client/LDClient/utils/loggers/LogFlow.cs
new file mode 100644
index 0000000..5041b12
--- /dev/null
+++ b/ld_client/LDClient/utils/loggers/LogFlow.cs
@@ -0,0 +1,6 @@
+namespace LDClient.utils.loggers {
+    public enum LogFlow {
+        Console = 0,
+        File
+    }
+}
diff --git a/ld_client/LDClient/utils/loggers/LogType.cs b/ld_client/LDClient/utils/loggers/LogType.cs
new file mode 100644
index 0000000..370057f
--- /dev/null
+++ b/ld_client/LDClient/utils/loggers/LogType.cs
@@ -0,0 +1,7 @@
+namespace LDClient.utils.loggers {
+    public enum LogType {
+        Info = 0,
+        Debug,
+        Error
+    }
+}
diff --git a/ld_client/LDClient/utils/loggers/LogVerbosity.cs b/ld_client/LDClient/utils/loggers/LogVerbosity.cs
new file mode 100644
index 0000000..03c292b
--- /dev/null
+++ b/ld_client/LDClient/utils/loggers/LogVerbosity.cs
@@ -0,0 +1,7 @@
+namespace LDClient.utils.loggers {
+    public enum LogVerbosity {
+        None = 0,
+        Exceptions,
+        Full
+    }
+}
-- 
GitLab