diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj index 727b90c..b670156 100644 --- a/EonaCat.Logger/EonaCat.Logger.csproj +++ b/EonaCat.Logger/EonaCat.Logger.csproj @@ -3,7 +3,7 @@ .netstandard2.1; net6.0; net7.0; net8.0; net4.8; icon.ico latest - 1.2.9 + 1.3.0 EonaCat (Jeroen Saey) true EonaCat (Jeroen Saey) @@ -24,7 +24,7 @@ - 1.2.9+{chash:10}.{c:ymd} + 1.3.0+{chash:10}.{c:ymd} true true v[0-9]* diff --git a/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs b/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs index 248281e..f5a888b 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs @@ -26,9 +26,16 @@ public static class FileLoggerFactoryExtensions public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null, FileLoggerOptions fileLoggerOptions = null) { - if (fileLoggerOptions == null) fileLoggerOptions = new FileLoggerOptions(); + if (fileLoggerOptions == null) + { + fileLoggerOptions = new FileLoggerOptions(); + } + + if (!string.IsNullOrWhiteSpace(filenamePrefix)) + { + fileLoggerOptions.FileNamePrefix = filenamePrefix; + } - if (!string.IsNullOrWhiteSpace(filenamePrefix)) fileLoggerOptions.FileNamePrefix = filenamePrefix; builder.AddEonaCatFileLogger(options => { options.FileNamePrefix = fileLoggerOptions.FileNamePrefix; @@ -55,7 +62,11 @@ public static class FileLoggerFactoryExtensions public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, Action configure) { - if (configure == null) throw new ArgumentNullException(nameof(configure)); + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + builder.AddEonaCatFileLogger(); builder.Services.Configure(configure); diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs index c6844f6..b5614e0 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs @@ -30,7 +30,10 @@ public class FileLoggerOptions : BatchingLoggerOptions set { if (value <= 0) + { throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive."); + } + _fileSizeLimit = value; } } @@ -46,8 +49,11 @@ public class FileLoggerOptions : BatchingLoggerOptions set { if (value <= 0) + { throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(RetainedFileCountLimit)} must be positive."); + } + _retainedFileCountLimit = value; } } @@ -74,7 +80,10 @@ public class FileLoggerOptions : BatchingLoggerOptions set { if (value <= 0) + { throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive."); + } + _maxRolloverFiles = value; } } diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs index 5c572d3..cf26031 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs @@ -63,8 +63,12 @@ public class FileLoggerProvider : BatchingLoggerProvider { var dir = Path.GetDirectoryName(_logFile); if (!string.IsNullOrEmpty(dir)) + { if (!Directory.Exists(dir)) + { Directory.CreateDirectory(dir); + } + } } } } @@ -128,7 +132,10 @@ public class FileLoggerProvider : BatchingLoggerProvider private async Task TryWriteToFileAsync(CancellationToken cancellationToken) { - if (!_buffer.ContainsKey(LogFile)) return true; + if (!_buffer.ContainsKey(LogFile)) + { + return true; + } var tries = 0; var completed = false; @@ -193,7 +200,10 @@ public class FileLoggerProvider : BatchingLoggerProvider { var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix); if (hasPrefix) + { return Path.Combine(_path, $"{_fileNamePrefix}_{group.Year:0000}{group.Month:00}{group.Day:00}.log"); + } + return Path.Combine(_path, $"{group.Year:0000}{group.Month:00}{group.Day:00}.log"); } @@ -260,7 +270,7 @@ public class FileLoggerProvider : BatchingLoggerProvider private async Task WriteEndMessageAsync(string logFilePath) { var stopMessage = LogHelper.GetStopMessage(); - stopMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, stopMessage, CurrentDateTme); + stopMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, stopMessage, CurrentDateTme, Category); using (var file = new StreamWriter(logFilePath, true)) { @@ -292,13 +302,20 @@ public class FileLoggerProvider : BatchingLoggerProvider IEnumerable files = null; if (hasPrefix) + { files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*"); + } else + { files = new DirectoryInfo(_path).GetFiles("*"); + } files = files.OrderByDescending(file => file.Name).Skip(_maxRetainedFiles); - foreach (var item in files) item.Delete(); + foreach (var item in files) + { + item.Delete(); + } } } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs index ad660d3..7a59919 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs @@ -4,67 +4,70 @@ using EonaCat.Logger.Extensions; using EonaCat.Logger.Managers; using Microsoft.Extensions.Logging; -namespace EonaCat.Logger.EonaCatCoreLogger.Internal; -// This file is part of the EonaCat project(s) which is released under the Apache License. -// See the LICENSE file or go to https://EonaCat.com/License for full license details. - -public class BatchingLogger : ILogger +namespace EonaCat.Logger.EonaCatCoreLogger.Internal { - private readonly string _category; - private readonly BatchingLoggerProvider _provider; - private LoggerSettings _loggerSettings; + // This file is part of the EonaCat project(s) which is released under the Apache License. + // See the LICENSE file or go to https://EonaCat.com/License for full license details. - public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings) + public class BatchingLogger : ILogger { - _loggerSettings = loggerSettings; - _provider = loggerProvider; - _category = categoryName; - } + private readonly string _category; + private readonly BatchingLoggerProvider _provider; + private readonly LoggerSettings _loggerSettings; - private DateTimeOffset CurrentDateTimeOffset => - _loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow; - - private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow; - - public IDisposable BeginScope(TState state) - { - return null; - } - - public bool IsEnabled(LogLevel logLevel) - { - return logLevel != LogLevel.None; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, - Func formatter) - { - Log(CurrentDateTimeOffset, logLevel, eventId, state, exception, formatter); - } - - 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(_loggerSettings, logLevel.FromLogLevel(), - formatter(state, exception), timestamp.DateTime) + Environment.NewLine; - if (exception != null) message = exception.FormatExceptionToMessage() + Environment.NewLine; - - _provider.AddMessage(timestamp, message); - - var currentMessage = new EonaCatLogMessage + public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings) { - DateTime = timestamp.DateTime, - Message = message, - LogType = logLevel.FromLogLevel() - }; + _provider = loggerProvider ?? throw new ArgumentNullException(nameof(loggerProvider)); + _category = categoryName ?? throw new ArgumentNullException(nameof(categoryName)); + _loggerSettings = loggerSettings ?? throw new ArgumentNullException(nameof(loggerSettings)); + } - currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin) - ? "BatchingLogger" - : _loggerSettings.LogOrigin; - _loggerSettings?.OnLogEvent(currentMessage); + private DateTimeOffset CurrentDateTimeOffset => CurrentDateTime; + + private DateTime CurrentDateTime => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow; + + public IDisposable BeginScope(TState state) => null; + + public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, + Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + + var timestamp = CurrentDateTimeOffset; + Log(timestamp, logLevel, eventId, state, exception, formatter, _category); + } + + public void Log(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state, + Exception exception, Func formatter, string category) + { + if (!IsEnabled(logLevel)) + { + return; + } + + string message = exception != null + ? exception.FormatExceptionToMessage() + Environment.NewLine + : formatter(state, exception); + + message = LogHelper.FormatMessageWithHeader(_loggerSettings, logLevel.FromLogLevel(), message, timestamp.DateTime, category) + + Environment.NewLine; + + _provider.AddMessage(timestamp, message); + + var currentMessage = new EonaCatLogMessage + { + DateTime = timestamp.DateTime, + Message = message, + LogType = logLevel.FromLogLevel(), + Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin) ? "BatchingLogger" : _loggerSettings.LogOrigin + }; + + _loggerSettings.OnLogEvent(currentMessage); + } } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs index b0bfe1d..2c0baaa 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs @@ -18,7 +18,10 @@ public class BatchingLoggerOptions set { if (value <= TimeSpan.Zero) + { throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive."); + } + _flushPeriod = value; } } diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs index 1ece0f4..3b92201 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs @@ -23,16 +23,23 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable private Task _outputTask; private object _writeLock = new object(); private bool _isDisposing; + protected string Category; protected BatchingLoggerProvider(IOptions options) { var loggerOptions = options.Value; if (loggerOptions.FlushPeriod <= TimeSpan.Zero) + { throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero."); + } + + if (options.Value is FileLoggerOptions fileLoggerOptions) + { + UseLocalTime = fileLoggerOptions.UseLocalTime; + } - if (options.Value is FileLoggerOptions fileLoggerOptions) UseLocalTime = fileLoggerOptions.UseLocalTime; _batchSize = loggerOptions.BatchSize; StartAsync().ConfigureAwait(false); @@ -47,7 +54,10 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable { get { - if (_loggerSettings != null) return _loggerSettings; + if (_loggerSettings != null) + { + return _loggerSettings; + } _loggerSettings = new LoggerSettings(); _loggerSettings.UseLocalTime = UseLocalTime; @@ -73,6 +83,7 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable public ILogger CreateLogger(string categoryName) { + Category = categoryName; return new BatchingLogger(this, categoryName, LoggerSettings); } diff --git a/EonaCat.Logger/Extensions/ExceptionExtensions.cs b/EonaCat.Logger/Extensions/ExceptionExtensions.cs index 8f4090e..3076f27 100644 --- a/EonaCat.Logger/Extensions/ExceptionExtensions.cs +++ b/EonaCat.Logger/Extensions/ExceptionExtensions.cs @@ -10,7 +10,9 @@ public static class ExceptionExtensions public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null) { if (exception == null) + { return string.Empty; + } var st = new StackTrace(exception, true); var frame = st.GetFrame(0); @@ -28,9 +30,15 @@ public static class ExceptionExtensions sb.AppendLine(); sb.AppendLine("--- Exception details provided by EonaCatLogger ---"); if (!string.IsNullOrEmpty(module)) + { sb.AppendLine(" Module : " + module); + } + if (!string.IsNullOrEmpty(method)) + { sb.AppendLine(" Method : " + method); + } + sb.Append(" Type : ").AppendLine(exception.GetType().ToString()); sb.Append(" Data : ").AppendLine(exception.Data != null && exception.Data.Count > 0 ? FormatExceptionData(exception.Data) @@ -54,10 +62,12 @@ public static class ExceptionExtensions var sb = new StringBuilder(); foreach (DictionaryEntry entry in data) + { sb.Append(" | ") .Append(entry.Key) .Append(": ") .AppendLine(entry.Value.ToString()); + } return sb.ToString(); } diff --git a/EonaCat.Logger/Extensions/OffsetStream.cs b/EonaCat.Logger/Extensions/OffsetStream.cs index 688415f..8e48f70 100644 --- a/EonaCat.Logger/Extensions/OffsetStream.cs +++ b/EonaCat.Logger/Extensions/OffsetStream.cs @@ -13,16 +13,26 @@ public class OffsetStream : Stream { if (stream.CanSeek) { - if (offset > stream.Length) throw new EndOfStreamException(); + if (offset > stream.Length) + { + throw new EndOfStreamException(); + } BaseStreamOffset = offset; - if (length > stream.Length - offset) throw new EndOfStreamException(); + if (length > stream.Length - offset) + { + throw new EndOfStreamException(); + } if (length == 0) + { Length1 = stream.Length - offset; + } else + { Length1 = length; + } } else { @@ -49,9 +59,15 @@ public class OffsetStream : Stream set { - if (value > Length1) throw new EndOfStreamException(); + if (value > Length1) + { + throw new EndOfStreamException(); + } - if (!BaseStream.CanSeek) throw new NotSupportedException("Cannot seek stream."); + if (!BaseStream.CanSeek) + { + throw new NotSupportedException("Cannot seek stream."); + } Position1 = value; } @@ -71,11 +87,18 @@ public class OffsetStream : Stream protected override void Dispose(bool disposing) { - if (Disposed) return; + if (Disposed) + { + return; + } if (disposing) + { if (OwnStream & (BaseStream != null)) + { BaseStream.Dispose(); + } + } Disposed = true; @@ -84,20 +107,35 @@ public class OffsetStream : Stream public override void Flush() { - if (ReadOnly) throw new IOException("OffsetStream is read only."); + if (ReadOnly) + { + throw new IOException("OffsetStream is read only."); + } BaseStream.Flush(); } public override int Read(byte[] buffer, int offset, int count) { - if (count < 1) throw new ArgumentOutOfRangeException("Count cannot be less than 1."); + if (count < 1) + { + throw new ArgumentOutOfRangeException("Count cannot be less than 1."); + } - if (Position1 >= Length1) return 0; + if (Position1 >= Length1) + { + return 0; + } - if (count > Length1 - Position1) count = Convert.ToInt32(Length1 - Position1); + if (count > Length1 - Position1) + { + count = Convert.ToInt32(Length1 - Position1); + } - if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1; + if (BaseStream.CanSeek) + { + BaseStream.Position = BaseStreamOffset + Position1; + } var bytesRead = BaseStream.Read(buffer, offset, count); Position1 += bytesRead; @@ -107,7 +145,10 @@ public class OffsetStream : Stream public override long Seek(long offset, SeekOrigin origin) { - if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable."); + if (!BaseStream.CanSeek) + { + throw new IOException("Stream is not seekable."); + } long pos; @@ -130,7 +171,10 @@ public class OffsetStream : Stream break; } - if (pos < 0 || pos >= Length1) throw new EndOfStreamException("OffsetStream reached begining/end of stream."); + if (pos < 0 || pos >= Length1) + { + throw new EndOfStreamException("OffsetStream reached begining/end of stream."); + } Position1 = pos; @@ -139,7 +183,10 @@ public class OffsetStream : Stream public override void SetLength(long value) { - if (ReadOnly) throw new IOException("OffsetStream is read only."); + if (ReadOnly) + { + throw new IOException("OffsetStream is read only."); + } BaseStream.SetLength(BaseStreamOffset + value); Length1 = value; @@ -147,15 +194,27 @@ public class OffsetStream : Stream public override void Write(byte[] buffer, int offset, int count) { - if (ReadOnly) throw new IOException("OffsetStream is read only."); + if (ReadOnly) + { + throw new IOException("OffsetStream is read only."); + } - if (count < 1) return; + if (count < 1) + { + return; + } var pos = Position1 + count; - if (pos > Length1) throw new EndOfStreamException("OffsetStream reached end of stream."); + if (pos > Length1) + { + throw new EndOfStreamException("OffsetStream reached end of stream."); + } - if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1; + if (BaseStream.CanSeek) + { + BaseStream.Position = BaseStreamOffset + Position1; + } BaseStream.Write(buffer, offset, count); Position1 = pos; @@ -175,9 +234,15 @@ public class OffsetStream : Stream public void WriteTo(Stream stream, int bufferSize) { - if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable."); + if (!BaseStream.CanSeek) + { + throw new IOException("Stream is not seekable."); + } - if (Length1 < bufferSize) bufferSize = Convert.ToInt32(Length1); + if (Length1 < bufferSize) + { + bufferSize = Convert.ToInt32(Length1); + } var previousPosition = Position1; Position1 = 0; diff --git a/EonaCat.Logger/GrayLog/GrayLogServer.cs b/EonaCat.Logger/GrayLog/GrayLogServer.cs index 64f1e9a..697a42c 100644 --- a/EonaCat.Logger/GrayLog/GrayLogServer.cs +++ b/EonaCat.Logger/GrayLog/GrayLogServer.cs @@ -42,7 +42,11 @@ public class GrayLogServer set { - if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname)); + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentNullException(nameof(Hostname)); + } + _Hostname = value; SetUdp(); @@ -57,7 +61,11 @@ public class GrayLogServer get => _Port; set { - if (value < 0) throw new ArgumentException("Port must be zero or greater."); + if (value < 0) + { + throw new ArgumentException("Port must be zero or greater."); + } + _Port = value; SetUdp(); diff --git a/EonaCat.Logger/Helpers/ColorHelper.cs b/EonaCat.Logger/Helpers/ColorHelper.cs index 6fa13b7..0788da0 100644 --- a/EonaCat.Logger/Helpers/ColorHelper.cs +++ b/EonaCat.Logger/Helpers/ColorHelper.cs @@ -80,10 +80,15 @@ public static class ColorHelper public static int HexColorToArgb(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF) { - if (string.IsNullOrEmpty(htmlColor)) throw new ArgumentNullException(nameof(htmlColor)); + if (string.IsNullOrEmpty(htmlColor)) + { + throw new ArgumentNullException(nameof(htmlColor)); + } if (!htmlColor.StartsWith("#") && requireHexSpecified) + { throw new ArgumentException($"Provided parameter '{htmlColor}' is not valid"); + } htmlColor = htmlColor.TrimStart('#'); diff --git a/EonaCat.Logger/Managers/LogHelper.cs b/EonaCat.Logger/Managers/LogHelper.cs index 0605b8c..8393f25 100644 --- a/EonaCat.Logger/Managers/LogHelper.cs +++ b/EonaCat.Logger/Managers/LogHelper.cs @@ -26,10 +26,17 @@ internal static class LogHelper internal static event EventHandler OnException; internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage, - DateTime dateTime) + DateTime dateTime, string category = null) { if (string.IsNullOrWhiteSpace(currentMessage)) + { return currentMessage; + } + + if (string.IsNullOrWhiteSpace(category)) + { + category = "General"; + } var sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]"); @@ -37,11 +44,14 @@ internal static class LogHelper dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " + (settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]")) .Replace("{host}", $"[Host:{Dns.GetHostName()}]") + .Replace("{category}", $"[Category:{category}]") .Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]") .Replace("{sev}", $"[{logType}]"); if (!settings?.RemoveMessagePrefix ?? (false && !currentMessage.Contains("[EonaCatLogger]"))) + { sb.Insert(0, "[EonaCatLogger] "); + } sb.Append(" ").Append(currentMessage); @@ -51,7 +61,9 @@ internal static class LogHelper internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message, bool writeToConsole) { if (settings == null || !writeToConsole || string.IsNullOrWhiteSpace(message)) + { return; + } if (settings.EnableColors && settings.Colors != null) { @@ -109,7 +121,10 @@ internal static class LogHelper internal static void SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message) { if (logger == null || settings == null || !settings.EnableFileLogging || - string.IsNullOrWhiteSpace(message)) return; + string.IsNullOrWhiteSpace(message)) + { + return; + } var logLevel = logType.ToLogLevel(); if (IsLogLevelEnabled(settings, logType)) @@ -132,9 +147,14 @@ internal static class LogHelper bool sendToSplunkServer) { if (settings == null || !sendToSplunkServer || splunkPayload == null) + { return; + } - if (settings.SplunkServers == null) settings.SplunkServers = new List(); + if (settings.SplunkServers == null) + { + settings.SplunkServers = new List(); + } foreach (var splunkServer in settings.SplunkServers) { @@ -154,12 +174,14 @@ internal static class LogHelper var response = await splunkServer.SendAsync(splunkPayload); if (!response.IsSuccessStatusCode) + { OnException?.Invoke(null, new ErrorMessage { Message = $"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}" }); + } } catch (Exception exception) { @@ -178,7 +200,9 @@ internal static class LogHelper bool sendToSplunkServer) { if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message)) + { return; + } var splunkPayload = new SplunkPayload { @@ -194,9 +218,12 @@ internal static class LogHelper string facility, string source, bool sendToGrayLogServer, string version = "1.1") { if (settings == null || !sendToGrayLogServer || string.IsNullOrWhiteSpace(message)) + { return; + } foreach (var grayLogServer in settings.GrayLogServers ?? new List { new("127.0.0.1") }) + { try { var gelfMessage = new @@ -223,15 +250,19 @@ internal static class LogHelper $"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}" }); } + } } internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, bool sendToSyslogServers) { if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message)) + { return; + } foreach (var server in settings.SysLogServers ?? new List { new("127.0.0.1") }) + { try { if (string.IsNullOrWhiteSpace(server.Hostname)) @@ -260,6 +291,7 @@ internal static class LogHelper Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}" }); } + } } internal static string GetStartupMessage() diff --git a/EonaCat.Logger/Managers/LogManager.cs b/EonaCat.Logger/Managers/LogManager.cs index 1518aab..0f7d33a 100644 --- a/EonaCat.Logger/Managers/LogManager.cs +++ b/EonaCat.Logger/Managers/LogManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -11,274 +12,309 @@ using EonaCat.Logger.Syslog; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace EonaCat.Logger.Managers; -// This file is part of the EonaCat project(s) which is released under the Apache License. -// See the LICENSE file or go to https://EonaCat.com/License for full license details. - -public class LogManager : ILogManager, IDisposable +namespace EonaCat.Logger.Managers { - private static LogManager _instance; + // This file is part of the EonaCat project(s) which is released under the Apache License. + // See the LICENSE file or go to https://EonaCat.com/License for full license details. - private readonly CancellationTokenSource _tokenSource = new(); - private DateTime _logDate; - private bool _isDisposing; - - public LogManager(LoggerSettings settings, string serverIp, int serverPort) + public class LogManager : ILogManager, IDisposable { - if (string.IsNullOrEmpty(serverIp)) - throw new ArgumentNullException(nameof(serverIp)); + private static readonly Lazy _instance = new(() => new LogManager(CreateDefaultSettings())); + private readonly CancellationTokenSource _tokenSource = new(); + private DateTime _logDate; + private bool _isDisposing; + private string _category; - if (serverPort < 0) - throw new ArgumentException("Server port must be zero or greater."); - - settings.SysLogServers = new List + public LogManager(LoggerSettings settings, string serverIp, int serverPort) { - new(serverIp, serverPort) - }; - - Settings = settings; - SetupLogManager(); - } - - public LogManager(LoggerSettings settings) - { - Settings = settings; - SetupFileLogger(settings); - SetupLogManager(); - LogHelper.OnException += LogHelper_OnException; - } - - private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow; - public ILoggerProvider LoggerProvider { get; private set; } - public ILoggerFactory LoggerFactory { get; private set; } - public ILogger Logger { get; private set; } - - public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider - ? fileLoggerProvider.LogFile - : string.Empty; - - public bool IsRunning { get; private set; } - - public static LogManager Instance => InstanceInit(); - - public LoggerSettings Settings { get; set; } = CreateDefaultSettings(); - - public void Dispose() - { - DisposeAsync(true).GetAwaiter().GetResult(); - GC.SuppressFinalize(this); - } - - public async Task WriteAsync(Exception exception, string module = null, string method = null, - bool criticalException = false, - bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, - string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, - string grayLogSource = null, string grayLogVersion = "1.1") - { - if (exception == null) - { - return; - } - - await WriteAsync(exception.FormatExceptionToMessage(module, method), - criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers, - sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, - grayLogVersion); - } - - public async Task WriteAsync(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, - bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string customSplunkSourceType = null, - bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, - string grayLogVersion = "1.1") - { - if (logType == ELogType.NONE) - return; - - await InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, - customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion).ConfigureAwait(false); - } - - public event EventHandler OnException; - - private static LogManager InstanceInit() - { - if (_instance == null) _instance = new LogManager(CreateDefaultSettings()); - return _instance; - } - - private static LoggerSettings CreateDefaultSettings() - { - var settings = new LoggerSettings(); - settings.Id = "EonaCatLogger"; - settings.MaxLogType = ELogType.INFO; - return settings; - } - - protected virtual async Task DisposeAsync(bool disposing) - { - if (disposing) - { - _isDisposing = true; - await StopLoggingAsync().ConfigureAwait(false); - await Task.Delay(100).ConfigureAwait(false); - } - } - - public async Task StartNewLogAsync() - { - if (_tokenSource.IsCancellationRequested) return; - - if (IsRunning && CurrentDateTme.Date > _logDate.Date) - { - await StopLoggingAsync().ConfigureAwait(false); - } - - IsRunning = true; - - CreateLogger(); - - Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory); - - _logDate = CurrentDateTme; - } - - private void CreateLogger() - { - // Dispose of previous ServiceProvider if it exists - LoggerProvider?.Dispose(); - LoggerFactory?.Dispose(); - - IServiceCollection serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel()) - .AddEonaCatFileLogger(configuration => + if (string.IsNullOrEmpty(serverIp)) { - var fileLoggerOptions = Settings.FileLoggerOptions; - configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries; - configuration.RetainedFileCountLimit = fileLoggerOptions.RetainedFileCountLimit; - configuration.FlushPeriod = fileLoggerOptions.FlushPeriod; - configuration.IsEnabled = fileLoggerOptions.IsEnabled; - configuration.BatchSize = fileLoggerOptions.BatchSize; - configuration.FileSizeLimit = fileLoggerOptions.FileSizeLimit; - configuration.LogDirectory = fileLoggerOptions.LogDirectory; - configuration.FileNamePrefix = fileLoggerOptions.FileNamePrefix; - configuration.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles; - configuration.UseLocalTime = Settings.UseLocalTime; - })); + throw new ArgumentNullException(nameof(serverIp)); + } - var serviceProvider = serviceCollection.BuildServiceProvider(); - LoggerProvider = serviceProvider.GetService(); - LoggerFactory = serviceProvider.GetService(); - Logger = LoggerFactory.CreateLogger(Settings.Id); - LogHelper.SendToFile(Logger, Settings, ELogType.INFO, LogHelper.GetStartupMessage()); - } + if (serverPort < 0) + { + throw new ArgumentException("Server port must be zero or greater."); + } + settings.SysLogServers = new List + { + new(serverIp, serverPort) + }; - private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO, - bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null, - string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, - string grayLogSource = null, string grayLogVersion = "1.1") - { - if (string.IsNullOrEmpty(message) || logType == ELogType.NONE || - (int)logType > (int)Settings.MaxLogType) return; - - if (_isDisposing) - { - return; + Settings = settings; + SetupLogManager(); } - if (!IsRunning) + public LogManager(LoggerSettings settings, string category = null) { - await StartNewLogAsync().ConfigureAwait(false); + _category = category; + if (string.IsNullOrWhiteSpace(category)) + { + _category = "General"; + } + Settings = settings; + SetupFileLogger(settings); + SetupLogManager(); + LogHelper.OnException += LogHelper_OnException; } - var messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime); - var writeToConsoleValue = writeToConsole ?? Settings.EnableConsole; - var sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers; - var sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers; - var sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers; + private DateTime CurrentDateTime => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow; + public ILoggerProvider LoggerProvider { get; private set; } + public ILoggerFactory LoggerFactory { get; private set; } + public ILogger Logger { get; private set; } - LogHelper.SendToFile(Logger, Settings, logType, message); + public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider + ? fileLoggerProvider.LogFile + : string.Empty; - if (writeToConsoleValue) LogHelper.SendToConsole(Settings, logType, messageWithHeader, true); + public bool IsRunning { get; private set; } - if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue) - await Task.Run(async () => + public static LogManager Instance => _instance.Value; + + public LoggerSettings Settings { get; set; } = CreateDefaultSettings(); + + public void Dispose() + { + DisposeAsync(true).GetAwaiter().GetResult(); + GC.SuppressFinalize(this); + } + + public async Task WriteAsync(Exception exception, string module = null, string method = null, + bool criticalException = false, + bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, + string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, + string grayLogSource = null, string grayLogVersion = "1.1") + { + if (exception == null) + { + return; + } + + await WriteAsync(exception.FormatExceptionToMessage(module, method), + criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers, + sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, + grayLogVersion); + } + + public async Task WriteAsync(string message, ELogType logType = ELogType.INFO, bool? writeToConsole = null, + bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string customSplunkSourceType = null, + bool? sendToGrayLogServers = null, string grayLogFacility = null, string grayLogSource = null, + string grayLogVersion = "1.1") + { + if (logType == ELogType.NONE) + { + return; + } + + await InternalWriteAsync(CurrentDateTime, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, + customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion); + } + + public event EventHandler OnException; + + private static LoggerSettings CreateDefaultSettings() + { + var settings = new LoggerSettings + { + Id = "EonaCatLogger", + MaxLogType = ELogType.INFO + }; + return settings; + } + + protected virtual async Task DisposeAsync(bool disposing) + { + if (disposing) + { + _isDisposing = true; + await StopLoggingAsync(); + await Task.Delay(100); + } + } + + public async Task StartNewLogAsync() + { + if (_tokenSource.IsCancellationRequested) + { + return; + } + + if (IsRunning && CurrentDateTime.Date > _logDate.Date) + { + await StopLoggingAsync(); + } + + IsRunning = true; + + CreateLogger(); + + Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory); + + _logDate = CurrentDateTime; + } + + private void CreateLogger() + { + // Dispose of previous ServiceProvider if it exists + LoggerProvider?.Dispose(); + LoggerFactory?.Dispose(); + + IServiceCollection serviceCollection = new ServiceCollection(); + serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel()) + .AddEonaCatFileLogger(configuration => + { + var fileLoggerOptions = Settings.FileLoggerOptions; + configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries; + configuration.RetainedFileCountLimit = fileLoggerOptions.RetainedFileCountLimit; + configuration.FlushPeriod = fileLoggerOptions.FlushPeriod; + configuration.IsEnabled = fileLoggerOptions.IsEnabled; + configuration.BatchSize = fileLoggerOptions.BatchSize; + configuration.FileSizeLimit = fileLoggerOptions.FileSizeLimit; + configuration.LogDirectory = fileLoggerOptions.LogDirectory; + configuration.FileNamePrefix = fileLoggerOptions.FileNamePrefix; + configuration.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles; + configuration.UseLocalTime = Settings.UseLocalTime; + })); + + var serviceProvider = serviceCollection.BuildServiceProvider(); + LoggerProvider = serviceProvider.GetService(); + LoggerFactory = serviceProvider.GetService(); + Logger = LoggerFactory.CreateLogger(Settings.Id); + LogHelper.SendToFile(Logger, Settings, ELogType.INFO, LogHelper.GetStartupMessage()); + } + + private async Task InternalWriteAsync(DateTime dateTime, string message, ELogType logType = ELogType.INFO, + bool? writeToConsole = null, bool? sendToSyslogServers = null, bool? sendToSplunkServers = null, + string customSplunkSourceType = null, bool? sendToGrayLogServers = null, string grayLogFacility = null, + string grayLogSource = null, string grayLogVersion = "1.1") + { + if (string.IsNullOrEmpty(message) || logType == ELogType.NONE || + (int)logType > (int)Settings.MaxLogType) + { + return; + } + + if (_isDisposing) + { + return; + } + + if (!IsRunning) + { + await StartNewLogAsync(); + } + + var messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime, _category); + var writeToConsoleValue = writeToConsole ?? Settings.EnableConsole; + var sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers; + var sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers; + var sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers; + + LogHelper.SendToFile(Logger, Settings, logType, message); + + if (writeToConsoleValue) + { + LogHelper.SendToConsole(Settings, logType, messageWithHeader, true); + } + + var tasks = new List(); + if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue) { if (sendToSyslogServersValue) - await LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true); + { + tasks.Add(LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true)); + } if (sendToSplunkServersValue) - await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), - messageWithHeader, true); + { + tasks.Add(LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), + messageWithHeader, true)); + } if (sendToGrayLogServersValue) - await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, - grayLogSource, true, grayLogVersion); - }); + { + tasks.Add(LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, + grayLogSource, true, grayLogVersion)); + } - var logMessage = new EonaCatLogMessage + await Task.WhenAll(tasks); + } + + var logMessage = new EonaCatLogMessage + { + DateTime = dateTime, + Message = message, + LogType = logType, + Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin + }; + + Settings.OnLogEvent(logMessage); + } + + public void Reset() { - DateTime = dateTime, - Message = message, - LogType = logType, - Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin - }; + Settings.ResetLogEvent(); + } - Settings.OnLogEvent(logMessage); - } + private void LogHelper_OnException(object sender, ErrorMessage e) + { + OnException?.Invoke(sender, e); + } - public void Reset() - { - Settings.ResetLogEvent(); - } + private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true) + { + if (settings == null) + { + settings = Settings; + } - private void LogHelper_OnException(object sender, ErrorMessage e) - { - OnException?.Invoke(sender, e); - } + if (!settings.EnableFileLogging) + { + return; + } - private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true) - { - if (settings == null) - settings = Settings; + if (logFolder != null) + { + settings.FileLoggerOptions.LogDirectory = logFolder; + } - if (!settings.EnableFileLogging) - return; + if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix)) + { + settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty; + } + } - if (logFolder != null) - settings.FileLoggerOptions.LogDirectory = logFolder; + private void SetupLogManager() + { + AppDomain.CurrentDomain.ProcessExit += ProcessExit; + _logDate = CurrentDateTime; + } - if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix)) - settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty; - } + private void ProcessExit(object sender, EventArgs e) + { + Dispose(); + } - private void SetupLogManager() - { - AppDomain.CurrentDomain.ProcessExit += ProcessExit; - _logDate = CurrentDateTme; - } + private Task StopLoggingAsync() + { + WriteStopMessage(); + IsRunning = false; + return Task.CompletedTask; + } - private void ProcessExit(object sender, EventArgs e) - { - Dispose(); - } + private void WriteStopMessage() + { + var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}"; + LogHelper.SendToFile(Logger, Settings, ELogType.INFO, stopMessage); + } - private Task StopLoggingAsync() - { - WriteStopMessage(); - IsRunning = false; - return Task.CompletedTask; - } - - private void WriteStopMessage() - { - var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}"; - LogHelper.SendToFile(Logger, Settings, ELogType.INFO, stopMessage); - } - - public void DeleteCurrentLogFile() - { - if (CurrentLogFile != null) - File.Delete(CurrentLogFile); + public void DeleteCurrentLogFile() + { + if (!string.IsNullOrEmpty(CurrentLogFile)) + { + File.Delete(CurrentLogFile); + } + } } } \ No newline at end of file diff --git a/EonaCat.Logger/Managers/LoggerSettings.cs b/EonaCat.Logger/Managers/LoggerSettings.cs index 6080476..4124f9c 100644 --- a/EonaCat.Logger/Managers/LoggerSettings.cs +++ b/EonaCat.Logger/Managers/LoggerSettings.cs @@ -21,7 +21,7 @@ public class LoggerSettings private FileLoggerOptions _fileLoggerOptions; - private string _headerFormat = "{ts} {host} {thread} {sev}"; + private string _headerFormat = "{ts} {host} {category} {thread} {sev}"; private int _maxMessageLength = 1024; private string _timestampFormat = "yyyy-MM-dd HH:mm:ss"; @@ -43,9 +43,10 @@ public class LoggerSettings /// variables including: /// {ts}: UTC timestamp /// {host}: Hostname + /// {category}: Category /// {thread}: Thread ID /// {sev}: Severity - /// Default: {ts} {host} {thread} {sev} + /// Default: {ts} {host} {category} {thread} {sev} /// A space will be inserted between the header and the message. /// public string HeaderFormat @@ -53,8 +54,14 @@ public class LoggerSettings get => _headerFormat; set { - if (string.IsNullOrEmpty(value)) _headerFormat = ""; - else _headerFormat = value; + if (string.IsNullOrEmpty(value)) + { + _headerFormat = ""; + } + else + { + _headerFormat = value; + } } } @@ -66,7 +73,11 @@ public class LoggerSettings get => _timestampFormat; set { - if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(HeaderFormat)); + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentNullException(nameof(HeaderFormat)); + } + _timestampFormat = value; } } @@ -81,8 +92,14 @@ public class LoggerSettings get => _enableConsole; set { - if (value) _enableConsole = ConsoleExists(); - else _enableConsole = false; + if (value) + { + _enableConsole = ConsoleExists(); + } + else + { + _enableConsole = false; + } } } @@ -99,7 +116,11 @@ public class LoggerSettings get => _colors; set { - if (value == null) throw new ArgumentNullException(nameof(Colors)); + if (value == null) + { + throw new ArgumentNullException(nameof(Colors)); + } + _colors = value; } } @@ -130,7 +151,11 @@ public class LoggerSettings { get { - if (_fileLoggerOptions == null) _fileLoggerOptions = CreateDefaultFileLoggerOptions(); + if (_fileLoggerOptions == null) + { + _fileLoggerOptions = CreateDefaultFileLoggerOptions(); + } + return _fileLoggerOptions; } diff --git a/EonaCat.Logger/Splunk/SplunkServer.cs b/EonaCat.Logger/Splunk/SplunkServer.cs index df6c75b..dbc4cad 100644 --- a/EonaCat.Logger/Splunk/SplunkServer.cs +++ b/EonaCat.Logger/Splunk/SplunkServer.cs @@ -29,7 +29,10 @@ public class SplunkServer SplunkHecUrl = splunkHecUrl; SplunkHecToken = splunkHecToken; - if (httpClientHandler == null) httpClientHandler = new HttpClientHandler(); + if (httpClientHandler == null) + { + httpClientHandler = new HttpClientHandler(); + } SplunkClientHandler = httpClientHandler; CreateHttpClient(); @@ -45,7 +48,10 @@ public class SplunkServer set { if (!string.IsNullOrWhiteSpace(_splunkHecUrl) && !_splunkHecUrl.ToLower().Contains("http")) + { value = $"http://{value}"; + } + _splunkHecUrl = value; } } @@ -103,7 +109,10 @@ public class SplunkServer public async Task SendAsync(SplunkPayload splunkPayload) { - if (splunkPayload == null) return null; + if (splunkPayload == null) + { + return null; + } // Create an event object with the required fields var eventObject = new @@ -116,7 +125,10 @@ public class SplunkServer // Serialize the event object to JSON var eventJson = JsonHelper.ToJson(eventObject); - if (!HasHecToken) CreateHttpClient(); + if (!HasHecToken) + { + CreateHttpClient(); + } // Create an HTTP content with the event data var content = new StringContent(eventJson, Encoding.UTF8, "application/json"); diff --git a/EonaCat.Logger/Syslog/SyslogServer.cs b/EonaCat.Logger/Syslog/SyslogServer.cs index 383b898..e725a9c 100644 --- a/EonaCat.Logger/Syslog/SyslogServer.cs +++ b/EonaCat.Logger/Syslog/SyslogServer.cs @@ -42,7 +42,11 @@ public class SyslogServer set { - if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname)); + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentNullException(nameof(Hostname)); + } + _Hostname = value; SetUdp(); @@ -57,7 +61,11 @@ public class SyslogServer get => _Port; set { - if (value < 0) throw new ArgumentException("Port must be zero or greater."); + if (value < 0) + { + throw new ArgumentException("Port must be zero or greater."); + } + _Port = value; SetUdp(); diff --git a/README.md b/README.md index 33485b1..087dea8 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ Microsoft.Extensions.Logging **Easy way of logging without configuring anything:** ```csharp -LogManager.Instance.Write("INFO", ELogType.INFO, true); -LogManager.Instance.Write("WARNING", ELogType.WARNING, true); -LogManager.Instance.Write("ERROR", ELogType.ERROR, true); -LogManager.Instance.Write("DEBUG", ELogType.DEBUG, true); -LogManager.Instance.Write("CRITICAL", ELogType.CRITICAL, true); -LogManager.Instance.Write("TRACE", ELogType.TRACE, true); -LogManager.Instance.Write("TRAFFIC", ELogType.TRAFFIC, true); -LogManager.Instance.Write("NONE", ELogType.NONE, true); +LogManager.Instance.WriteAsync("INFO", ELogType.INFO, true); +LogManager.Instance.WriteAsync("WARNING", ELogType.WARNING, true); +LogManager.Instance.WriteAsync("ERROR", ELogType.ERROR, true); +LogManager.Instance.WriteAsync("DEBUG", ELogType.DEBUG, true); +LogManager.Instance.WriteAsync("CRITICAL", ELogType.CRITICAL, true); +LogManager.Instance.WriteAsync("TRACE", ELogType.TRACE, true); +LogManager.Instance.WriteAsync("TRAFFIC", ELogType.TRAFFIC, true); +LogManager.Instance.WriteAsync("NONE", ELogType.NONE, true); ``` **Logging in .NET 4.8 or higher: ** @@ -186,28 +186,28 @@ namespace EonaCat.Logger.Advanced // Logs an informational message. public static void Info(string message, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) { - Write(message, ELogType.INFO, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); + WriteAsync(message, ELogType.INFO, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); } // Internal method to write logs. - private static void Write(string message, ELogType logType, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) + private static void WriteAsync(string message, ELogType logType, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) { if (grayLogSettings != null) { // Log the message with specified settings. - _logManager.Write(message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings.Facility, grayLogSettings.Source, grayLogSettings.Version); + _logManager.WriteAsync(message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings.Facility, grayLogSettings.Source, grayLogSettings.Version); } else { // Log the message with default settings. - _logManager.Write(message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers); + _logManager.WriteAsync(message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers); } } // Logs a debug message. public static void Debug(string message, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) { - Write(message, ELogType.DEBUG, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); + WriteAsync(message, ELogType.DEBUG, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); } // Logs an error message along with an exception. @@ -216,12 +216,12 @@ namespace EonaCat.Logger.Advanced if (grayLogSettings != null) { // Log the exception and message with specified settings. - _logManager.Write(exception, message, criticalException: isCriticalException, writeToConsole: writeToConsole, sendToSysLogServers: sendToSysLogServers, sendToSplunkServers: sendToSplunkServers, customSplunkSourceType: customSplunkSourceType, sendToGrayLogServers: sendToGrayLogServers, grayLogFacility: grayLogSettings.Facility, grayLogSource: grayLogSettings.Source, grayLogVersion: grayLogSettings.Version); + _logManager.WriteAsync(exception, message, criticalException: isCriticalException, writeToConsole: writeToConsole, sendToSysLogServers: sendToSysLogServers, sendToSplunkServers: sendToSplunkServers, customSplunkSourceType: customSplunkSourceType, sendToGrayLogServers: sendToGrayLogServers, grayLogFacility: grayLogSettings.Facility, grayLogSource: grayLogSettings.Source, grayLogVersion: grayLogSettings.Version); } else { // Log the exception and message with default settings. - _logManager.Write(exception, message, criticalException: isCriticalException, writeToConsole: writeToConsole, sendToSysLogServers: sendToSysLogServers, sendToSplunkServers: sendToSplunkServers, customSplunkSourceType: customSplunkSourceType, sendToGrayLogServers: sendToGrayLogServers); + _logManager.WriteAsync(exception, message, criticalException: isCriticalException, writeToConsole: writeToConsole, sendToSysLogServers: sendToSysLogServers, sendToSplunkServers: sendToSplunkServers, customSplunkSourceType: customSplunkSourceType, sendToGrayLogServers: sendToGrayLogServers); } } @@ -231,37 +231,37 @@ namespace EonaCat.Logger.Advanced if (isCriticalException) { // Log a critical error message. - _logManager.Write(message, ELogType.CRITICAL, writeToConsole); + _logManager.WriteAsync(message, ELogType.CRITICAL, writeToConsole); } else { // Log a regular error message. - _logManager.Write(message, ELogType.ERROR, writeToConsole); + _logManager.WriteAsync(message, ELogType.ERROR, writeToConsole); } } // Logs a warning message. public static void Warning(string message, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) { - Write(message, ELogType.WARNING, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); + WriteAsync(message, ELogType.WARNING, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); } // Logs a critical message. public static void Critical(string message, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) { - Write(message, ELogType.CRITICAL, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); + WriteAsync(message, ELogType.CRITICAL, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); } // Logs a traffic message. public static void Traffic(string message, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) { - Write(message, ELogType.TRAFFIC, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); + WriteAsync(message, ELogType.TRAFFIC, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); } // Logs a trace message. public static void Trace(string message, bool? writeToConsole = null, bool? sendToSysLogServers = null, bool? sendToSplunkServers = null, string? customSplunkSourceType = null, bool? sendToGrayLogServers = null, GrayLogSettings grayLogSettings = null) { - Write(message, ELogType.TRACE, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); + WriteAsync(message, ELogType.TRACE, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogSettings); } } } @@ -319,14 +319,14 @@ Task.Run(() => // 10.000.000 lines to go :) for (int i = 0; i < 10000000; i++) { - logger.Write($"test to file {i} INFO", ELogType.INFO); - logger.Write($"test to file {i} CRITICAL", ELogType.CRITICAL); - logger.Write($"test to file {i} DEBUG", ELogType.DEBUG); - logger.Write($"test to file {i} ERROR", ELogType.ERROR); - logger.Write($"test to file {i} TRACE", ELogType.TRACE); - logger.Write($"test to file {i} TRAFFIC", ELogType.TRAFFIC); - logger.Write($"test to file {i} WARNING", ELogType.WARNING); - logger.Write($"test to file {i} NONE", ELogType.NONE); + logger.WriteAsync($"test to file {i} INFO", ELogType.INFO); + logger.WriteAsync($"test to file {i} CRITICAL", ELogType.CRITICAL); + logger.WriteAsync($"test to file {i} DEBUG", ELogType.DEBUG); + logger.WriteAsync($"test to file {i} ERROR", ELogType.ERROR); + logger.WriteAsync($"test to file {i} TRACE", ELogType.TRACE); + logger.WriteAsync($"test to file {i} TRAFFIC", ELogType.TRAFFIC); + logger.WriteAsync($"test to file {i} WARNING", ELogType.WARNING); + logger.WriteAsync($"test to file {i} NONE", ELogType.NONE); Console.WriteLine($"Logged: {i}"); Task.Delay(1); } diff --git a/Testers/EonaCat.Logger.Test.Web/Logger.cs b/Testers/EonaCat.Logger.Test.Web/Logger.cs index cf6e0c0..bf9390d 100644 --- a/Testers/EonaCat.Logger.Test.Web/Logger.cs +++ b/Testers/EonaCat.Logger.Test.Web/Logger.cs @@ -5,29 +5,52 @@ using EonaCat.Logger.Managers; namespace EonaCat.Logger.Test.Web; -public static class Logger +public class Logger { - private static LogManager LogManager; - public static ELogType MaxLogType { get; set; } - public static bool UseLocalTime { get; set; } - public static string LogFolder => Path.Combine(FileLoggerOptions.DefaultPath, "logs"); - public static string CurrentLogFile => LogManager.CurrentLogFile; - public static bool IsDisabled { get; set; } + private LogManager _logManager; + public ELogType MaxLogType { get; set; } + public LoggerSettings LoggerSettings { get; } + public bool UseLocalTime { get; set; } + public string LogFolder { get; set; } = Path.Combine(FileLoggerOptions.DefaultPath, "logs"); + public string CurrentLogFile => _logManager.CurrentLogFile; + public bool IsDisabled { get; set; } - public static void DeleteCurrentLogFile() + public Logger(string name = "EonaCatTestLogger", ELogType maxLogType = ELogType.INFO, bool useLocalTime = false, int maxFileSize = 20_000_000) { - if (IsDisabled) - return; + UseLocalTime = useLocalTime; + MaxLogType = maxLogType; - LogManager.DeleteCurrentLogFile(); + LoggerSettings = new LoggerSettings + { + Id = name, + MaxLogType = MaxLogType, + UseLocalTime = UseLocalTime, + FileLoggerOptions = + { + LogDirectory = LogFolder, + FileSizeLimit = maxFileSize, + UseLocalTime = UseLocalTime, + }, + }; + _logManager = new LogManager(LoggerSettings); } - private static string ConvertToAbsolutePath(string path) + public void DeleteCurrentLogFile() + { + if (IsDisabled) + { + return; + } + + _logManager.DeleteCurrentLogFile(); + } + + private string ConvertToAbsolutePath(string path) { return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path); } - public static async Task DownloadLogAsync(HttpContext context, string logName, long limit) + public async Task DownloadLogAsync(HttpContext context, string logName, long limit) { var logFileName = logName + ".log"; @@ -39,7 +62,9 @@ public static class Logger response.Headers.ContentDisposition = "attachment;filename=" + logFileName; if (limit > fS.Length || limit < 1) + { limit = fS.Length; + } var oFS = new OffsetStream(fS, 0, limit); var request = context.Request; @@ -81,43 +106,32 @@ public static class Logger await oFS.CopyToAsync(stream).ConfigureAwait(false); if (fS.Length > limit) + { await stream.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false); + } } } - public static async Task LogAsync(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true) + public async Task LogAsync(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true) { if (IsDisabled) + { return; + } - await LogManager.WriteAsync(message, logType, writeToConsole).ConfigureAwait(false); + await _logManager.WriteAsync(message, logType, writeToConsole).ConfigureAwait(false); } - public static async Task LogAsync(Exception exception, string message = "", bool writeToConsole = true) + public async Task LogAsync(Exception exception, string message = "", bool writeToConsole = true) { if (IsDisabled) + { return; + } if (ELogType.ERROR <= MaxLogType) { - await LogManager.WriteAsync(exception, message, writeToConsole: writeToConsole).ConfigureAwait(false); + await _logManager.WriteAsync(exception, message, writeToConsole: writeToConsole).ConfigureAwait(false); } } - - public static void Configure() - { - var loggerSettings = new LoggerSettings - { - Id = "EonaCatTestLogger", - MaxLogType = ELogType.INFO, - UseLocalTime = UseLocalTime, - FileLoggerOptions = - { - LogDirectory = LogFolder, - FileSizeLimit = 20_000_000, // 20 MB, - UseLocalTime = UseLocalTime - } - }; - LogManager = new LogManager(loggerSettings); - } } \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Program.cs b/Testers/EonaCat.Logger.Test.Web/Program.cs index 1c3d7fa..3b0d31d 100644 --- a/Testers/EonaCat.Logger.Test.Web/Program.cs +++ b/Testers/EonaCat.Logger.Test.Web/Program.cs @@ -1,6 +1,7 @@ using EonaCat.Logger; using EonaCat.Logger.EonaCatCoreLogger; using EonaCat.Logger.EonaCatCoreLogger.Extensions; +using EonaCat.Logger.EonaCatCoreLogger.Models; using EonaCat.Logger.Managers; using EonaCat.Logger.Test.Web; using EonaCat.Web.RateLimiter; @@ -8,11 +9,21 @@ using EonaCat.Web.RateLimiter.Endpoints.Extensions; using EonaCat.Web.Tracer.Extensions; var builder = WebApplication.CreateBuilder(args); +int onLogCounter = 0; +var defaultColor = Console.ForegroundColor; // Add services to the container. -Logger.UseLocalTime = true; -Logger.MaxLogType = ELogType.TRACE; -Logger.Configure(); +Logger logger = new Logger(); +logger.UseLocalTime = true; +logger.MaxLogType = ELogType.TRACE; +logger.LoggerSettings.OnLog += LoggerSettings_OnLog; + +void LoggerSettings_OnLog(EonaCatLogMessage message) +{ + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"LogCounter: {++onLogCounter} {message}"); + Console.ForegroundColor = defaultColor; +} var options = new FileLoggerOptions(); options.MaxRolloverFiles = 5; @@ -124,11 +135,11 @@ void RunLoggingExceptionTests() } } -Task.Run(RunWebLoggerTests).ConfigureAwait(false); -Task.Run(RunWebLoggingTests).ConfigureAwait(false); -Task.Run(RunLoggingTests).ConfigureAwait(false); -Task.Run(RunLoggingExceptionTests).ConfigureAwait(false); -Task.Run(RunWebLoggingExceptionTests).ConfigureAwait(false); +await Task.Run(RunWebLoggerTestsAsync).ConfigureAwait(false); +await Task.Run(RunWebLoggingTests).ConfigureAwait(false); +await Task.Run(RunLoggingTestsAsync).ConfigureAwait(false); +await Task.Run(RunLoggingExceptionTests).ConfigureAwait(false); +await Task.Run(RunWebLoggingExceptionTests).ConfigureAwait(false); void RunWebLoggingExceptionTests() { @@ -155,12 +166,15 @@ void RunWebLoggingExceptionTests() async void RunWebLoggingTests() { - if (!Directory.Exists(Logger.LogFolder)) Directory.CreateDirectory(Logger.LogFolder); + if (!Directory.Exists(logger.LogFolder)) + { + Directory.CreateDirectory(logger.LogFolder); + } for (var i = 0; i < 9000000; i++) { app.Logger.LogInformation($"web-test {i}"); - using (var file = new StreamWriter(Path.Combine(Logger.LogFolder, "test.log"), true)) + using (var file = new StreamWriter(Path.Combine(logger.LogFolder, "test.log"), true)) { await file.WriteAsync($"WebLogged: {i}{Environment.NewLine}").ConfigureAwait(false); } @@ -169,7 +183,7 @@ async void RunWebLoggingTests() } } -void RunLoggingTests() +async Task RunLoggingTestsAsync() { var loggerSettings = new LoggerSettings(); loggerSettings.UseLocalTime = true; @@ -182,38 +196,37 @@ void RunLoggingTests() for (var i = 0; i < 9000000; i++) { - logger.WriteAsync($"test to file {i} INFO").ConfigureAwait(false); - logger.WriteAsync($"test to file {i} CRITICAL", ELogType.CRITICAL).ConfigureAwait(false); - logger.WriteAsync($"test to file {i} DEBUG", ELogType.DEBUG).ConfigureAwait(false); - logger.WriteAsync($"test to file {i} ERROR", ELogType.ERROR).ConfigureAwait(false); - logger.WriteAsync($"test to file {i} TRACE", ELogType.TRACE).ConfigureAwait(false); - logger.WriteAsync($"test to file {i} TRAFFIC", ELogType.TRAFFIC).ConfigureAwait(false); - logger.WriteAsync($"test to file {i} WARNING", ELogType.WARNING).ConfigureAwait(false); - logger.WriteAsync($"test to file {i} NONE", ELogType.NONE).ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} INFO").ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} CRITICAL", ELogType.CRITICAL).ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} DEBUG", ELogType.DEBUG).ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} ERROR", ELogType.ERROR).ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} TRACE", ELogType.TRACE).ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} TRAFFIC", ELogType.TRAFFIC).ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} WARNING", ELogType.WARNING).ConfigureAwait(false); + await logger.WriteAsync($"test to file {i} NONE", ELogType.NONE).ConfigureAwait(false); Console.WriteLine($"Logged: {i}"); - Task.Delay(1); + await Task.Delay(1).ConfigureAwait(false); } } -void RunWebLoggerTests() +async Task RunWebLoggerTestsAsync() { var i = 0; while (true) { i++; - Task.Run(() => + await Task.Run(async () => { - Logger.LogAsync($"test via logger {i} INFO"); - Logger.LogAsync($"test via logger {i} CRITICAL", ELogType.CRITICAL); - Logger.LogAsync($"test via logger {i} DEBUG", ELogType.DEBUG); - Logger.LogAsync($"test via logger {i} ERROR", ELogType.ERROR); - Logger.LogAsync($"test via logger {i} TRACE", ELogType.TRACE); - Logger.LogAsync($"test via logger {i} TRAFFIC", ELogType.TRAFFIC); - Logger.LogAsync($"test via logger {i} WARNING", ELogType.WARNING); - Logger.LogAsync($"test via logger {i} NONE", ELogType.NONE); - }); - Console.WriteLine($"Logger: {i}"); - Task.Delay(1); + await logger.LogAsync($"test via logger {i} INFO").ConfigureAwait(false); + await logger.LogAsync($"test via logger {i} CRITICAL", ELogType.CRITICAL).ConfigureAwait(false); + await logger.LogAsync($"test via logger {i} DEBUG", ELogType.DEBUG).ConfigureAwait(false); + await logger.LogAsync($"test via logger {i} ERROR", ELogType.ERROR).ConfigureAwait(false); + await logger.LogAsync($"test via logger {i} TRACE", ELogType.TRACE).ConfigureAwait(false); + await logger.LogAsync($"test via logger {i} TRAFFIC", ELogType.TRAFFIC).ConfigureAwait(false); + await logger.LogAsync($"test via logger {i} WARNING", ELogType.WARNING).ConfigureAwait(false); + await logger.LogAsync($"test via logger {i} NONE", ELogType.NONE).ConfigureAwait(false); + }).ConfigureAwait(false); + await Task.Delay(1).ConfigureAwait(false); } }