From 57f0e8030fccca7f311b26f37fa0090ec4391e84 Mon Sep 17 00:00:00 2001 From: EonaCat Date: Sat, 31 Jan 2026 00:30:37 +0100 Subject: [PATCH] Updated --- .../EonaCat.Logger.LogClient.csproj | 4 +- .../EonaCatCoreLogger/FileLoggerOptions.cs | 3 + .../EonaCatCoreLogger/FileLoggerProvider.cs | 1 - .../Internal/BatchingLogger.cs | 4 +- .../Internal/BatchingLoggerProvider.cs | 23 +- EonaCat.Logger/Logger.cs | 15 +- EonaCat.Logger/Managers/HeaderTokens.cs | 128 ++++++++ EonaCat.Logger/Managers/LogHelper.cs | 296 +++++++----------- EonaCat.Logger/Managers/LogManager.cs | 29 +- EonaCat.Logger/Managers/LoggerSettings.cs | 6 +- Testers/EonaCat.Logger.Test.Web/Logger.cs | 28 +- Testers/EonaCat.Logger.Test.Web/Program.cs | 34 +- 12 files changed, 326 insertions(+), 245 deletions(-) create mode 100644 EonaCat.Logger/Managers/HeaderTokens.cs diff --git a/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj b/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj index 134926f..edc9919 100644 --- a/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj +++ b/EonaCat.Logger.LogClient/EonaCat.Logger.LogClient.csproj @@ -25,9 +25,11 @@ - + + + True diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs index 8008735..42c9ebe 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using EonaCat.Logger.EonaCatCoreLogger.Internal; +using EonaCat.Logger.Managers; namespace EonaCat.Logger.EonaCatCoreLogger; // This file is part of the EonaCat project(s) which is released under the Apache License. @@ -144,4 +145,6 @@ public class FileLoggerOptions : BatchingLoggerOptions /// Determines if we need to include the correlation ID in the log (default: false) /// public bool IncludeCorrelationId { get; set; } + public LoggerSettings Settings { get; internal set; } + public LoggerSettings LoggerSettings { get; internal set; } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs index 17aa586..05511a4 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs @@ -94,7 +94,6 @@ public class FileLoggerProvider : BatchingLoggerProvider foreach (var group in messages.GroupBy(GetGrouping)) { LogFile = GetFullName(group.Key); - var currentMessages = string.Join(string.Empty, group.Select(item => { if (IncludeCorrelationId) diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs index 5bd70a9..dadf54f 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs @@ -13,13 +13,13 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Internal { private readonly string _category; private readonly BatchingLoggerProvider _provider; - private readonly LoggerSettings _loggerSettings; + private LoggerSettings _loggerSettings; public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings) { + _loggerSettings = loggerSettings ?? throw new ArgumentNullException(nameof(loggerSettings)); _provider = loggerProvider ?? throw new ArgumentNullException(nameof(loggerProvider)); _category = categoryName ?? throw new ArgumentNullException(nameof(categoryName)); - _loggerSettings = loggerSettings ?? throw new ArgumentNullException(nameof(loggerSettings)); } private DateTimeOffset CurrentDateTimeOffset => CurrentDateTime; diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs index 07be424..21ed824 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs @@ -25,6 +25,7 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable private CancellationTokenSource _cancellationTokenSource; private Task _outputTask; private bool _isDisposed; + private LoggerSettings _loggerSettings; protected BatchingLoggerProvider(IOptions options) { @@ -40,6 +41,7 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable { UseLocalTime = fileLoggerOptions.UseLocalTime; UseMask = fileLoggerOptions.UseMask; + LoggerSettings = fileLoggerOptions.LoggerSettings; } _batchSize = loggerOptions.BatchSize > 0 ? loggerOptions.BatchSize : 100; @@ -51,30 +53,27 @@ public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable protected bool UseLocalTime { get; set; } + public bool IsStarted { get; set; } + public bool UseMask { get; set; } + public string Category { get; set; } = "General"; + protected LoggerSettings LoggerSettings { get { - if (_loggerSettings != null) + if (_loggerSettings == null) { - return _loggerSettings; + _loggerSettings = new LoggerSettings(); + _loggerSettings.UseLocalTime = UseLocalTime; + _loggerSettings.UseMask = UseMask; + //TODO: Add the tokens and custom tokens } - - _loggerSettings = new LoggerSettings(); - _loggerSettings.UseLocalTime = UseLocalTime; - _loggerSettings.UseMask = UseMask; return _loggerSettings; } set => _loggerSettings = value; } - public bool IsStarted { get; set; } - public bool UseMask { get; set; } - public string Category { get; set; } = "General"; - - private LoggerSettings _loggerSettings; - /// /// Creates a new logger instance for the specified category name. /// diff --git a/EonaCat.Logger/Logger.cs b/EonaCat.Logger/Logger.cs index 34e8ac6..19d3827 100644 --- a/EonaCat.Logger/Logger.cs +++ b/EonaCat.Logger/Logger.cs @@ -48,9 +48,6 @@ namespace EonaCat.Logger IsEnabled = true, }, }; - - _logManager = new LogManager(LoggerSettings); - _logManager.Settings.TypesToLog.Clear(); } /// @@ -87,9 +84,20 @@ namespace EonaCat.Logger return; } + InitLogger(); await _logManager.WriteAsync(message, logType, writeToConsole).ConfigureAwait(false); } + private void InitLogger() + { + if (_logManager == null) + { + // Initialize LogManager + _logManager = new LogManager(LoggerSettings); + _logManager.Settings.TypesToLog.Clear(); + } + } + /// /// Asynchronously logs the specified exception and an optional message, with the option to also write the log /// entry to the console. @@ -108,6 +116,7 @@ namespace EonaCat.Logger return; } + InitLogger(); if (LoggerSettings.TypesToLog.Contains(ELogType.ERROR)) { await _logManager.WriteAsync(exception, message, writeToConsole: writeToConsole).ConfigureAwait(false); diff --git a/EonaCat.Logger/Managers/HeaderTokens.cs b/EonaCat.Logger/Managers/HeaderTokens.cs new file mode 100644 index 0000000..37e5548 --- /dev/null +++ b/EonaCat.Logger/Managers/HeaderTokens.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using static EonaCat.Logger.Managers.LogHelper; + +// 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. + +namespace EonaCat.Logger.Managers; + +/// +/// Provides a collection of predefined and customizable header token resolvers for formatting log or message headers. +/// +/// HeaderTokens exposes a set of standard tokens that can be used to insert runtime, environment, and +/// contextual information into header strings, such as timestamps, process details, thread information, and more. +/// Custom tokens can be added or overridden to support application-specific formatting needs. All token keys are +/// case-insensitive. This class is thread-safe for read operations, but adding or overriding tokens is not guaranteed +/// to be thread-safe and should be synchronized if used concurrently. +public class HeaderTokens +{ + private readonly Dictionary> _tokenResolvers = + new(StringComparer.OrdinalIgnoreCase) + { + ["date"] = ctx => $"[Date: {ctx.Timestamp:yyyy-MM-dd}]", + + ["time"] = ctx => $"[Time: {ctx.Timestamp:HH:mm:ss.fff}]", + + ["ticks"] = ctx => $"[Ticks: {ctx.Timestamp.Ticks}]", + + ["ts"] = ctx => $"[{ctx.Timestamp.ToString(ctx.TimestampFormat)}]", + + ["tz"] = ctx => $"[{(ctx.Timestamp.Kind == DateTimeKind.Utc ? "UTC" : "LOCAL")}]", + + ["unix"] = ctx => $"[Unix: {new DateTimeOffset(ctx.Timestamp).ToUnixTimeSeconds()}]", + + ["procstart"] = _ => + { + var p = Process.GetCurrentProcess(); + return $"[ProcStart: {p.StartTime:O}]"; + }, + + ["uptime"] = _ => + { + var p = Process.GetCurrentProcess(); + return $"[Uptime: {(DateTime.Now - p.StartTime).TotalSeconds:F0}s]"; + }, + + ["framework"] = _ => + $"[Runtime: {RuntimeInformation.FrameworkDescription}]", + + ["os"] = _ => + $"[OS: {RuntimeInformation.OSDescription}]", + + ["arch"] = _ => + $"[Arch: {RuntimeInformation.ProcessArchitecture}]", + + ["mem"] = _ => $"[Memory: {GC.GetTotalMemory(false) / 1024 / 1024}MB]", + + ["gc"] = _ => $"[GC: {GC.CollectionCount(0)}/{GC.CollectionCount(1)}/{GC.CollectionCount(2)}]", + + ["cwd"] = _ => $"[CWD: {Environment.CurrentDirectory}]", + + ["app"] = _ => $"[App: {AppDomain.CurrentDomain.FriendlyName}]", + + ["appbase"] = _ => $"[AppBase: {AppDomain.CurrentDomain.BaseDirectory}]", + + ["domain"] = _ => $"[Domain: {AppDomain.CurrentDomain.Id}]", + + ["threadname"] = _ => $"[ThreadName: {Thread.CurrentThread.Name ?? "n/a"}]", + + ["task"] = _ => $"[TaskId: {Task.CurrentId?.ToString() ?? "n/a"}]", + + ["host"] = ctx => $"[Host: {ctx.HostName}]", + + ["machine"] = _ => $"[Machine: {Environment.MachineName}]", + + ["category"] = ctx => $"[Category: {ctx.Category}]", + + ["thread"] = _ => $"[Thread: {Environment.CurrentManagedThreadId}]", + + ["process"] = _ => + { + var p = Process.GetCurrentProcess(); + return $"[Process: {p.ProcessName}]"; + }, + + ["pid"] = _ => $"[PID: {Process.GetCurrentProcess().Id}]", + + ["sev"] = ctx => $"[Severity: {ctx.LogType}]", + + ["LogType"] = ctx => $"[{ctx.LogType}]", + + ["user"] = _ => $"[User: {Environment.UserName}]", + + ["env"] = ctx => $"[Env: {ctx.EnvironmentName}]", + + ["newline"] = _ => Environment.NewLine + }; + + /// + /// Gets a read-only dictionary of token resolver functions used to generate header values based on a token name and + /// header context. + /// + /// Each entry maps a token name to a function that produces the corresponding header value for a + /// given . The dictionary is static and cannot be modified at runtime. + public IReadOnlyDictionary> TokenResolvers => _tokenResolvers; + + /// + /// Adds or overrides a custom token. + /// + public void AddCustomToken(string key, Func resolver) + { + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("Token key cannot be null or whitespace.", nameof(key)); + } + + if (resolver == null) + { + throw new ArgumentNullException(nameof(resolver)); + } + + _tokenResolvers[key] = resolver; + } +} diff --git a/EonaCat.Logger/Managers/LogHelper.cs b/EonaCat.Logger/Managers/LogHelper.cs index efb702a..6f50d89 100644 --- a/EonaCat.Logger/Managers/LogHelper.cs +++ b/EonaCat.Logger/Managers/LogHelper.cs @@ -1,9 +1,12 @@ using EonaCat.Json; +using EonaCat.Logger.EonaCatCoreLogger; +using EonaCat.Logger.EonaCatCoreLogger.Internal; using EonaCat.Logger.Extensions; using EonaCat.Logger.Servers.GrayLog; using EonaCat.Logger.Servers.Splunk.Models; using Microsoft.Extensions.Logging; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -20,120 +23,6 @@ using static EonaCat.Logger.Managers.LogHelper; namespace EonaCat.Logger.Managers; -/// -/// Provides a collection of predefined and customizable header token resolvers for formatting log or message headers. -/// -/// HeaderTokens exposes a set of standard tokens that can be used to insert runtime, environment, and -/// contextual information into header strings, such as timestamps, process details, thread information, and more. -/// Custom tokens can be added or overridden to support application-specific formatting needs. All token keys are -/// case-insensitive. This class is thread-safe for read operations, but adding or overriding tokens is not guaranteed -/// to be thread-safe and should be synchronized if used concurrently. -public class HeaderTokens -{ - private readonly Dictionary> _tokenResolvers = - new(StringComparer.OrdinalIgnoreCase) - { - ["date"] = ctx => $"[Date: {ctx.Timestamp:yyyy-MM-dd}]", - - ["time"] = ctx => $"[Time: {ctx.Timestamp:HH:mm:ss.fff}]", - - ["ticks"] = ctx => $"[Ticks: {ctx.Timestamp.Ticks}]", - - ["ts"] = ctx => $"[{ctx.Timestamp.ToString(ctx.TimestampFormat)}]", - - ["tz"] = ctx => $"[{(ctx.Timestamp.Kind == DateTimeKind.Utc ? "UTC" : "LOCAL")}]", - - ["unix"] = ctx => $"[Unix: {new DateTimeOffset(ctx.Timestamp).ToUnixTimeSeconds()}]", - - ["procstart"] = _ => - { - var p = Process.GetCurrentProcess(); - return $"[ProcStart: {p.StartTime:O}]"; - }, - - ["uptime"] = _ => - { - var p = Process.GetCurrentProcess(); - return $"[Uptime: {(DateTime.Now - p.StartTime).TotalSeconds:F0}s]"; - }, - - ["framework"] = _ => - $"[Runtime: {RuntimeInformation.FrameworkDescription}]", - - ["os"] = _ => - $"[OS: {RuntimeInformation.OSDescription}]", - - ["arch"] = _ => - $"[Arch: {RuntimeInformation.ProcessArchitecture}]", - - ["mem"] = _ => $"[Memory: {GC.GetTotalMemory(false) / 1024 / 1024}MB]", - - ["gc"] = _ => $"[GC: {GC.CollectionCount(0)}/{GC.CollectionCount(1)}/{GC.CollectionCount(2)}]", - - ["cwd"] = _ => $"[CWD: {Environment.CurrentDirectory}]", - - ["app"] = _ => $"[App: {AppDomain.CurrentDomain.FriendlyName}]", - - ["appbase"] = _ => $"[AppBase: {AppDomain.CurrentDomain.BaseDirectory}]", - - ["domain"] = _ => $"[Domain: {AppDomain.CurrentDomain.Id}]", - - ["threadname"] = _ => $"[ThreadName: {Thread.CurrentThread.Name ?? "n/a"}]", - - ["task"] = _ => $"[TaskId: {Task.CurrentId?.ToString() ?? "n/a"}]", - - ["host"] = ctx => $"[Host: {ctx.HostName}]", - - ["machine"] = _ => $"[Machine: {Environment.MachineName}]", - - ["category"] = ctx => $"[Category: {ctx.Category}]", - - ["thread"] = _ => $"[Thread: {Environment.CurrentManagedThreadId}]", - - ["process"] = _ => - { - var p = Process.GetCurrentProcess(); - return $"[Process: {p.ProcessName}]"; - }, - - ["pid"] = _ => $"[PID: {Process.GetCurrentProcess().Id}]", - - ["sev"] = ctx => $"[Severity: {ctx.LogType}]", - - ["user"] = _ => $"[User: {Environment.UserName}]", - - ["env"] = ctx => $"[Env: {ctx.EnvironmentName}]", - - ["newline"] = _ => Environment.NewLine - }; - - /// - /// Gets a read-only dictionary of token resolver functions used to generate header values based on a token name and - /// header context. - /// - /// Each entry maps a token name to a function that produces the corresponding header value for a - /// given . The dictionary is static and cannot be modified at runtime. - public IReadOnlyDictionary> TokenResolvers => _tokenResolvers; - - /// - /// Adds or overrides a custom token. - /// - public void AddCustomToken(string key, Func resolver) - { - if (string.IsNullOrWhiteSpace(key)) - { - throw new ArgumentException("Token key cannot be null or whitespace.", nameof(key)); - } - - if (resolver == null) - { - throw new ArgumentNullException(nameof(resolver)); - } - - _tokenResolvers[key] = resolver; - } -} - public class ErrorMessage { public Exception Exception { get; set; } @@ -160,10 +49,15 @@ public static class LogHelper private static readonly string MachineName = Environment.MachineName; private static readonly string HostName = Dns.GetHostName(); + private static readonly object _settingsLock = new object(); + + private static readonly object _consoleLock = new object(); + + private static readonly ConcurrentDictionary _regexCache = new ConcurrentDictionary(); + internal static string ConvertToSyslogRfc5424(LoggerSettings settings, ELogType logType, string currentMessage, DateTime dateTime, string category = null) { string formattedMessage = FormatMessageWithHeader(settings, logType, currentMessage, dateTime, category); - if (string.IsNullOrWhiteSpace(formattedMessage)) { return formattedMessage; @@ -188,7 +82,6 @@ public static class LogHelper internal static string ConvertToSyslogRfc3164(LoggerSettings settings, ELogType logType, string currentMessage, DateTime dateTime, string category = null) { string formattedMessage = FormatMessageWithHeader(settings, logType, currentMessage, dateTime, category); - if (string.IsNullOrWhiteSpace(formattedMessage)) { return formattedMessage; @@ -210,7 +103,9 @@ public static class LogHelper return string.Empty; } - return Regex.Replace(format, @"\{([^}]+)\}", match => + var regex = _regexCache.GetOrAdd(@"\{([^}]+)\}", pattern => new Regex(pattern, RegexOptions.Compiled)); + + return regex.Replace(format, match => { var tokenText = match.Groups[1].Value; @@ -230,15 +125,15 @@ public static class LogHelper }); } - - - - internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string message, DateTime dateTime, string category = null) + internal static string FormatMessageWithHeader( + LoggerSettings settings, + ELogType logType, + string message, + DateTime dateTime, + string category = null) { if (string.IsNullOrWhiteSpace(message)) - { return message; - } var ctx = new HeaderContext { @@ -251,19 +146,28 @@ public static class LogHelper EnvironmentName = settings?.EnvironmentName }; - string header = settings?.CustomHeaderFormatter != null - ? settings.CustomHeaderFormatter(ctx) - : ResolveHeader(settings?.HeaderFormat, ctx); + string header = string.Empty; - if (!string.IsNullOrEmpty(header)) + if (!string.IsNullOrWhiteSpace(settings?.HeaderFormat) || settings?.CustomHeaderFormatter != null) { - header += " "; + if (settings.CustomHeaderFormatter != null) + { + header = settings.CustomHeaderFormatter(ctx) ?? string.Empty; + } + else + { + header = ResolveHeader(settings.HeaderFormat, ctx); + } + + if (!string.IsNullOrEmpty(header)) + header += " "; } return header + message; } + internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message) { if (settings == null || string.IsNullOrWhiteSpace(message)) @@ -271,24 +175,27 @@ public static class LogHelper return; } - if (settings.EnableColors && settings.Colors != null) + lock (_consoleLock) { - var prevForeground = Console.ForegroundColor; - var prevBackground = Console.BackgroundColor; - - if (TryGetLogColor(settings.Colors, logType, out var foregroundColor, out var backgroundColor)) + if (settings.EnableColors && settings.Colors != null) { - Console.ForegroundColor = foregroundColor; - Console.BackgroundColor = backgroundColor; - } + var prevForeground = Console.ForegroundColor; + var prevBackground = Console.BackgroundColor; - Console.WriteLine(message); - Console.ForegroundColor = prevForeground; - Console.BackgroundColor = prevBackground; - } - else - { - Console.WriteLine(message); + if (TryGetLogColor(settings.Colors, logType, out var foregroundColor, out var backgroundColor)) + { + Console.ForegroundColor = foregroundColor; + Console.BackgroundColor = backgroundColor; + } + + Console.WriteLine(message); + Console.ForegroundColor = prevForeground; + Console.BackgroundColor = prevBackground; + } + else + { + Console.WriteLine(message); + } } } @@ -338,7 +245,9 @@ public static class LogHelper return false; } - var isEnabled = loggerSettings?.TypesToLog == null || loggerSettings.TypesToLog.Count == 0 || loggerSettings.TypesToLog.Contains(logType); + var typesToLog = loggerSettings?.TypesToLog; + var isEnabled = typesToLog == null || typesToLog.Count == 0 || typesToLog.Contains(logType); + if (!isEnabled) { OnLogLevelDisabled?.Invoke(null, new ErrorMessage { Message = $"Logtype '{logType}' is not enabled." }); @@ -351,39 +260,44 @@ public static class LogHelper public static void AddLogLevel(LoggerSettings settings, ELogType logType) { - if (logType == ELogType.NONE) + if (logType == ELogType.NONE || settings == null) { return; } - if (settings.TypesToLog == null || settings.TypesToLog.Count == 0) + lock (_settingsLock) { - settings.TypesToLog = new List(); - } + if (settings.TypesToLog == null) + { + settings.TypesToLog = new List(); + } - if (!settings.TypesToLog.Contains(logType)) - { - settings.TypesToLog.Add(logType); + if (!settings.TypesToLog.Contains(logType)) + { + settings.TypesToLog.Add(logType); + } } } public static void RemoveLogLevel(LoggerSettings settings, ELogType logType) { - if (logType == ELogType.NONE) + if (logType == ELogType.NONE || settings == null) { return; } - if (settings.TypesToLog == null || settings.TypesToLog.Count == 0) + lock (_settingsLock) { - settings.TypesToLog = new List(); - } + if (settings.TypesToLog == null) + { + settings.TypesToLog = new List(); + } - if (!settings.TypesToLog.Contains(logType)) - { - return; + if (settings.TypesToLog.Contains(logType)) + { + settings.TypesToLog.Remove(logType); + } } - settings.TypesToLog.Remove(logType); } private static void Log(ILogger logger, LogLevel logLevel, string message) @@ -403,12 +317,13 @@ public static class LogHelper return; } - if (settings.SplunkServers == null || settings.SplunkServers.Count() == 0) + var servers = settings.SplunkServers?.ToList(); + if (servers == null || servers.Count == 0) { return; } - var tasks = settings.SplunkServers? + var tasks = servers .Where(splunkServer => splunkServer.HasHecUrl && splunkServer.HasHecToken) .Select(async splunkServer => { @@ -422,7 +337,8 @@ public static class LogHelper return; } - var isEnabled = (splunkServer?.TypesToLog == null || splunkServer?.TypesToLog.Count == 0 || splunkServer.TypesToLog.Contains(currentLogType)); + var serverTypesToLog = splunkServer?.TypesToLog; + var isEnabled = (serverTypesToLog == null || serverTypesToLog.Count == 0 || serverTypesToLog.Contains(currentLogType)); if (!isEnabled) { splunkServer?.DisposeHttpClient(); @@ -431,7 +347,7 @@ public static class LogHelper } var response = await splunkServer?.SendAsync(splunkPayload, disableSplunkSSL); - if (!response.IsSuccessStatusCode) + if (response != null && !response.IsSuccessStatusCode) { LogError($"Failed to send log to Splunk '{splunkServer?.SplunkHecUrl}'. Status code: {response.StatusCode}"); } @@ -440,7 +356,7 @@ public static class LogHelper { LogError($"Error logging to Splunk '{splunkServer?.SplunkHecUrl}': {ex.Message}", ex); } - }) ?? new List(); + }); await Task.WhenAll(tasks); } @@ -455,7 +371,8 @@ public static class LogHelper return; } - if (settings.SplunkServers == null || settings.SplunkServers.Count() == 0) + var servers = settings.SplunkServers?.ToList(); + if (servers == null || servers.Count == 0) { return; } @@ -487,7 +404,8 @@ public static class LogHelper return; } - if (settings.GrayLogServers == null || settings.GrayLogServers.Count() == 0) + var servers = settings.GrayLogServers?.ToList(); + if (servers == null || servers.Count == 0) { return; } @@ -507,7 +425,7 @@ public static class LogHelper var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage)); - var tasks = settings.GrayLogServers? + var tasks = servers .Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0) .Select(async grayLogServer => { @@ -519,7 +437,8 @@ public static class LogHelper return; } - var isEnabled = (grayLogServer?.TypesToLog == null || grayLogServer.TypesToLog.Count == 0 || grayLogServer.TypesToLog.Contains(logType)); + var serverTypesToLog = grayLogServer?.TypesToLog; + var isEnabled = (serverTypesToLog == null || serverTypesToLog.Count == 0 || serverTypesToLog.Contains(logType)); if (!isEnabled) { grayLogServer?.DisposeUdp(); @@ -555,7 +474,7 @@ public static class LogHelper Message = $"Error logging to GrayLog Server '{grayLogServer.Hostname}': {ex.Message}" }); } - }) ?? new List(); + }); await Task.WhenAll(tasks); } @@ -599,12 +518,13 @@ public static class LogHelper return; } - if (settings.SysLogServers == null || settings.SysLogServers.Count() == 0) + var servers = settings.SysLogServers?.ToList(); + if (servers == null || servers.Count == 0) { return; } - var tasks = settings.SysLogServers? + var tasks = servers .Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0) .Select(async server => { @@ -616,27 +536,30 @@ public static class LogHelper return; } - var isEnabled = (server?.TypesToLog == null || server.TypesToLog.Count == 0 || server.TypesToLog.Contains(logType)); + var serverTypesToLog = server?.TypesToLog; + var isEnabled = (serverTypesToLog == null || serverTypesToLog.Count == 0 || serverTypesToLog.Contains(logType)); if (!isEnabled) { server?.DisposeUdp(); return; } + string serverMessage = message; + if (server.ConvertToRfc5424) { - message = ConvertToSyslogRfc5424(settings, logType, message, dateTime, category); + serverMessage = ConvertToSyslogRfc5424(settings, logType, serverMessage, dateTime, category); } else if (server.ConvertToRfc3164) { - message = ConvertToSyslogRfc3164(settings, logType, message, dateTime, category); + serverMessage = ConvertToSyslogRfc3164(settings, logType, serverMessage, dateTime, category); } if (!server.IsConnected) { server.SetUdp(); } - await server.WriteAsync(message); + await server.WriteAsync(serverMessage); } catch (Exception ex) { @@ -646,7 +569,7 @@ public static class LogHelper Message = $"Error logging to SysLog Server '{server.Hostname}': {ex.Message}" }); } - }) ?? new List(); + }); await Task.WhenAll(tasks); } @@ -658,12 +581,13 @@ public static class LogHelper return; } - if (settings.TcpServers == null || settings.TcpServers.Count() == 0) + var servers = settings.TcpServers?.ToList(); + if (servers == null || servers.Count == 0) { return; } - var tasks = settings.TcpServers? + var tasks = servers .Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0) .Select(async server => { @@ -675,7 +599,8 @@ public static class LogHelper return; } - var isEnabled = (server?.TypesToLog == null || server.TypesToLog.Count == 0 || server.TypesToLog.Contains(logType)); + var serverTypesToLog = server?.TypesToLog; + var isEnabled = (serverTypesToLog == null || serverTypesToLog.Count == 0 || serverTypesToLog.Contains(logType)); if (!isEnabled) { server?.DisposeTcp(); @@ -698,7 +623,7 @@ public static class LogHelper Message = $"Error logging to Tcp Server '{server.Hostname}': {ex.Message}" }); } - }) ?? new List(); + }); await Task.WhenAll(tasks); } @@ -710,12 +635,13 @@ public static class LogHelper return; } - if (settings.UdpServers == null || settings.UdpServers.Count() == 0) + var servers = settings.UdpServers?.ToList(); + if (servers == null || servers.Count == 0) { return; } - var tasks = settings.UdpServers? + var tasks = servers .Where(server => !string.IsNullOrWhiteSpace(server.Hostname) && server.Port >= 0) .Select(async server => { @@ -727,7 +653,8 @@ public static class LogHelper return; } - var isEnabled = (server?.TypesToLog == null || server.TypesToLog.Count == 0 || server.TypesToLog.Contains(logType)); + var serverTypesToLog = server?.TypesToLog; + var isEnabled = (serverTypesToLog == null || serverTypesToLog.Count == 0 || serverTypesToLog.Contains(logType)); if (!isEnabled) { server?.DisposeUdp(); @@ -750,10 +677,11 @@ public static class LogHelper Message = $"Error logging to Udp Server '{server.Hostname}': {ex.Message}" }); } - }) ?? new List(); + }); await Task.WhenAll(tasks); } + internal static string GetStartupMessage() { return $"{DllInfo.ApplicationName} started."; diff --git a/EonaCat.Logger/Managers/LogManager.cs b/EonaCat.Logger/Managers/LogManager.cs index ea43d2b..9ef12f7 100644 --- a/EonaCat.Logger/Managers/LogManager.cs +++ b/EonaCat.Logger/Managers/LogManager.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using EonaCat.Logger.EonaCatCoreLogger; +using EonaCat.Logger.EonaCatCoreLogger; using EonaCat.Logger.EonaCatCoreLogger.Extensions; +using EonaCat.Logger.EonaCatCoreLogger.Internal; using EonaCat.Logger.EonaCatCoreLogger.Models; using EonaCat.Logger.Extensions; using EonaCat.Logger.Servers.GrayLog; @@ -16,6 +10,13 @@ using EonaCat.Logger.Servers.Tcp; using EonaCat.Logger.Servers.Udp; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; // 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. @@ -171,6 +172,7 @@ namespace EonaCat.Logger.Managers .AddEonaCatFileLogger(config => { var options = Settings.FileLoggerOptions; + config.LoggerSettings = Settings; config.MaxWriteTries = options.MaxWriteTries; config.RetainedFileCountLimit = options.RetainedFileCountLimit; config.FlushPeriod = options.FlushPeriod; @@ -185,13 +187,13 @@ namespace EonaCat.Logger.Managers config.Mask = options.Mask; config.UseDefaultMasking = Settings.UseDefaultMasking; config.MaskedKeywords = options.MaskedKeywords; + config.Settings = Settings; })); - var serviceProvider = serviceCollection.BuildServiceProvider(); - LoggerProvider = serviceProvider.GetService(); - LoggerFactory = serviceProvider.GetService(); + var serviceProvider = serviceCollection.BuildServiceProvider(); + LoggerProvider = serviceProvider.GetService(); + LoggerFactory = serviceProvider.GetService(); Logger = LoggerFactory.CreateLogger(Settings.Id); - LogHelper.SendToFile(Logger, Settings, ELogType.INFO, LogHelper.GetStartupMessage()); } @@ -212,6 +214,7 @@ namespace EonaCat.Logger.Managers var messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime, _category); var writeToConsoleValue = writeToConsole.GetValueOrDefault(Settings.EnableConsole); + // Send message without header to the fileProvider LogHelper.SendToFile(Logger, Settings, logType, message); if (writeToConsoleValue) @@ -240,7 +243,7 @@ namespace EonaCat.Logger.Managers Settings.OnLogEvent(new EonaCatLogMessage { DateTime = dateTime, - Message = message, + Message = messageWithHeader, LogType = logType, Origin = string.IsNullOrEmpty(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin, Category = _category diff --git a/EonaCat.Logger/Managers/LoggerSettings.cs b/EonaCat.Logger/Managers/LoggerSettings.cs index 9eda9bf..ce95f87 100644 --- a/EonaCat.Logger/Managers/LoggerSettings.cs +++ b/EonaCat.Logger/Managers/LoggerSettings.cs @@ -24,7 +24,7 @@ public class LoggerSettings private FileLoggerOptions _fileLoggerOptions; - private string _headerFormat = "{ts} {tz} {host} {category} {thread} {sev}"; + private string _headerFormat = "{ts} {tz} {host} {category} {thread} {LogType}"; private string _timestampFormat = "yyyy-MM-dd HH:mm:ss"; /// @@ -51,8 +51,8 @@ public class LoggerSettings /// {host}: Hostname /// {category}: Category /// {thread}: Thread ID - /// {sev}: Severity - /// Default: {ts} {tz} {host} {category} {thread} {sev} + /// {LogType}: LogType + /// Default: {ts} {tz} {host} {category} {thread} {LogType} /// A space will be inserted between the header and the message. /// public string HeaderFormat diff --git a/Testers/EonaCat.Logger.Test.Web/Logger.cs b/Testers/EonaCat.Logger.Test.Web/Logger.cs index c113505..851ec7f 100644 --- a/Testers/EonaCat.Logger.Test.Web/Logger.cs +++ b/Testers/EonaCat.Logger.Test.Web/Logger.cs @@ -37,22 +37,6 @@ public class Logger UseLocalTime = UseLocalTime, }, }; - - LoggerSettings.CustomHeaderFormatter = ctx => - { - if (ctx.LogType == ELogType.ERROR) - { - return $"{ctx.Timestamp:HH:mm:ss} [{ctx.LogType}]"; - } - - return $"{ctx.Timestamp:HH:mm:ss} [{ctx.LogType}]"; - }; - - LoggerSettings.Toke - - _logManager = new LogManager(LoggerSettings); - _logManager.Settings.TypesToLog.Clear(); - _logManager.Settings.LogInfo(); } public void DeleteCurrentLogFile() @@ -139,9 +123,20 @@ public class Logger return; } + InitLogger(); await _logManager.WriteAsync(message, logType, writeToConsole).ConfigureAwait(false); } + private void InitLogger() + { + if (_logManager == null) + { + // Initialize LogManager + _logManager = new LogManager(LoggerSettings); + _logManager.Settings.TypesToLog.Clear(); + } + } + public async Task LogAsync(Exception exception, string message = "", bool writeToConsole = true) { if (IsDisabled) @@ -149,6 +144,7 @@ public class Logger return; } + InitLogger(); if (LoggerSettings.TypesToLog.Contains(ELogType.ERROR)) { await _logManager.WriteAsync(exception, message, writeToConsole: writeToConsole).ConfigureAwait(false); diff --git a/Testers/EonaCat.Logger.Test.Web/Program.cs b/Testers/EonaCat.Logger.Test.Web/Program.cs index 4f457ad..a077139 100644 --- a/Testers/EonaCat.Logger.Test.Web/Program.cs +++ b/Testers/EonaCat.Logger.Test.Web/Program.cs @@ -39,16 +39,16 @@ int onLogCounter = 0; var defaultColor = Console.ForegroundColor; - _ = Task.Run(() => - { - var logman = LogManager.Instance; - var i = 0; - while (true) - { - logman.WriteAsync($"Logman test {++i}", ELogType.TRACE).ConfigureAwait(false); - Task.Delay(10).ConfigureAwait(false); - } - }).ConfigureAwait(false); + //_ = Task.Run(() => + //{ + // var logman = LogManager.Instance; + // var i = 0; + // while (true) + // { + // logman.WriteAsync($"Logman test {++i}", ELogType.TRACE).ConfigureAwait(false); + // Task.Delay(10).ConfigureAwait(false); + // } + //}).ConfigureAwait(false); // Add services to the container. Logger logger = new Logger(); @@ -64,6 +64,20 @@ logger.LoggerSettings.OnLog += LoggerSettings_OnLog; logger.LoggerSettings.UseMask = true; + //LoggerSettings.CustomHeaderFormatter = ctx => + //{ + // if (ctx.LogType == ELogType.ERROR) + // { + // return $"{ctx.Timestamp:HH:mm:ss} [{ctx.LogType}]"; + // } + + // return $"{ctx.Timestamp:HH:mm:ss} [{ctx.LogType}]"; + //}; + + logger.LoggerSettings.CustomHeaderFormatter = null; // remove the lambda + logger.LoggerSettings.HeaderTokens.AddCustomToken("AppName", x => "[JIJ BENT EEN BRASSER!]"); + logger.LoggerSettings.HeaderFormat = "{AppName} {LogType} {ts}"; + // Configure the client var centralOptions = new LogCentralOptions {