From 5d3c85ffd39c4ca172c467c1c1d56650b885aa62 Mon Sep 17 00:00:00 2001 From: EonaCat Date: Thu, 15 Dec 2022 21:32:33 +0100 Subject: [PATCH] Updated --- EonaCat.Logger/EonaCat.Logger.csproj | 2 +- .../Extensions/FileLoggerFactoryExtensions.cs | 39 ++++++-- .../EonaCatCoreLogger/FileLoggerProvider.cs | 92 ++++++++++++++++--- .../Internal/BatchingLogger.cs | 13 ++- .../Internal/BatchingLoggerOptions.cs | 20 +--- .../Internal/BatchingLoggerProvider.cs | 33 ++----- EonaCat.Logger/Managers/LogHelper.cs | 71 +++++++------- EonaCat.Logger/Managers/LogManager.cs | 6 +- EonaCat.Logger/Managers/LoggerSettings.cs | 2 +- EonaCat.Logger/Syslog/SyslogServer.cs | 1 + 10 files changed, 170 insertions(+), 109 deletions(-) diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj index 3c8a49e..b6c1105 100644 --- a/EonaCat.Logger/EonaCat.Logger.csproj +++ b/EonaCat.Logger/EonaCat.Logger.csproj @@ -8,7 +8,7 @@ net6.0; icon.ico - 1.0.1 + 1.0.2 EonaCat (Jeroen Saey) true EonaCat (Jeroen Saey) diff --git a/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs b/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs index 077e0ea..8a9d1ba 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; namespace EonaCat.Logger.Extensions @@ -12,11 +13,7 @@ namespace EonaCat.Logger.Extensions /// public static class FileLoggerFactoryExtensions { - /// - /// Adds a file logger named 'File' to the factory. - /// - /// The to use. - public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder) + private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder) { builder.Services.AddSingleton(); return builder; @@ -26,15 +23,39 @@ namespace EonaCat.Logger.Extensions /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory. /// /// The to use. - /// Sets the filename prefix to use for log files - public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix) + /// Sets the filename prefix to use for log files (optional) + /// the options for the fileLogger that needs to be used (optional) + public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null, FileLoggerOptions fileLoggerOptions = null) { - builder.AddEonaCatFileLogger(options => options.FileNamePrefix = filenamePrefix); + if (fileLoggerOptions == null) + { + fileLoggerOptions = new FileLoggerOptions(); + } + + if (!string.IsNullOrWhiteSpace(filenamePrefix)) + { + fileLoggerOptions.FileNamePrefix = filenamePrefix; + } + builder.AddEonaCatFileLogger(options => + { + options.FileNamePrefix = fileLoggerOptions.FileNamePrefix; + options.FlushPeriod = fileLoggerOptions.FlushPeriod; + options.RetainedFileCountLimit = fileLoggerOptions.RetainedFileCountLimit; + options.MaxWriteTries = fileLoggerOptions.MaxWriteTries; + options.FileSizeLimit = fileLoggerOptions.FileSizeLimit; + options.LogDirectory = fileLoggerOptions.LogDirectory; + options.BatchSize = fileLoggerOptions.BatchSize; + options.FileSizeLimit = fileLoggerOptions.FileSizeLimit; + options.IsEnabled = fileLoggerOptions.IsEnabled; + options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles; + } + + ); return builder; } /// - /// Adds a file logger named 'File' to the factory. + /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory. /// /// The to use. /// Configure an instance of the to set logging options diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs index cc575c9..79f2fa6 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs @@ -17,7 +17,7 @@ namespace EonaCat.Logger /// /// An that writes logs to a file /// - [ProviderAlias("File")] + [ProviderAlias("EonaCatFileLogger")] public class FileLoggerProvider : BatchingLoggerProvider { private readonly string _path; @@ -28,7 +28,9 @@ namespace EonaCat.Logger private readonly int _maxTries; private int _rollOverCount = 0; private static readonly object _writeLock = new object(); + private static readonly object _rollOverLock = new object(); private string _logFile; + private bool rollingOver; /// /// The file to which log messages should be appended. @@ -87,27 +89,31 @@ namespace EonaCat.Logger { if (_rollOverCount < _maxRolloverFiles) { - fileInfo.CopyTo(LogFile.Replace(".log", $"_{++_rollOverCount}.log")); + var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log"); + if (File.Exists(rollOverFile)) + { + File.Delete(rollOverFile); + } + fileInfo.CopyTo(rollOverFile); File.WriteAllText(LogFile, string.Empty); } else { - bool areFilesDeleted = false; - for (int i = 0; i < _rollOverCount; i++) + lock (_rollOverLock) { - File.Delete(LogFile.Replace(".log", $"_{i}.log")); - areFilesDeleted = true; - } - - if (areFilesDeleted) - { - File.Move(LogFile.Replace(".log", $"_{_rollOverCount}.log"), LogFile.Replace(".log", $"_1.log")); - _rollOverCount = 0; + rollingOver = true; + MoveRolloverLogFiles(); + rollingOver = false; } } } } + while (rollingOver) + { + await Task.Delay(100); + } + lock (_writeLock) { int tries = 0; @@ -135,9 +141,9 @@ namespace EonaCat.Logger } } } - } - DeleteOldLogFiles(); + DeleteOldLogFiles(); + } } private string GetFullName((int Year, int Month, int Day) group) @@ -158,6 +164,64 @@ namespace EonaCat.Logger return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day); } + private static void MoveFile(string copyFromPath, string copyToPath) + { + var origin = new FileInfo(copyFromPath); + origin.MoveTo(copyToPath); + + var destination = new FileInfo(copyToPath); + destination.CreationTime = origin.CreationTime; + destination.LastWriteTime = origin.LastWriteTime; + destination.LastAccessTime = origin.LastAccessTime; + } + + /// + /// Rollover logFiles + /// + protected void MoveRolloverLogFiles() + { + if (_maxRolloverFiles > 0 && _rollOverCount >= 0) + { + if (_rollOverCount >= _maxRolloverFiles) + { + var maxRollover = _rollOverCount; + bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix); + IEnumerable files; + if (hasPrefix) + { + files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*").OrderBy(x => x.CreationTime); + } + else + { + files = new DirectoryInfo(_path).GetFiles("*").OrderBy(x => x.CreationTime); + } + + for (int i = files.Count() -1; i >= 0; i--) + { + var currentFile = files.ElementAt(i); + if (i == 0) + { + // Temporary move first file + var newFilename2 = Path.GetFileName(currentFile.FullName).Replace($".log", $"_{i + 1}.log"); + MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}"); + continue; + } + + if (i == files.Count() - 1) + { + // Delete the last file + File.Delete(currentFile.FullName); + continue; + } + + var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log"); + MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}"); + } + _rollOverCount = 0; + } + } + } + /// /// Deletes old log files, keeping a number of files defined by /// diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs index ac15f3f..c3dd504 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs @@ -1,4 +1,5 @@ using EonaCat.logger.Managers; +using EonaCat.Logger.Managers; using Microsoft.Extensions.Logging; using System; using System.Text; @@ -10,11 +11,13 @@ namespace EonaCat.Logger.Internal public class BatchingLogger : ILogger { + private LoggerSettings _loggerSettings; private readonly BatchingLoggerProvider _provider; private readonly string _category; - public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName) + public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings = null) { + _loggerSettings = loggerSettings; _provider = loggerProvider; _category = categoryName; } @@ -32,9 +35,13 @@ namespace EonaCat.Logger.Internal public void Log(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { if (!IsEnabled(logLevel)) return; + + if (_loggerSettings == null) + { + _loggerSettings = new LoggerSettings(); + } - var message = LogHelper.FormatMessageWithHeader(new Managers.LoggerSettings(), logLevel.FromLogLevel(), formatter(state, exception)) + Environment.NewLine; - + var message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), formatter(state, exception)) + Environment.NewLine; if (exception != null) { message = exception.FormatExceptionToMessage() + Environment.NewLine; diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs index cb320aa..12cf965 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs @@ -7,9 +7,8 @@ namespace EonaCat.Logger.Internal public class BatchingLoggerOptions { - private int _batchSize = 32; - private int _backgroundQueueSize; - private TimeSpan _flushPeriod = TimeSpan.FromSeconds(1); + private int _batchSize = 0; + private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100); /// /// Gets or sets the period after which logs will be flushed to the store. @@ -28,21 +27,6 @@ namespace EonaCat.Logger.Internal } } - /// - /// Gets or sets the maximum size of the background log message queue or less than 1 for no limit. - /// After maximum queue size is reached log event sink would start blocking. - /// Defaults to 0. - /// - public int BackgroundQueueSize - { - get => _backgroundQueueSize; - - set - { - _backgroundQueueSize = value; - } - } - /// /// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit. /// diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs index 25ebe75..b66e4a2 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using EonaCat.Logger.Helpers; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System; using System.Collections.Concurrent; @@ -15,10 +16,9 @@ namespace EonaCat.Logger.Internal { private readonly List _currentBatch = new List(); private readonly TimeSpan _interval; - private readonly int _queueSize; private readonly int _batchSize; - private BlockingCollection _messageQueue; + private ConcurrentQueue _messageQueue; private Task _outputTask; private CancellationTokenSource _cancellationTokenSource; @@ -26,11 +26,6 @@ namespace EonaCat.Logger.Internal { BatchingLoggerOptions loggerOptions = options.Value; - if (loggerOptions.BatchSize <= 0) - { - throw new ArgumentOutOfRangeException(nameof(loggerOptions.BatchSize), $"{nameof(loggerOptions.BatchSize)} must be a positive number."); - } - if (loggerOptions.FlushPeriod <= TimeSpan.Zero) { throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero."); @@ -38,7 +33,6 @@ namespace EonaCat.Logger.Internal _interval = loggerOptions.FlushPeriod; _batchSize = loggerOptions.BatchSize; - _queueSize = loggerOptions.BackgroundQueueSize; Start(); } @@ -47,11 +41,12 @@ namespace EonaCat.Logger.Internal private async Task ProcessLogQueue(object state) { + await WriteMessagesAsync(new List { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} started.{Environment.NewLine}", Timestamp = DateTimeOffset.Now } }, _cancellationTokenSource.Token); while (!_cancellationTokenSource.IsCancellationRequested) { int limit = _batchSize <= 0 ? int.MaxValue : _batchSize; - while (limit > 0 && _messageQueue.TryTake(out LogMessage message)) + while (limit > 0 && _messageQueue.TryDequeue(out LogMessage message)) { _currentBatch.Add(message); limit--; @@ -73,6 +68,7 @@ namespace EonaCat.Logger.Internal await IntervalAsync(_interval, _cancellationTokenSource.Token); } + await WriteMessagesAsync(new List { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} stopped.{Environment.NewLine}", Timestamp = DateTimeOffset.Now } }, _cancellationTokenSource.Token); } protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken) @@ -82,24 +78,12 @@ namespace EonaCat.Logger.Internal internal void AddMessage(DateTimeOffset timestamp, string message) { - if (!_messageQueue.IsAddingCompleted) - { - try - { - _messageQueue.Add(new LogMessage { Message = message, Timestamp = timestamp }, _cancellationTokenSource.Token); - } - catch - { - // cancellation token canceled or CompleteAdding called - } - } + _messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp }); } private void Start() { - _messageQueue = _queueSize == 0 ? - new BlockingCollection(new ConcurrentQueue()) : - new BlockingCollection(new ConcurrentQueue(), _queueSize); + _messageQueue = new ConcurrentQueue(); _cancellationTokenSource = new CancellationTokenSource(); _outputTask = Task.Factory.StartNew( @@ -111,7 +95,6 @@ namespace EonaCat.Logger.Internal private void Stop() { _cancellationTokenSource.Cancel(); - _messageQueue.CompleteAdding(); try { diff --git a/EonaCat.Logger/Managers/LogHelper.cs b/EonaCat.Logger/Managers/LogHelper.cs index 3de259a..d072125 100644 --- a/EonaCat.Logger/Managers/LogHelper.cs +++ b/EonaCat.Logger/Managers/LogHelper.cs @@ -4,6 +4,7 @@ using EonaCat.Logger.Syslog; using Microsoft.Extensions.Logging; using System; using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -207,9 +208,34 @@ namespace EonaCat.logger.Managers { try { - System.IO.StreamWriter file = new System.IO.StreamWriter($"{settings.FileLoggerOptions.LogDirectory}{Path.DirectorySeparatorChar}{settings.FileLoggerOptions.FileNamePrefix}_{DateTime.Now.ToString("yyyyMMdd")}.log", true); - file.Write(message + Environment.NewLine); - file.Close(); + if (logType == ELogType.CRITICAL) + { + logger.LogCritical(message); + } + else if (logType == ELogType.DEBUG) + { + logger.LogDebug(message); + } + else if (logType == ELogType.ERROR) + { + logger.LogError(message); + } + else if (logType == ELogType.INFO) + { + logger.LogInformation(message); + } + else if (logType == ELogType.TRACE) + { + logger.LogTrace(message); + } + else if (logType == ELogType.TRAFFIC) + { + logger.LogTrace($"[TRAFFIC] {message}"); + } + else if (logType == ELogType.WARNING) + { + logger.LogWarning(message); + } completed = true; } catch (Exception exc) @@ -219,37 +245,7 @@ namespace EonaCat.logger.Managers if (tries >= maxTries) throw; } - } - return; - - if (logType == ELogType.CRITICAL) - { - logger.LogCritical(message); - } - else if (logType == ELogType.DEBUG) - { - logger.LogDebug(message); - } - else if (logType == ELogType.ERROR) - { - logger.LogError(message); - } - else if (logType == ELogType.INFO) - { - logger.LogInformation(message); - } - else if (logType == ELogType.TRACE) - { - logger.LogTrace(message); - } - else if (logType == ELogType.TRAFFIC) - { - logger.LogTrace($"[TRAFFIC] {message}"); - } - else if (logType == ELogType.WARNING) - { - logger.LogWarning(message); - } + } } } @@ -257,10 +253,13 @@ namespace EonaCat.logger.Managers { if (settings == null) return; if (!settings.SendToSyslogServers) return; - if (settings.SysLogServers == null) return; - if (!settings.SysLogServers.Any()) return; if (string.IsNullOrWhiteSpace(message)) return; + if (settings.SysLogServers == null || !settings.SysLogServers.Any()) + { + settings.SysLogServers = new List { new SyslogServer("127.0.0.1", 514) }; + } + byte[] data = Encoding.UTF8.GetBytes(message); var sysLogServers = settings.SysLogServers.ToList(); diff --git a/EonaCat.Logger/Managers/LogManager.cs b/EonaCat.Logger/Managers/LogManager.cs index d1b616f..63dc737 100644 --- a/EonaCat.Logger/Managers/LogManager.cs +++ b/EonaCat.Logger/Managers/LogManager.cs @@ -189,8 +189,10 @@ namespace EonaCat.Logger.Managers if (string.IsNullOrEmpty(serverIp)) throw new ArgumentNullException(nameof(serverIp)); if (serverPort < 0) throw new ArgumentException("Server port must be zero or greater."); - settings.SysLogServers = new List(); - settings.SysLogServers.Add(new SyslogServer(serverIp, serverPort)); + settings.SysLogServers = new List + { + new SyslogServer(serverIp, serverPort) + }; Id = id; Settings = settings; diff --git a/EonaCat.Logger/Managers/LoggerSettings.cs b/EonaCat.Logger/Managers/LoggerSettings.cs index bd5c571..a350db0 100644 --- a/EonaCat.Logger/Managers/LoggerSettings.cs +++ b/EonaCat.Logger/Managers/LoggerSettings.cs @@ -98,7 +98,7 @@ namespace EonaCat.Logger.Managers public bool SendToSyslogServers { get; set; } - public List SysLogServers { get; set; } = new List { new SyslogServer("127.0.0.1", 514) }; + public List SysLogServers { get; set; } /// /// Determines if the fileLogging is enabled diff --git a/EonaCat.Logger/Syslog/SyslogServer.cs b/EonaCat.Logger/Syslog/SyslogServer.cs index f044b14..f682932 100644 --- a/EonaCat.Logger/Syslog/SyslogServer.cs +++ b/EonaCat.Logger/Syslog/SyslogServer.cs @@ -19,6 +19,7 @@ namespace EonaCat.Logger.Syslog { return _Hostname; } + set { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname));