From 6c48b43a20bdce9adf8c6fe78515d7b88e405cd0 Mon Sep 17 00:00:00 2001 From: EonaCat Date: Thu, 25 Apr 2024 22:05:17 +0200 Subject: [PATCH] Updated --- EonaCat.Logger/Constants.cs | 16 +- EonaCat.Logger/DllInfo.cs | 32 +- EonaCat.Logger/Enums.cs | 350 +++++++------- EonaCat.Logger/EonaCat.Logger.csproj | 4 +- .../Extensions/FileLoggerFactoryExtensions.cs | 84 ++-- .../EonaCatCoreLogger/FileLoggerOptions.cs | 175 ++++--- .../EonaCatCoreLogger/FileLoggerProvider.cs | 421 ++++++++-------- .../Internal/BatchingLogger.cs | 113 +++-- .../Internal/BatchingLoggerOptions.cs | 63 +-- .../Internal/BatchingLoggerProvider.cs | 266 +++++------ .../EonaCatCoreLogger/Internal/LogMessage.cs | 16 +- .../Models/EonaCatLogMessage.cs | 25 +- .../EonaCatLoggerAssertionException.cs | 9 +- .../Extensions/DateTimeExtensions.cs | 13 +- .../Extensions/ExceptionExtensions.cs | 123 ++--- EonaCat.Logger/Extensions/OffsetStream.cs | 417 +++++++--------- EonaCat.Logger/GrayLog/GrayLogServer.cs | 144 +++--- EonaCat.Logger/Helpers/ColorHelper.cs | 217 +++++---- EonaCat.Logger/Helpers/EnumHelper.cs | 86 ++-- EonaCat.Logger/Managers/ColorSchema.cs | 124 +++-- EonaCat.Logger/Managers/ILogManager.cs | 24 +- EonaCat.Logger/Managers/LogHelper.cs | 402 +++++++++------- EonaCat.Logger/Managers/LogManager.cs | 450 +++++++++--------- EonaCat.Logger/Managers/LoggerSettings.cs | 340 +++++++------ EonaCat.Logger/Splunk/Models/SplunkPayload.cs | 35 +- EonaCat.Logger/Splunk/SplunkServer.cs | 259 +++++----- EonaCat.Logger/Syslog/SyslogServer.cs | 145 +++--- .../EonaCat.Logger.Test.Console.csproj | 14 - .../EonaCat.Logger.Test.Console/Program.cs | 14 - Testers/EonaCat.Logger.Test.Web/Logger.cs | 198 ++++---- .../Pages/Error.cshtml | 2 +- .../Pages/Error.cshtml.cs | 33 +- .../Pages/Index.cshtml | 2 +- .../Pages/Index.cshtml.cs | 22 +- .../Pages/Privacy.cshtml | 2 +- .../Pages/Privacy.cshtml.cs | 24 +- .../Pages/Shared/_Layout.cshtml | 78 +-- .../Shared/_ValidationScriptsPartial.cshtml | 2 +- .../Pages/_ViewImports.cshtml | 2 +- .../Pages/_ViewStart.cshtml | 2 +- Testers/EonaCat.Logger.Test.Web/Program.cs | 43 +- 41 files changed, 2276 insertions(+), 2515 deletions(-) delete mode 100644 Testers/EonaCat.Logger.Test.Console/EonaCat.Logger.Test.Console.csproj delete mode 100644 Testers/EonaCat.Logger.Test.Console/Program.cs diff --git a/EonaCat.Logger/Constants.cs b/EonaCat.Logger/Constants.cs index e6f01b3..299afd8 100644 --- a/EonaCat.Logger/Constants.cs +++ b/EonaCat.Logger/Constants.cs @@ -1,13 +1,11 @@ -namespace EonaCat.Logger -{ - // 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; +// 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 static class Constants +public static class Constants +{ + public static class DateTimeFormats { - public static class DateTimeFormats - { - public static string LOGGING { get; } = "yyyy-MM-dd HH:mm:ss"; - } + public static string LOGGING { get; } = "yyyy-MM-dd HH:mm:ss"; } } \ No newline at end of file diff --git a/EonaCat.Logger/DllInfo.cs b/EonaCat.Logger/DllInfo.cs index dcd4b8a..b0a88a7 100644 --- a/EonaCat.Logger/DllInfo.cs +++ b/EonaCat.Logger/DllInfo.cs @@ -1,24 +1,22 @@ -namespace EonaCat.Logger +namespace EonaCat.Logger; +// 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 static class DllInfo { - // 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 const string NAME = "EonaCatLogger"; + public const string VERSION = "1.1.0"; - public static class DllInfo + static DllInfo() { - public const string NAME = "EonaCatLogger"; - public const string VERSION = "1.1.0"; - - static DllInfo() - { - bool isDebug = false; + var isDebug = false; #if DEBUG - isDebug = true; + isDebug = true; #endif - VersionName = isDebug ? "DEBUG" : "RELEASE"; - } - - internal static string VersionName { get; } - - public static string ApplicationName { get; internal set; } = "EonaCatLogger"; + VersionName = isDebug ? "DEBUG" : "RELEASE"; } + + internal static string VersionName { get; } + + public static string ApplicationName { get; internal set; } = "EonaCatLogger"; } \ No newline at end of file diff --git a/EonaCat.Logger/Enums.cs b/EonaCat.Logger/Enums.cs index 2c159ab..517b748 100644 --- a/EonaCat.Logger/Enums.cs +++ b/EonaCat.Logger/Enums.cs @@ -1,188 +1,192 @@ using Microsoft.Extensions.Logging; -namespace EonaCat.Logger +namespace EonaCat.Logger; +// 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 enum ELogType { - // 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. + NONE = 0, + INFO = 1, + WARNING = 2, + ERROR = 3, + TRAFFIC = 4, + DEBUG = 5, + CRITICAL = 6, + TRACE = 7 +} - public enum ELogType +public static class LogTypeConverter +{ + public static ELogType FromLogLevel(this LogLevel logLevel) { - NONE = 0, - INFO = 1, - WARNING = 2, - ERROR = 3, - TRAFFIC = 4, - DEBUG = 5, - CRITICAL = 6, - TRACE = 7 + switch (logLevel) + { + case LogLevel.None: + return ELogType.NONE; + case LogLevel.Error: + return ELogType.ERROR; + case LogLevel.Debug: + return ELogType.DEBUG; + case LogLevel.Critical: + return ELogType.CRITICAL; + case LogLevel.Warning: + return ELogType.WARNING; + case LogLevel.Trace: + return ELogType.TRACE; + case LogLevel.Information: + return ELogType.INFO; + default: + return ELogType.TRAFFIC; + } } - public static class LogTypeConverter + public static LogLevel ToLogLevel(this ELogType logLevel) { - public static ELogType FromLogLevel(this LogLevel logLevel) + switch (logLevel) { - switch (logLevel) - { - case LogLevel.None: - return ELogType.NONE; - case LogLevel.Error: - return ELogType.ERROR; - case LogLevel.Debug: - return ELogType.DEBUG; - case LogLevel.Critical: - return ELogType.CRITICAL; - case LogLevel.Warning: - return ELogType.WARNING; - case LogLevel.Trace: - return ELogType.TRACE; - case LogLevel.Information: - return ELogType.INFO; - default: - return ELogType.TRAFFIC; - } - } - - public static LogLevel ToLogLevel(this ELogType logLevel) - { - switch (logLevel) - { - case ELogType.NONE: - return LogLevel.None; - case ELogType.ERROR: - return LogLevel.Error; - case ELogType.DEBUG: - return LogLevel.Debug; - case ELogType.CRITICAL: - return LogLevel.Critical; - case ELogType.WARNING: - return LogLevel.Warning; - case ELogType.TRACE: - return LogLevel.Trace; - case ELogType.TRAFFIC: - return LogLevel.Trace; - case ELogType.INFO: - return LogLevel.Information; - default: - return LogLevel.Information; - } - } - - public static string ToString(this ELogType logLevel) - { - switch (logLevel) - { - case ELogType.NONE: - return "NONE"; - case ELogType.ERROR: - return "ERROR"; - case ELogType.DEBUG: - return "DEBUG"; - case ELogType.CRITICAL: - return "CRITICAL"; - case ELogType.WARNING: - return "WARNING"; - case ELogType.TRACE: - return "TRACE"; - case ELogType.TRAFFIC: - return "TRAFFIC"; - case ELogType.INFO: - return "INFO"; - default: - return "INFO"; - } - } - - public static ELogType FromSeverity(this ESeverity logLevel) - { - switch (logLevel) - { - case ESeverity.Debug: - return ELogType.DEBUG; - case ESeverity.Warn: - return ELogType.WARNING; - case ESeverity.Emergency: - return ELogType.TRACE; - case ESeverity.Critical: - return ELogType.CRITICAL; - case ESeverity.Alert: - return ELogType.TRAFFIC; - case ESeverity.Error: - return ELogType.ERROR; - default: - return ELogType.INFO; - } - } - - public static int ToGrayLogLevel(this ELogType logLevel) - { - // Loglevel to GELF format - switch (logLevel.ToString()) - { - case "TRAFFIC": return 7; - case "TRACE": return 7; - case "DEBUG": return 7; - case "INFO": return 6; - case "WARNING": return 4; - case "ERROR": return 3; - case "CRITICAL": return 2; - default: return 6; // Default to INFO - } - } - - public static ESeverity ToSeverity(this ELogType logLevel) - { - switch (logLevel) - { - case ELogType.DEBUG: - return ESeverity.Debug; - case ELogType.WARNING: - return ESeverity.Warn; - case ELogType.CRITICAL: - return ESeverity.Critical; - case ELogType.TRACE: - return ESeverity.Emergency; - case ELogType.ERROR: - return ESeverity.Error; - case ELogType.TRAFFIC: - return ESeverity.Alert; - default: - return ESeverity.Info; - } + case ELogType.NONE: + return LogLevel.None; + case ELogType.ERROR: + return LogLevel.Error; + case ELogType.DEBUG: + return LogLevel.Debug; + case ELogType.CRITICAL: + return LogLevel.Critical; + case ELogType.WARNING: + return LogLevel.Warning; + case ELogType.TRACE: + return LogLevel.Trace; + case ELogType.TRAFFIC: + return LogLevel.Trace; + case ELogType.INFO: + return LogLevel.Information; + default: + return LogLevel.Information; } } + public static string ToString(this ELogType logLevel) + { + switch (logLevel) + { + case ELogType.NONE: + return "NONE"; + case ELogType.ERROR: + return "ERROR"; + case ELogType.DEBUG: + return "DEBUG"; + case ELogType.CRITICAL: + return "CRITICAL"; + case ELogType.WARNING: + return "WARNING"; + case ELogType.TRACE: + return "TRACE"; + case ELogType.TRAFFIC: + return "TRAFFIC"; + case ELogType.INFO: + return "INFO"; + default: + return "INFO"; + } + } + + public static ELogType FromSeverity(this ESeverity logLevel) + { + switch (logLevel) + { + case ESeverity.Debug: + return ELogType.DEBUG; + case ESeverity.Warn: + return ELogType.WARNING; + case ESeverity.Emergency: + return ELogType.TRACE; + case ESeverity.Critical: + return ELogType.CRITICAL; + case ESeverity.Alert: + return ELogType.TRAFFIC; + case ESeverity.Error: + return ELogType.ERROR; + default: + return ELogType.INFO; + } + } + + public static int ToGrayLogLevel(this ELogType logLevel) + { + // Loglevel to GELF format + switch (logLevel.ToString()) + { + case "TRAFFIC": return 7; + case "TRACE": return 7; + case "DEBUG": return 7; + case "INFO": return 6; + case "WARNING": return 4; + case "ERROR": return 3; + case "CRITICAL": return 2; + default: return 6; // Default to INFO + } + } + + public static ESeverity ToSeverity(this ELogType logLevel) + { + switch (logLevel) + { + case ELogType.DEBUG: + return ESeverity.Debug; + case ELogType.WARNING: + return ESeverity.Warn; + case ELogType.CRITICAL: + return ESeverity.Critical; + case ELogType.TRACE: + return ESeverity.Emergency; + case ELogType.ERROR: + return ESeverity.Error; + case ELogType.TRAFFIC: + return ESeverity.Alert; + default: + return ESeverity.Info; + } + } +} + +/// +/// Message severity. +/// +public enum ESeverity +{ + /// + /// Debug messages. + /// + Debug = 0, + /// - /// Message severity. + /// Informational messages. /// - public enum ESeverity - { - /// - /// Debug messages. - /// - Debug = 0, - /// - /// Informational messages. - /// - Info = 1, - /// - /// Warning messages. - /// - Warn = 2, - /// - /// Error messages. - /// - Error = 3, - /// - /// Alert messages. - /// - Alert = 4, - /// - /// Critical messages. - /// - Critical = 5, - /// - /// Emergency messages. - /// - Emergency = 6 - } + Info = 1, + + /// + /// Warning messages. + /// + Warn = 2, + + /// + /// Error messages. + /// + Error = 3, + + /// + /// Alert messages. + /// + Alert = 4, + + /// + /// Critical messages. + /// + Critical = 5, + + /// + /// Emergency messages. + /// + Emergency = 6 } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCat.Logger.csproj b/EonaCat.Logger/EonaCat.Logger.csproj index 463f851..b25bbd8 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.5 + 1.2.5 EonaCat (Jeroen Saey) true EonaCat (Jeroen Saey) @@ -24,7 +24,7 @@ - 1.0.0.0+{chash:10}.{c:ymd} + 1.2.5+{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 baf295e..248281e 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Extensions/FileLoggerFactoryExtensions.cs @@ -2,40 +2,34 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace EonaCat.Logger.EonaCatCoreLogger.Extensions +namespace EonaCat.Logger.EonaCatCoreLogger.Extensions; +// 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. + +/// +/// Extensions for adding the to the +/// +public static class FileLoggerFactoryExtensions { - // 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 static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder) + { + builder.Services.AddSingleton(); + return builder; + } /// - /// Extensions for adding the to the + /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory. /// - public static class FileLoggerFactoryExtensions + /// The to use. + /// Sets the filename prefix to use for log files (optional) + /// the options for the fileLogger that needs to be used (optional) + public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null, + FileLoggerOptions fileLoggerOptions = null) { - private static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder) - { - builder.Services.AddSingleton(); - return builder; - } + if (fileLoggerOptions == null) fileLoggerOptions = new FileLoggerOptions(); - /// - /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory. - /// - /// The to use. - /// Sets the filename prefix to use for log files (optional) - /// the options for the fileLogger that needs to be used (optional) - public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, string filenamePrefix = null, FileLoggerOptions fileLoggerOptions = null) - { - if (fileLoggerOptions == null) - { - fileLoggerOptions = new FileLoggerOptions(); - } - - if (!string.IsNullOrWhiteSpace(filenamePrefix)) - { - fileLoggerOptions.FileNamePrefix = filenamePrefix; - } - builder.AddEonaCatFileLogger(options => + if (!string.IsNullOrWhiteSpace(filenamePrefix)) fileLoggerOptions.FileNamePrefix = filenamePrefix; + builder.AddEonaCatFileLogger(options => { options.FileNamePrefix = fileLoggerOptions.FileNamePrefix; options.FlushPeriod = fileLoggerOptions.FlushPeriod; @@ -49,26 +43,22 @@ namespace EonaCat.Logger.EonaCatCoreLogger.Extensions options.MaxRolloverFiles = fileLoggerOptions.MaxRolloverFiles; options.UseLocalTime = fileLoggerOptions.UseLocalTime; } + ); + return builder; + } - ); - return builder; - } + /// + /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory. + /// + /// The to use. + /// Configure an instance of the to set logging options + public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, + Action configure) + { + if (configure == null) throw new ArgumentNullException(nameof(configure)); + builder.AddEonaCatFileLogger(); + builder.Services.Configure(configure); - /// - /// Adds the EonaCat File Logger named 'EonaCatFileLogger' to the factory. - /// - /// The to use. - /// Configure an instance of the to set logging options - public static ILoggingBuilder AddEonaCatFileLogger(this ILoggingBuilder builder, Action configure) - { - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } - builder.AddEonaCatFileLogger(); - builder.Services.Configure(configure); - - return builder; - } + return builder; } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs index 862fb5b..c6844f6 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerOptions.cs @@ -2,108 +2,93 @@ using System.IO; using EonaCat.Logger.EonaCatCoreLogger.Internal; -namespace EonaCat.Logger.EonaCatCoreLogger +namespace EonaCat.Logger.EonaCatCoreLogger; +// 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. + +/// +/// Options for file logging. +/// +public class FileLoggerOptions : BatchingLoggerOptions { - // 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 int _fileSizeLimit = 200 * 1024 * 1024; + private int _maxRolloverFiles = 10; + private int _retainedFileCountLimit = 50; + + public static string DefaultPath => + AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory; /// - /// Options for file logging. + /// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit. + /// Once the log is full, no more messages will be appended. + /// Defaults to 200MB. /// - public class FileLoggerOptions : BatchingLoggerOptions + public int FileSizeLimit { - private int _fileSizeLimit = 200 * 1024 * 1024; - private int _retainedFileCountLimit = 50; - private int _maxRolloverFiles = 10; - private int _maxTries = 3; + get => _fileSizeLimit; - public static string DefaultPath => AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory; - - /// - /// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit. - /// Once the log is full, no more messages will be appended. - /// Defaults to 200MB. - /// - public int FileSizeLimit + set { - get => _fileSizeLimit; - - set - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive."); - } - _fileSizeLimit = value; - } + if (value <= 0) + throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive."); + _fileSizeLimit = value; } - - /// - /// Gets or sets a strictly positive value representing the maximum retained file count or null for no limit. - /// Defaults to 50. - /// - public int RetainedFileCountLimit - { - get => _retainedFileCountLimit; - - set - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(RetainedFileCountLimit)} must be positive."); - } - _retainedFileCountLimit = value; - } - } - - /// - /// Gets or sets the max times to try to write to the file. - /// Defaults 3. - /// - public int MaxWriteTries - { - get => _maxTries; - - set - { - _maxTries = value; - } - } - - /// - /// Determines if we need to use the local time in the logging or UTC (default:false) - /// - public bool UseLocalTime { get; set; } - - /// - /// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit. - /// Defaults to 10. - /// - public int MaxRolloverFiles - { - get => _maxRolloverFiles; - - set - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive."); - } - _maxRolloverFiles = value; - } - } - - /// - /// Gets or sets the filename prefix to use for log files. - /// Defaults to EonaCat_. - /// - public string FileNamePrefix { get; set; } = "EonaCat"; - - /// - /// The directory in which log files will be written, relative to the app process. - /// Default to executablePath\logs - /// - /// - public string LogDirectory { get; set; } = Path.Combine(DefaultPath, "logs"); } + + /// + /// Gets or sets a strictly positive value representing the maximum retained file count or null for no limit. + /// Defaults to 50. + /// + public int RetainedFileCountLimit + { + get => _retainedFileCountLimit; + + set + { + if (value <= 0) + throw new ArgumentOutOfRangeException(nameof(value), + $"{nameof(RetainedFileCountLimit)} must be positive."); + _retainedFileCountLimit = value; + } + } + + /// + /// Gets or sets the max times to try to write to the file. + /// Defaults 3. + /// + public int MaxWriteTries { get; set; } = 3; + + /// + /// Determines if we need to use the local time in the logging or UTC (default:false) + /// + public bool UseLocalTime { get; set; } + + /// + /// Gets or sets a strictly positive value representing the maximum retained file rollovers or null for no limit. + /// Defaults to 10. + /// + public int MaxRolloverFiles + { + get => _maxRolloverFiles; + + set + { + if (value <= 0) + throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(MaxRolloverFiles)} must be positive."); + _maxRolloverFiles = value; + } + } + + /// + /// Gets or sets the filename prefix to use for log files. + /// Defaults to EonaCat_. + /// + public string FileNamePrefix { get; set; } = "EonaCat"; + + /// + /// The directory in which log files will be written, relative to the app process. + /// Default to executablePath\logs + /// + /// + public string LogDirectory { get; set; } = Path.Combine(DefaultPath, "logs"); } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs index 5dc1794..cb60394 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/FileLoggerProvider.cs @@ -8,244 +8,207 @@ using EonaCat.Logger.EonaCatCoreLogger.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace EonaCat.Logger.EonaCatCoreLogger +namespace EonaCat.Logger.EonaCatCoreLogger; +// 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. + +/// +/// An that writes logs to a file +/// +[ProviderAlias("EonaCatFileLogger")] +public class FileLoggerProvider : BatchingLoggerProvider { - // 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 static readonly object WriteLock = new(); + private static readonly object RollOverLock = new(); + private readonly string _fileNamePrefix; + private readonly int _maxFileSize; + private readonly int _maxRetainedFiles; + private readonly int _maxRolloverFiles; + private readonly int _maxTries; + private readonly string _path; + private string _logFile; + private bool _rollingOver; + private int _rollOverCount; /// - /// An that writes logs to a file + /// Creates an instance of the /// - [ProviderAlias("EonaCatFileLogger")] - public class FileLoggerProvider : BatchingLoggerProvider + /// The options object controlling the logger + public FileLoggerProvider(IOptions options) : base(options) { - private readonly string _path; - private readonly string _fileNamePrefix; - private readonly int _maxFileSize; - private readonly int _maxRetainedFiles; - private readonly int _maxRolloverFiles; - private readonly int _maxTries; - private int _rollOverCount = 0; - private static readonly object WriteLock = new object(); - private static readonly object RollOverLock = new object(); - private string _logFile; - private bool _rollingOver; + var loggerOptions = options.Value; + _path = loggerOptions.LogDirectory; + _fileNamePrefix = loggerOptions.FileNamePrefix; + _maxFileSize = loggerOptions.FileSizeLimit; + _maxRetainedFiles = loggerOptions.RetainedFileCountLimit; + _maxRolloverFiles = loggerOptions.MaxRolloverFiles; + _maxTries = loggerOptions.MaxWriteTries; + } - /// - /// The file to which log messages should be appended. - /// - public string LogFile + /// + /// The file to which log messages should be appended. + /// + public string LogFile + { + get => _logFile; + set { - get + _logFile = value; + + if (!string.IsNullOrEmpty(_logFile)) { - return _logFile; - } - set - { - _logFile = value; - - if (!string.IsNullOrEmpty(_logFile)) - { - string dir = Path.GetDirectoryName(_logFile); - if (!string.IsNullOrEmpty(dir)) - { - if (!Directory.Exists(dir)) - { - Directory.CreateDirectory(dir); - } - } - } - } - } - - /// - /// Creates an instance of the - /// - /// The options object controlling the logger - public FileLoggerProvider(IOptions options) : base(options) - { - FileLoggerOptions loggerOptions = options.Value; - _path = loggerOptions.LogDirectory; - _fileNamePrefix = loggerOptions.FileNamePrefix; - _maxFileSize = loggerOptions.FileSizeLimit; - _maxRetainedFiles = loggerOptions.RetainedFileCountLimit; - _maxRolloverFiles = loggerOptions.MaxRolloverFiles; - _maxTries = loggerOptions.MaxWriteTries; - } - - /// - protected override async Task WriteMessagesAsync(IEnumerable messages, CancellationToken cancellationToken) - { - Directory.CreateDirectory(_path); - - foreach (IGrouping<(int Year, int Month, int Day), LogMessage> group in messages.GroupBy(GetGrouping)) - { - LogFile = GetFullName(group.Key); - FileInfo fileInfo = new FileInfo(LogFile); - if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize) - { - if (_maxRolloverFiles > 0 && _rollOverCount >= 0) - { - if (_rollOverCount < _maxRolloverFiles) - { - var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log"); - if (File.Exists(rollOverFile)) - { - File.Delete(rollOverFile); - } - fileInfo.CopyTo(rollOverFile); - File.WriteAllText(LogFile, string.Empty); - } - else - { - lock (RollOverLock) - { - _rollingOver = true; - MoveRolloverLogFiles(); - _rollingOver = false; - } - } - } - } - - while (_rollingOver) - { - await Task.Delay(100, cancellationToken).ConfigureAwait(false); - } - - lock (WriteLock) - { - int tries = 0; - bool completed = false; - - while (!completed) - { - try - { - System.IO.StreamWriter file = new System.IO.StreamWriter(LogFile, true); - foreach (LogMessage item in group) - { - file.Write(item.Message); - } - file.Close(); - completed = true; - } - catch (Exception exc) - { - tries++; - Task.Delay(100); - if (tries >= _maxTries) - throw; - } - } - } - - DeleteOldLogFiles(); - } - } - - private string GetFullName((int Year, int Month, int Day) group) - { - bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix); - if (hasPrefix) - { - return Path.Combine(_path, $"{_fileNamePrefix}_{group.Year:0000}{group.Month:00}{group.Day:00}.log"); - } - else - { - return Path.Combine(_path, $"{group.Year:0000}{group.Month:00}{group.Day:00}.log"); - } - } - - private (int Year, int Month, int Day) GetGrouping(LogMessage message) - { - return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day); - } - - private static void MoveFile(string copyFromPath, string copyToPath) - { - var origin = new FileInfo(copyFromPath); - origin.MoveTo(copyToPath); - - var destination = new FileInfo(copyToPath); - destination.CreationTime = origin.CreationTime; - destination.LastWriteTime = origin.LastWriteTime; - destination.LastAccessTime = origin.LastAccessTime; - } - - /// - /// Rollover logFiles - /// - protected void MoveRolloverLogFiles() - { - if (_maxRolloverFiles > 0 && _rollOverCount >= 0) - { - if (_rollOverCount >= _maxRolloverFiles) - { - var maxRollover = _rollOverCount; - bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix); - IEnumerable files; - if (hasPrefix) - { - files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*").OrderBy(x => x.CreationTime); - } - else - { - files = new DirectoryInfo(_path).GetFiles("*").OrderBy(x => x.CreationTime); - } - - for (int i = files.Count() -1; i >= 0; i--) - { - var currentFile = files.ElementAt(i); - if (i == 0) - { - // Temporary move first file - var newFilename2 = Path.GetFileName(currentFile.FullName).Replace($".log", $"_{i + 1}.log"); - MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}"); - continue; - } - - if (i == files.Count() - 1) - { - // Delete the last file - File.Delete(currentFile.FullName); - continue; - } - - var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log"); - MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}"); - } - _rollOverCount = 0; - } - } - } - - /// - /// Deletes old log files, keeping a number of files defined by - /// - protected void DeleteOldLogFiles() - { - if (_maxRetainedFiles > 0) - { - bool hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix); - 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 (FileInfo item in files) - { - item.Delete(); - } + var dir = Path.GetDirectoryName(_logFile); + if (!string.IsNullOrEmpty(dir)) + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); } } } + + /// + protected override async Task WriteMessagesAsync(IEnumerable messages, + CancellationToken cancellationToken) + { + Directory.CreateDirectory(_path); + + foreach (var group in messages.GroupBy(GetGrouping)) + { + LogFile = GetFullName(group.Key); + var fileInfo = new FileInfo(LogFile); + if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize) + if (_maxRolloverFiles > 0 && _rollOverCount >= 0) + { + if (_rollOverCount < _maxRolloverFiles) + { + var rollOverFile = LogFile.Replace(".log", $"_{++_rollOverCount}.log"); + if (File.Exists(rollOverFile)) File.Delete(rollOverFile); + fileInfo.CopyTo(rollOverFile); + File.WriteAllText(LogFile, string.Empty); + } + else + { + lock (RollOverLock) + { + _rollingOver = true; + MoveRolloverLogFiles(); + _rollingOver = false; + } + } + } + + while (_rollingOver) await Task.Delay(100, cancellationToken).ConfigureAwait(false); + + lock (WriteLock) + { + var tries = 0; + var completed = false; + + while (!completed) + try + { + var file = new StreamWriter(LogFile, true); + foreach (var item in group) file.Write(item.Message); + file.Close(); + completed = true; + } + catch (Exception exc) + { + tries++; + Task.Delay(100); + if (tries >= _maxTries) + throw; + } + } + + DeleteOldLogFiles(); + } + } + + private string GetFullName((int Year, int Month, int Day) group) + { + 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"); + } + + private (int Year, int Month, int Day) GetGrouping(LogMessage message) + { + return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day); + } + + private static void MoveFile(string copyFromPath, string copyToPath) + { + var origin = new FileInfo(copyFromPath); + origin.MoveTo(copyToPath); + + var destination = new FileInfo(copyToPath); + destination.CreationTime = origin.CreationTime; + destination.LastWriteTime = origin.LastWriteTime; + destination.LastAccessTime = origin.LastAccessTime; + } + + /// + /// Rollover logFiles + /// + protected void MoveRolloverLogFiles() + { + if (_maxRolloverFiles > 0 && _rollOverCount >= 0) + if (_rollOverCount >= _maxRolloverFiles) + { + var maxRollover = _rollOverCount; + var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix); + IEnumerable files; + if (hasPrefix) + files = new DirectoryInfo(_path).GetFiles(_fileNamePrefix + "*").OrderBy(x => x.CreationTime); + else + files = new DirectoryInfo(_path).GetFiles("*").OrderBy(x => x.CreationTime); + + for (var i = files.Count() - 1; i >= 0; i--) + { + var currentFile = files.ElementAt(i); + if (i == 0) + { + // Temporary move first file + var newFilename2 = Path.GetFileName(currentFile.FullName).Replace(".log", $"_{i + 1}.log"); + MoveFile(currentFile.FullName, + $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename2}"); + continue; + } + + if (i == files.Count() - 1) + { + // Delete the last file + File.Delete(currentFile.FullName); + continue; + } + + var newFilename = Path.GetFileName(currentFile.FullName).Replace($"_{i}.log", $"_{i + 1}.log"); + MoveFile(currentFile.FullName, $@"{Path.GetDirectoryName(currentFile.FullName)}\{newFilename}"); + } + + _rollOverCount = 0; + } + } + + /// + /// Deletes old log files, keeping a number of files defined by + /// + protected void DeleteOldLogFiles() + { + if (_maxRetainedFiles > 0) + { + var hasPrefix = !string.IsNullOrWhiteSpace(_fileNamePrefix); + 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(); + } + } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs index 184962d..ad660d3 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLogger.cs @@ -4,68 +4,67 @@ using EonaCat.Logger.Extensions; using EonaCat.Logger.Managers; using Microsoft.Extensions.Logging; -namespace EonaCat.Logger.EonaCatCoreLogger.Internal +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 { - // 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 string _category; + private readonly BatchingLoggerProvider _provider; + private LoggerSettings _loggerSettings; - public class BatchingLogger : ILogger + public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, LoggerSettings loggerSettings) { - private LoggerSettings _loggerSettings; - private readonly BatchingLoggerProvider _provider; - private readonly string _category; - private DateTimeOffset CurrentDateTimeOffset => _loggerSettings.UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow; - private DateTime CurrentDateTme => _loggerSettings.UseLocalTime ? DateTime.Now : DateTime.UtcNow; + _loggerSettings = loggerSettings; + _provider = loggerProvider; + _category = categoryName; + } - public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName, 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 { - _loggerSettings = loggerSettings; - _provider = loggerProvider; - _category = categoryName; - } + DateTime = timestamp.DateTime, + Message = message, + LogType = logLevel.FromLogLevel() + }; - public IDisposable BeginScope(TState state) - { - return null; - } - - public bool IsEnabled(LogLevel logLevel) - { - return logLevel != LogLevel.None; - } - - 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 - { - DateTime = timestamp.DateTime, - Message = message, - LogType = logLevel.FromLogLevel() - }; - - if (_loggerSettings == null) return; - currentMessage.Origin = string.IsNullOrWhiteSpace(_loggerSettings.LogOrigin) ? "BatchingLogger" : _loggerSettings.LogOrigin; - _loggerSettings?.OnLogEvent(currentMessage); - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - Log(CurrentDateTimeOffset, logLevel, eventId, state, exception, formatter); - } + currentMessage.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 e083b57..b0bfe1d 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerOptions.cs @@ -1,48 +1,35 @@ using System; -namespace EonaCat.Logger.EonaCatCoreLogger.Internal +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 BatchingLoggerOptions { - // 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 TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100); - public class BatchingLoggerOptions + /// + /// Gets or sets the period after which logs will be flushed to the store. + /// + public TimeSpan FlushPeriod { - private int _batchSize = 0; - private TimeSpan _flushPeriod = TimeSpan.FromMilliseconds(100); + get => _flushPeriod; - /// - /// Gets or sets the period after which logs will be flushed to the store. - /// - public TimeSpan FlushPeriod + set { - get => _flushPeriod; - - set - { - if (value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive."); - } - _flushPeriod = value; - } + if (value <= TimeSpan.Zero) + throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive."); + _flushPeriod = value; } - - /// - /// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit. - /// - public int BatchSize - { - get => _batchSize; - - set - { - _batchSize = value; - } - } - - /// - /// Gets or sets value indicating if logger accepts and queues writes. - /// - public bool IsEnabled { get; set; } } + + /// + /// Gets or sets a maximum number of events to include in a single batch or less than 1 for no limit. + /// + public int BatchSize { get; set; } = 0; + + /// + /// Gets or sets value indicating if logger accepts and queues writes. + /// + public bool IsEnabled { get; set; } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs index d97d144..1854e61 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/BatchingLoggerProvider.cs @@ -7,161 +7,151 @@ using EonaCat.Logger.Managers; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace EonaCat.Logger.EonaCatCoreLogger.Internal +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 abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable { - // 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 int _batchSize; - public abstract class BatchingLoggerProvider : ILoggerProvider, IDisposable + private readonly List _currentBatch = new(); + private CancellationTokenSource _cancellationTokenSource; + private LoggerSettings _loggerSettings; + + private ConcurrentQueue _messageQueue; + private Task _outputTask; + + protected BatchingLoggerProvider(IOptions options) { - protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow; - protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow; + var loggerOptions = options.Value; - private readonly List _currentBatch = new List(); - private readonly TimeSpan _interval; - private readonly int _batchSize; + if (loggerOptions.FlushPeriod <= TimeSpan.Zero) + throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), + $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero."); - private ConcurrentQueue _messageQueue; - private Task _outputTask; - private CancellationTokenSource _cancellationTokenSource; - private LoggerSettings _loggerSettings; + if (options.Value is FileLoggerOptions fileLoggerOptions) UseLocalTime = fileLoggerOptions.UseLocalTime; + _batchSize = loggerOptions.BatchSize; - protected bool UseLocalTime { get; set; } + Start(); + } - protected BatchingLoggerProvider(IOptions options) + protected DateTimeOffset CurrentDateTimeOffset => UseLocalTime ? DateTimeOffset.Now : DateTimeOffset.UtcNow; + protected DateTime CurrentDateTme => UseLocalTime ? DateTime.Now : DateTime.UtcNow; + + protected bool UseLocalTime { get; set; } + + protected LoggerSettings LoggerSettings + { + get { - BatchingLoggerOptions loggerOptions = options.Value; + if (_loggerSettings != null) return _loggerSettings; - 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; - } - - _interval = loggerOptions.FlushPeriod; - _batchSize = loggerOptions.BatchSize; - - Start(); + _loggerSettings = new LoggerSettings(); + _loggerSettings.UseLocalTime = UseLocalTime; + return _loggerSettings; } - protected LoggerSettings LoggerSettings - { - get - { - if (_loggerSettings != null) return _loggerSettings; + set => _loggerSettings = value; + } - _loggerSettings = new LoggerSettings(); - _loggerSettings.UseLocalTime = UseLocalTime; - return _loggerSettings; + public bool IsStarted { get; set; } + + public void Dispose() + { + while (!_messageQueue.IsEmpty) Task.Delay(10); + + StopAsync().GetAwaiter().GetResult(); + GC.SuppressFinalize(this); + } + + public ILogger CreateLogger(string categoryName) + { + return new BatchingLogger(this, categoryName, LoggerSettings); + } + + protected abstract Task WriteMessagesAsync(IEnumerable messages, CancellationToken token); + + private async Task ProcessLogQueueAsync(object state) + { + var startupMessage = $"{DllInfo.ApplicationName} started.{Environment.NewLine}"; + startupMessage = + LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, startupMessage, CurrentDateTme); + AddMessage(DateTimeOffset.Now, startupMessage); + + while (!_cancellationTokenSource.IsCancellationRequested) + { + var limit = _batchSize <= 0 ? int.MaxValue : _batchSize; + while (limit > 0 && _messageQueue.TryDequeue(out var message)) + { + _currentBatch.Add(message); + limit--; } - set => _loggerSettings = value; - } - - protected abstract Task WriteMessagesAsync(IEnumerable messages, CancellationToken token); - - private async Task ProcessLogQueueAsync(object state) - { - var startupMessage = $"{DllInfo.ApplicationName} started.{Environment.NewLine}"; - startupMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, startupMessage, CurrentDateTme); - - AddMessage(DateTimeOffset.Now, startupMessage); - - while (!_cancellationTokenSource.IsCancellationRequested) - { - int limit = _batchSize <= 0 ? int.MaxValue : _batchSize; - - while (limit > 0 && _messageQueue.TryDequeue(out LogMessage message)) + if (_currentBatch.Count > 0) + try { - _currentBatch.Add(message); - limit--; + await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false); + _currentBatch.Clear(); + } + catch + { + // ignored } - if (_currentBatch.Count > 0) - { - bool isBatchWritten = false; - try - { - await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token).ConfigureAwait(false); - isBatchWritten = true; - } - catch - { - // ignored - } - - if (isBatchWritten) - { - _currentBatch.Clear(); - } - } - - await IntervalAsync(_interval, _cancellationTokenSource.Token).ConfigureAwait(false); - } - await WriteMessagesAsync(new List { new LogMessage { Message = $"[{DllInfo.ApplicationName}] {DllInfo.ApplicationName} stopped.{Environment.NewLine}", Timestamp = CurrentDateTimeOffset } }, _cancellationTokenSource.Token).ConfigureAwait(false); - } - - protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken) - { - return Task.Delay(interval, cancellationToken); - } - - internal void AddMessage(DateTimeOffset timestamp, string message) - { - _messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp }); - } - - private void Start() - { - _messageQueue = new ConcurrentQueue(); - - _cancellationTokenSource = new CancellationTokenSource(); - _outputTask = Task.Factory.StartNew( - ProcessLogQueueAsync, - null, - TaskCreationOptions.LongRunning); - } - - private async Task StopAsync() - { - _cancellationTokenSource.Cancel(); - - try - { - await _outputTask.ConfigureAwait(false); - } - catch (TaskCanceledException) - { - } - catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 && exception.InnerExceptions[0] is TaskCanceledException) - { - } - } - - public void Dispose() - { - while (!_messageQueue.IsEmpty) - { - _messageQueue.TryDequeue(out _); - } - - StopAsync().GetAwaiter().GetResult(); - - GC.SuppressFinalize(this); - } - - ~BatchingLoggerProvider() - { - Dispose(); - } - - public ILogger CreateLogger(string categoryName) - { - return new BatchingLogger(this, categoryName, LoggerSettings); + Thread.Sleep(10); } } + + internal void AddMessage(DateTimeOffset timestamp, string message) + { + _messageQueue.Enqueue(new LogMessage { Message = message, Timestamp = timestamp }); + } + + private void Start() + { + IsStarted = true; + _messageQueue = new ConcurrentQueue(); + + _cancellationTokenSource = new CancellationTokenSource(); + _outputTask = Task.Factory.StartNew( + ProcessLogQueueAsync, + null, + TaskCreationOptions.LongRunning).ContinueWith(async x => + { + var stopMessage = $"{DllInfo.ApplicationName} stopped.{Environment.NewLine}"; + stopMessage = LogHelper.FormatMessageWithHeader(LoggerSettings, ELogType.INFO, stopMessage, CurrentDateTme); + await WriteMessagesAsync( + new List(new List + { new() { Message = stopMessage, Timestamp = CurrentDateTme } }), + _cancellationTokenSource.Token) + .ConfigureAwait(false); + }); + } + + private async Task StopAsync() + { + _cancellationTokenSource.Cancel(); + + try + { + while (_outputTask.Status != TaskStatus.RanToCompletion && _outputTask.Status != TaskStatus.Canceled) + { + await _outputTask.ConfigureAwait(false); + await Task.Delay(100); + } + } + catch (TaskCanceledException) + { + } + catch (AggregateException exception) when (exception.InnerExceptions.Count == 1 && + exception.InnerExceptions[0] is TaskCanceledException) + { + } + } + + ~BatchingLoggerProvider() + { + Dispose(); + } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/Internal/LogMessage.cs b/EonaCat.Logger/EonaCatCoreLogger/Internal/LogMessage.cs index 8100fac..fbf0f69 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Internal/LogMessage.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Internal/LogMessage.cs @@ -1,13 +1,11 @@ using System; -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. +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 struct LogMessage - { - public DateTimeOffset Timestamp { get; set; } - public string Message { get; set; } - } +public struct LogMessage +{ + public DateTimeOffset Timestamp { get; set; } + public string Message { get; set; } } \ No newline at end of file diff --git a/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs b/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs index dffe5e3..2176c13 100644 --- a/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs +++ b/EonaCat.Logger/EonaCatCoreLogger/Models/EonaCatLogMessage.cs @@ -1,21 +1,20 @@ using System; using EonaCat.Logger.Helpers; -namespace EonaCat.Logger.EonaCatCoreLogger.Models +namespace EonaCat.Logger.EonaCatCoreLogger.Models; +// 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 EonaCatLogMessage { - // 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 DateTime DateTime { get; set; } + public string Message { get; set; } + public ELogType LogType { get; set; } + public string Origin { get; set; } - public class EonaCatLogMessage + public override string ToString() { - public DateTime DateTime { get; set; } - public string Message { get; set; } - public ELogType LogType { get; set; } - public string Origin { get; set; } - - public override string ToString() - { - return $"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper.ToString(LogType)}] {Message}"; - } + return + $"[{DateTime.ToString(Constants.DateTimeFormats.LOGGING)}] [{EnumHelper.ToString(LogType)}] {Message}"; } } \ No newline at end of file diff --git a/EonaCat.Logger/Exceptions/EonaCatLoggerAssertionException.cs b/EonaCat.Logger/Exceptions/EonaCatLoggerAssertionException.cs index 810ec68..4117321 100644 --- a/EonaCat.Logger/Exceptions/EonaCatLoggerAssertionException.cs +++ b/EonaCat.Logger/Exceptions/EonaCatLoggerAssertionException.cs @@ -1,9 +1,10 @@ using System; -namespace EonaCat.Logger.Exceptions +namespace EonaCat.Logger.Exceptions; + +public class EonaCatLoggerAssertionException : Exception { - public class EonaCatLoggerAssertionException : Exception + public EonaCatLoggerAssertionException(string message) : base(message) { - public EonaCatLoggerAssertionException(string message) : base(message) { } } -} +} \ No newline at end of file diff --git a/EonaCat.Logger/Extensions/DateTimeExtensions.cs b/EonaCat.Logger/Extensions/DateTimeExtensions.cs index f23fcfe..0ffb519 100644 --- a/EonaCat.Logger/Extensions/DateTimeExtensions.cs +++ b/EonaCat.Logger/Extensions/DateTimeExtensions.cs @@ -1,12 +1,11 @@ using System; -namespace EonaCat.Logger.Extensions +namespace EonaCat.Logger.Extensions; + +public static class DateTimeExtensions { - public static class DateTimeExtensions + public static long ToUnixTimestamp(this DateTime dateTime) { - public static long ToUnixTimestamp(this DateTime dateTime) - { - return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; - } + return (long)(dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; } -} +} \ No newline at end of file diff --git a/EonaCat.Logger/Extensions/ExceptionExtensions.cs b/EonaCat.Logger/Extensions/ExceptionExtensions.cs index 2ba23d5..7fedc30 100644 --- a/EonaCat.Logger/Extensions/ExceptionExtensions.cs +++ b/EonaCat.Logger/Extensions/ExceptionExtensions.cs @@ -3,70 +3,73 @@ using System.Collections; using System.Diagnostics; using System.Text; -namespace EonaCat.Logger.Extensions +namespace EonaCat.Logger.Extensions; + +public static class ExceptionExtensions { - public static class ExceptionExtensions + public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null) { - public static string FormatExceptionToMessage(this Exception exception, string module = null, string method = null) - { - if (exception == null) - return string.Empty; + if (exception == null) + return string.Empty; - var st = new StackTrace(exception, true); - var frame = st.GetFrame(0); - int fileLine = frame.GetFileLineNumber(); - string filename = frame.GetFileName(); + var st = new StackTrace(exception, true); + var frame = st.GetFrame(0); + var fileLine = frame.GetFileLineNumber(); + var filename = frame.GetFileName(); - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); - 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) : "(none)"); - sb.Append(" Inner : ").AppendLine(exception.InnerException != null ? FormatInnerException(exception.InnerException) : "(null)"); - sb.Append(" Message : ").AppendLine(exception.Message); - sb.Append(" Source : ").AppendLine(exception.Source); - sb.Append(" StackTrace : ").AppendLine(exception.StackTrace); - sb.Append(" Line : ").AppendLine(fileLine.ToString()); - sb.Append(" File : ").AppendLine(filename); - sb.Append(" ToString : ").AppendLine(exception.ToString()); - sb.AppendLine("---"); + 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) + : "(none)"); + sb.Append(" Inner : ").AppendLine(exception.InnerException != null + ? FormatInnerException(exception.InnerException) + : "(null)"); + sb.Append(" Message : ").AppendLine(exception.Message); + sb.Append(" Source : ").AppendLine(exception.Source); + sb.Append(" StackTrace : ").AppendLine(exception.StackTrace); + sb.Append(" Line : ").AppendLine(fileLine.ToString()); + sb.Append(" File : ").AppendLine(filename); + sb.Append(" ToString : ").AppendLine(exception.ToString()); + sb.AppendLine("---"); - return sb.ToString(); - } - - private static string FormatExceptionData(IDictionary data) - { - StringBuilder sb = new StringBuilder(); - - foreach (DictionaryEntry entry in data) - { - sb.Append(" | ") - .Append(entry.Key) - .Append(": ") - .AppendLine(entry.Value.ToString()); - } - - return sb.ToString(); - } - - private static string FormatInnerException(Exception innerException) - { - StringBuilder sb = new StringBuilder(); - - sb.AppendLine(innerException.GetType().ToString()) - .AppendLine(" Message : " + innerException.Message) - .AppendLine(" Source : " + innerException.Source) - .AppendLine(" StackTrace : " + innerException.StackTrace) - .AppendLine(" ToString : " + innerException.ToString()) - .Append(" Data : ") - .AppendLine(innerException.Data != null && innerException.Data.Count > 0 ? FormatExceptionData(innerException.Data) : "(none)"); - - return sb.ToString(); - } + return sb.ToString(); } -} + + private static string FormatExceptionData(IDictionary data) + { + var sb = new StringBuilder(); + + foreach (DictionaryEntry entry in data) + sb.Append(" | ") + .Append(entry.Key) + .Append(": ") + .AppendLine(entry.Value.ToString()); + + return sb.ToString(); + } + + private static string FormatInnerException(Exception innerException) + { + var sb = new StringBuilder(); + + sb.AppendLine(innerException.GetType().ToString()) + .AppendLine(" Message : " + innerException.Message) + .AppendLine(" Source : " + innerException.Source) + .AppendLine(" StackTrace : " + innerException.StackTrace) + .AppendLine(" ToString : " + innerException) + .Append(" Data : ") + .AppendLine(innerException.Data != null && innerException.Data.Count > 0 + ? FormatExceptionData(innerException.Data) + : "(none)"); + + return sb.ToString(); + } +} \ No newline at end of file diff --git a/EonaCat.Logger/Extensions/OffsetStream.cs b/EonaCat.Logger/Extensions/OffsetStream.cs index 7a25aa1..688415f 100644 --- a/EonaCat.Logger/Extensions/OffsetStream.cs +++ b/EonaCat.Logger/Extensions/OffsetStream.cs @@ -1,261 +1,194 @@ using System; using System.IO; -namespace EonaCat.Logger.Extensions +namespace EonaCat.Logger.Extensions; +// 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 OffsetStream : Stream { - // 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 const int BufferSize = 4096; - public class OffsetStream : Stream + public OffsetStream(Stream stream, long offset = 0, long length = 0, bool readOnly = false, bool ownStream = false) { - private const int BufferSize = 4096; - - public OffsetStream(Stream stream, long offset = 0, long length = 0, bool readOnly = false, bool ownStream = false) + if (stream.CanSeek) { - 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 == 0) - { - Length1 = stream.Length - offset; - } - else - { - Length1 = length; - } - } - else - { - BaseStreamOffset = 0; - Length1 = length; - } - - BaseStream = stream; - ReadOnly = readOnly; - OwnStream = ownStream; - } - - protected override void Dispose(bool disposing) - { - if (Disposed) - { - return; - } - - if (disposing) - { - if (OwnStream & (BaseStream != null)) - { - BaseStream.Dispose(); - } - } - - Disposed = true; - - base.Dispose(disposing); - } - - public override bool CanRead => BaseStream.CanRead; - - public override bool CanSeek => BaseStream.CanSeek; - - public override bool CanWrite => BaseStream.CanWrite && !ReadOnly; - - public override long Length => Length1; - - public override long Position - { - get => Position1; - - set - { - if (value > Length1) - { - throw new EndOfStreamException(); - } - - if (!BaseStream.CanSeek) - { - throw new NotSupportedException("Cannot seek stream."); - } - - Position1 = value; - } - } - - public override void Flush() - { - 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 (Position1 >= Length1) - { - return 0; - } - - if (count > Length1 - Position1) - { - count = Convert.ToInt32(Length1 - Position1); - } - - if (BaseStream.CanSeek) - { - BaseStream.Position = BaseStreamOffset + Position1; - } - - int bytesRead = BaseStream.Read(buffer, offset, count); - Position1 += bytesRead; - - return bytesRead; - } - - public override long Seek(long offset, SeekOrigin origin) - { - if (!BaseStream.CanSeek) - { - throw new IOException("Stream is not seekable."); - } - - long pos; - - switch (origin) - { - case SeekOrigin.Begin: - pos = offset; - break; - - case SeekOrigin.Current: - pos = Position1 + offset; - break; - - case SeekOrigin.End: - pos = Length1 + offset; - break; - - default: - pos = 0; - break; - } - - if (pos < 0 || pos >= Length1) - { - throw new EndOfStreamException("OffsetStream reached begining/end of stream."); - } - - Position1 = pos; - - return pos; - } - - public override void SetLength(long value) - { - if (ReadOnly) - { - throw new IOException("OffsetStream is read only."); - } - - BaseStream.SetLength(BaseStreamOffset + value); - Length1 = value; - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (ReadOnly) - { - throw new IOException("OffsetStream is read only."); - } - - if (count < 1) - { - return; - } - - long pos = Position1 + count; - - if (pos > Length1) - { - throw new EndOfStreamException("OffsetStream reached end of stream."); - } - - if (BaseStream.CanSeek) - { - BaseStream.Position = BaseStreamOffset + Position1; - } - - BaseStream.Write(buffer, offset, count); - Position1 = pos; - } - - public long BaseStreamOffset { get; private set; } - - public Stream BaseStream { get; } - public long Length1 { get; set; } - public long Position1 { get; set; } - - public bool ReadOnly { get; } - - public bool Disposed { get; set; } - - public bool OwnStream { get; } - - public void Reset(long offset, long length, long position) - { BaseStreamOffset = offset; + + if (length > stream.Length - offset) throw new EndOfStreamException(); + + if (length == 0) + Length1 = stream.Length - offset; + else + Length1 = length; + } + else + { + BaseStreamOffset = 0; Length1 = length; - Position1 = position; } - public void WriteTo(Stream stream) + BaseStream = stream; + ReadOnly = readOnly; + OwnStream = ownStream; + } + + public override bool CanRead => BaseStream.CanRead; + + public override bool CanSeek => BaseStream.CanSeek; + + public override bool CanWrite => BaseStream.CanWrite && !ReadOnly; + + public override long Length => Length1; + + public override long Position + { + get => Position1; + + set { - WriteTo(stream, BufferSize); + if (value > Length1) throw new EndOfStreamException(); + + if (!BaseStream.CanSeek) throw new NotSupportedException("Cannot seek stream."); + + Position1 = value; + } + } + + public long BaseStreamOffset { get; private set; } + + public Stream BaseStream { get; } + public long Length1 { get; set; } + public long Position1 { get; set; } + + public bool ReadOnly { get; } + + public bool Disposed { get; set; } + + public bool OwnStream { get; } + + protected override void Dispose(bool disposing) + { + if (Disposed) return; + + if (disposing) + if (OwnStream & (BaseStream != null)) + BaseStream.Dispose(); + + Disposed = true; + + base.Dispose(disposing); + } + + public override void Flush() + { + 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 (Position1 >= Length1) return 0; + + if (count > Length1 - Position1) count = Convert.ToInt32(Length1 - Position1); + + if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1; + + var bytesRead = BaseStream.Read(buffer, offset, count); + Position1 += bytesRead; + + return bytesRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable."); + + long pos; + + switch (origin) + { + case SeekOrigin.Begin: + pos = offset; + break; + + case SeekOrigin.Current: + pos = Position1 + offset; + break; + + case SeekOrigin.End: + pos = Length1 + offset; + break; + + default: + pos = 0; + break; } - public void WriteTo(Stream stream, int bufferSize) + if (pos < 0 || pos >= Length1) throw new EndOfStreamException("OffsetStream reached begining/end of stream."); + + Position1 = pos; + + return pos; + } + + public override void SetLength(long value) + { + if (ReadOnly) throw new IOException("OffsetStream is read only."); + + BaseStream.SetLength(BaseStreamOffset + value); + Length1 = value; + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (ReadOnly) throw new IOException("OffsetStream is read only."); + + if (count < 1) return; + + var pos = Position1 + count; + + if (pos > Length1) throw new EndOfStreamException("OffsetStream reached end of stream."); + + if (BaseStream.CanSeek) BaseStream.Position = BaseStreamOffset + Position1; + + BaseStream.Write(buffer, offset, count); + Position1 = pos; + } + + public void Reset(long offset, long length, long position) + { + BaseStreamOffset = offset; + Length1 = length; + Position1 = position; + } + + public void WriteTo(Stream stream) + { + WriteTo(stream, BufferSize); + } + + public void WriteTo(Stream stream, int bufferSize) + { + if (!BaseStream.CanSeek) throw new IOException("Stream is not seekable."); + + if (Length1 < bufferSize) bufferSize = Convert.ToInt32(Length1); + + var previousPosition = Position1; + Position1 = 0; + + try { - if (!BaseStream.CanSeek) - { - throw new IOException("Stream is not seekable."); - } - - if (Length1 < bufferSize) - { - bufferSize = Convert.ToInt32(Length1); - } - - long previousPosition = Position1; - Position1 = 0; - - try - { - CopyTo(stream, bufferSize); - } - finally - { - Position1 = previousPosition; - } + CopyTo(stream, bufferSize); + } + finally + { + Position1 = previousPosition; } } } \ No newline at end of file diff --git a/EonaCat.Logger/GrayLog/GrayLogServer.cs b/EonaCat.Logger/GrayLog/GrayLogServer.cs index 6f63a74..64f1e9a 100644 --- a/EonaCat.Logger/GrayLog/GrayLogServer.cs +++ b/EonaCat.Logger/GrayLog/GrayLogServer.cs @@ -1,91 +1,77 @@ using System; using System.Net.Sockets; -namespace EonaCat.Logger.GrayLog +namespace EonaCat.Logger.GrayLog; +// 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. + +/// +/// Syslog server. +/// +public class GrayLogServer { - // 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. + internal readonly object SendLock = new(); + private string _Hostname = "127.0.0.1"; + private int _Port = 12201; + internal UdpClient Udp; /// - /// Syslog server. + /// Instantiate the object. /// - public class GrayLogServer + public GrayLogServer() { - /// - /// Hostname. - /// - public string Hostname + } + + /// + /// Instantiate the object. + /// + /// Hostname. + /// Port. + public GrayLogServer(string hostname = "127.0.0.1", int port = 12201) + { + Hostname = hostname; + Port = port; + } + + /// + /// Hostname. + /// + public string Hostname + { + get => _Hostname; + + set { - get - { - return _Hostname; - } + if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname)); + _Hostname = value; - set - { - if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname)); - _Hostname = value; - - SetUdp(); - } - } - - /// - /// UDP port. - /// - public int Port - { - get - { - return _Port; - } - set - { - if (value < 0) throw new ArgumentException("Port must be zero or greater."); - _Port = value; - - SetUdp(); - } - } - - /// - /// IP:port of the server. - /// - public string IpPort - { - get - { - return _Hostname + ":" + _Port; - } - } - - internal readonly object SendLock = new object(); - internal UdpClient Udp = null; - private string _Hostname = "127.0.0.1"; - private int _Port = 12201; - - /// - /// Instantiate the object. - /// - public GrayLogServer() - { - } - - /// - /// Instantiate the object. - /// - /// Hostname. - /// Port. - public GrayLogServer(string hostname = "127.0.0.1", int port = 12201) - { - Hostname = hostname; - Port = port; - } - - private void SetUdp() - { - Udp = null; - Udp = new UdpClient(_Hostname, _Port); + SetUdp(); } } -} + + /// + /// UDP port. + /// + public int Port + { + get => _Port; + set + { + if (value < 0) throw new ArgumentException("Port must be zero or greater."); + _Port = value; + + SetUdp(); + } + } + + /// + /// IP:port of the server. + /// + public string IpPort => _Hostname + ":" + _Port; + + private void SetUdp() + { + Udp = null; + Udp = new UdpClient(_Hostname, _Port); + } +} \ No newline at end of file diff --git a/EonaCat.Logger/Helpers/ColorHelper.cs b/EonaCat.Logger/Helpers/ColorHelper.cs index 63d5422..6fa13b7 100644 --- a/EonaCat.Logger/Helpers/ColorHelper.cs +++ b/EonaCat.Logger/Helpers/ColorHelper.cs @@ -2,134 +2,131 @@ using System.Drawing; using System.Globalization; -namespace EonaCat.Logger.Helpers +namespace EonaCat.Logger.Helpers; + +public static class ColorHelper { - public static class ColorHelper + public static string ColorToHexString(Color c) { - public static string ColorToHexString(Color c) + return "#" + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2"); + } + + public static string ColorToRGBString(Color c) + { + return "RGB(" + c.R + "," + c.G + "," + c.B + ")"; + } + + public static Color ConsoleColorToColor(this ConsoleColor consoleColor) + { + switch (consoleColor) { - return "#" + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2"); - } + case ConsoleColor.Black: + return Color.Black; - public static string ColorToRGBString(Color c) - { - return "RGB(" + c.R.ToString() + "," + c.G.ToString() + "," + c.B.ToString() + ")"; - } + case ConsoleColor.DarkBlue: + return HexStringToColor("#000080"); - public static Color ConsoleColorToColor(this ConsoleColor consoleColor) - { - switch (consoleColor) - { - case ConsoleColor.Black: - return Color.Black; + case ConsoleColor.DarkGreen: + return HexStringToColor("#008000"); - case ConsoleColor.DarkBlue: - return HexStringToColor("#000080"); + case ConsoleColor.DarkCyan: + return HexStringToColor("#008080"); - case ConsoleColor.DarkGreen: - return HexStringToColor("#008000"); + case ConsoleColor.DarkRed: + return HexStringToColor("#800000"); - case ConsoleColor.DarkCyan: - return HexStringToColor("#008080"); + case ConsoleColor.DarkMagenta: + return HexStringToColor("#800080"); - case ConsoleColor.DarkRed: - return HexStringToColor("#800000"); + case ConsoleColor.DarkYellow: + return HexStringToColor("#808000"); - case ConsoleColor.DarkMagenta: - return HexStringToColor("#800080"); + case ConsoleColor.Gray: + return HexStringToColor("#C0C0C0"); - case ConsoleColor.DarkYellow: - return HexStringToColor("#808000"); + case ConsoleColor.DarkGray: + return HexStringToColor("#808080"); - case ConsoleColor.Gray: - return HexStringToColor("#C0C0C0"); + case ConsoleColor.Blue: + return Color.Blue; - case ConsoleColor.DarkGray: - return HexStringToColor("#808080"); + case ConsoleColor.Green: + return Color.Lime; - case ConsoleColor.Blue: - return Color.Blue; + case ConsoleColor.Cyan: + return Color.Cyan; - case ConsoleColor.Green: - return Color.Lime; + case ConsoleColor.Red: + return Color.Red; - case ConsoleColor.Cyan: - return Color.Cyan; + case ConsoleColor.Magenta: + return Color.Magenta; - case ConsoleColor.Red: - return Color.Red; + case ConsoleColor.Yellow: + return Color.Yellow; - case ConsoleColor.Magenta: - return Color.Magenta; + case ConsoleColor.White: + return Color.White; - case ConsoleColor.Yellow: - return Color.Yellow; - - case ConsoleColor.White: - return Color.White; - - default: - throw new NotSupportedException(); - } - } - - public static Color HexStringToColor(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF) => Color.FromArgb(HexColorToArgb(htmlColor, requireHexSpecified, defaultAlpha)); - - public static int HexColorToArgb(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF) - { - 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('#'); - - - var symbolCount = htmlColor.Length; - var value = int.Parse(htmlColor, NumberStyles.HexNumber); - switch (symbolCount) - { - case 3: // RGB short hand - { - return defaultAlpha << 24 - | value & 0xF - | (value & 0xF) << 4 - | (value & 0xF0) << 4 - | (value & 0xF0) << 8 - | (value & 0xF00) << 8 - | (value & 0xF00) << 12 - ; - } - case 4: // RGBA short hand - { - // Inline alpha swap - return (value & 0xF) << 24 - | (value & 0xF) << 28 - | (value & 0xF0) >> 4 - | value & 0xF0 - | value & 0xF00 - | (value & 0xF00) << 4 - | (value & 0xF000) << 4 - | (value & 0xF000) << 8 - ; - } - case 6: // RGB complete definition - { - return defaultAlpha << 24 | value; - } - case 8: // RGBA complete definition - { - // Alpha swap - return (value & 0xFF) << 24 | value >> 8; - } - default: - throw new FormatException("Invalid HTML Color"); - } + default: + throw new NotSupportedException(); } } -} + + public static Color HexStringToColor(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF) + { + return Color.FromArgb(HexColorToArgb(htmlColor, requireHexSpecified, defaultAlpha)); + } + + public static int HexColorToArgb(string htmlColor, bool requireHexSpecified = false, int defaultAlpha = 0xFF) + { + 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('#'); + + + var symbolCount = htmlColor.Length; + var value = int.Parse(htmlColor, NumberStyles.HexNumber); + switch (symbolCount) + { + case 3: // RGB short hand + { + return (defaultAlpha << 24) + | (value & 0xF) + | ((value & 0xF) << 4) + | ((value & 0xF0) << 4) + | ((value & 0xF0) << 8) + | ((value & 0xF00) << 8) + | ((value & 0xF00) << 12) + ; + } + case 4: // RGBA short hand + { + // Inline alpha swap + return ((value & 0xF) << 24) + | ((value & 0xF) << 28) + | ((value & 0xF0) >> 4) + | (value & 0xF0) + | (value & 0xF00) + | ((value & 0xF00) << 4) + | ((value & 0xF000) << 4) + | ((value & 0xF000) << 8) + ; + } + case 6: // RGB complete definition + { + return (defaultAlpha << 24) | value; + } + case 8: // RGBA complete definition + { + // Alpha swap + return ((value & 0xFF) << 24) | (value >> 8); + } + default: + throw new FormatException("Invalid HTML Color"); + } + } +} \ No newline at end of file diff --git a/EonaCat.Logger/Helpers/EnumHelper.cs b/EonaCat.Logger/Helpers/EnumHelper.cs index 82b54f0..40fef7b 100644 --- a/EonaCat.Logger/Helpers/EnumHelper.cs +++ b/EonaCat.Logger/Helpers/EnumHelper.cs @@ -1,53 +1,51 @@ using System; using System.Collections.Generic; -namespace EonaCat.Logger.Helpers +namespace EonaCat.Logger.Helpers; +// 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. + +internal static class EnumHelper + where T : struct { - // 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. - - internal static class EnumHelper - where T : struct + static EnumHelper() { - static EnumHelper() + var names = Enum.GetNames(typeof(T)); + var values = (T[])Enum.GetValues(typeof(T)); + + Names = new Dictionary(names.Length); + Values = new Dictionary(names.Length * 2); + + for (var i = 0; i < names.Length; i++) { - string[] names = Enum.GetNames(typeof(T)); - T[] values = (T[])Enum.GetValues(typeof(T)); - - Names = new Dictionary(names.Length); - Values = new Dictionary(names.Length * 2); - - for (int i = 0; i < names.Length; i++) - { - Names[values[i]] = names[i]; - Values[names[i]] = values[i]; - Values[names[i].ToLower()] = values[i]; - } - } - - public static Dictionary Names { get; } - - public static Dictionary Values { get; } - - public static string ToString(T value) - { - return Names.TryGetValue(value, out string result) ? result : Convert.ToInt64(value).ToString(); - } - - public static bool TryParse(string input, bool ignoreCase, out T value) - { - if (string.IsNullOrEmpty(input)) - { - value = default; - return false; - } - - return Values.TryGetValue(ignoreCase ? input.ToLower() : input, out value); - } - - internal static T Parse(string input, bool ignoreCase, T defaultValue) - { - return TryParse(input, ignoreCase, out T result) ? result : defaultValue; + Names[values[i]] = names[i]; + Values[names[i]] = values[i]; + Values[names[i].ToLower()] = values[i]; } } + + public static Dictionary Names { get; } + + public static Dictionary Values { get; } + + public static string ToString(T value) + { + return Names.TryGetValue(value, out var result) ? result : Convert.ToInt64(value).ToString(); + } + + public static bool TryParse(string input, bool ignoreCase, out T value) + { + if (string.IsNullOrEmpty(input)) + { + value = default; + return false; + } + + return Values.TryGetValue(ignoreCase ? input.ToLower() : input, out value); + } + + internal static T Parse(string input, bool ignoreCase, T defaultValue) + { + return TryParse(input, ignoreCase, out var result) ? result : defaultValue; + } } \ No newline at end of file diff --git a/EonaCat.Logger/Managers/ColorSchema.cs b/EonaCat.Logger/Managers/ColorSchema.cs index 9e7b1b0..eb90d2e 100644 --- a/EonaCat.Logger/Managers/ColorSchema.cs +++ b/EonaCat.Logger/Managers/ColorSchema.cs @@ -1,75 +1,73 @@ using System; -namespace EonaCat.Logger.Managers +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. + +/// +/// Colors to use when writing to the console. +/// +public class ColorSchema { - // 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. + /// + /// The color to use for critical messages. + /// + public ColorScheme Critical = new(ConsoleColor.DarkRed, ConsoleColor.Black); /// - /// Colors to use when writing to the console. + /// The color to use for debug messages. /// - public class ColorSchema - { - /// - /// The color to use for debug messages. - /// - public ColorScheme Debug = new ColorScheme(ConsoleColor.Green, ConsoleColor.Black); - - /// - /// The color to use for informational messages. - /// - public ColorScheme Info = new ColorScheme(ConsoleColor.Blue, ConsoleColor.Black); - - /// - /// The color to use for warning messages. - /// - public ColorScheme Warning = new ColorScheme(ConsoleColor.DarkYellow, ConsoleColor.Black); - - /// - /// The color to use for error messages. - /// - public ColorScheme Error = new ColorScheme(ConsoleColor.Red, ConsoleColor.Black); - - /// - /// The color to use for alert messages. - /// - public ColorScheme Traffic = new ColorScheme(ConsoleColor.DarkMagenta, ConsoleColor.Black); - - /// - /// The color to use for critical messages. - /// - public ColorScheme Critical = new ColorScheme(ConsoleColor.DarkRed, ConsoleColor.Black); - - /// - /// The color to use for emergency messages. - /// - public ColorScheme Trace = new ColorScheme(ConsoleColor.Cyan, ConsoleColor.Black); - } + public ColorScheme Debug = new(ConsoleColor.Green, ConsoleColor.Black); /// - /// Color scheme for logging messages. + /// The color to use for error messages. /// - public class ColorScheme + public ColorScheme Error = new(ConsoleColor.Red, ConsoleColor.Black); + + /// + /// The color to use for informational messages. + /// + public ColorScheme Info = new(ConsoleColor.Blue, ConsoleColor.Black); + + /// + /// The color to use for emergency messages. + /// + public ColorScheme Trace = new(ConsoleColor.Cyan, ConsoleColor.Black); + + /// + /// The color to use for alert messages. + /// + public ColorScheme Traffic = new(ConsoleColor.DarkMagenta, ConsoleColor.Black); + + /// + /// The color to use for warning messages. + /// + public ColorScheme Warning = new(ConsoleColor.DarkYellow, ConsoleColor.Black); +} + +/// +/// Color scheme for logging messages. +/// +public class ColorScheme +{ + /// + /// Background color. + /// + public ConsoleColor Background = Console.BackgroundColor; + + /// + /// Foreground color. + /// + public ConsoleColor Foreground = Console.ForegroundColor; + + /// + /// Instantiates a new color scheme. + /// + /// Foreground color. + /// Background color. + public ColorScheme(ConsoleColor foreground, ConsoleColor background) { - /// - /// Foreground color. - /// - public ConsoleColor Foreground = Console.ForegroundColor; - - /// - /// Background color. - /// - public ConsoleColor Background = Console.BackgroundColor; - - /// - /// Instantiates a new color scheme. - /// - /// Foreground color. - /// Background color. - public ColorScheme(ConsoleColor foreground, ConsoleColor background) - { - Foreground = foreground; - Background = background; - } + Foreground = foreground; + Background = background; } } \ No newline at end of file diff --git a/EonaCat.Logger/Managers/ILogManager.cs b/EonaCat.Logger/Managers/ILogManager.cs index bff79b7..f9124f0 100644 --- a/EonaCat.Logger/Managers/ILogManager.cs +++ b/EonaCat.Logger/Managers/ILogManager.cs @@ -1,16 +1,18 @@ using System; -using System.Threading.Tasks; -namespace EonaCat.Logger.Managers +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 interface ILogManager { - // 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. + void Write(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"); - public interface ILogManager - { - void Write(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"); - - void Write(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"); - } + void Write(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"); } \ No newline at end of file diff --git a/EonaCat.Logger/Managers/LogHelper.cs b/EonaCat.Logger/Managers/LogHelper.cs index 6b191c2..50a209b 100644 --- a/EonaCat.Logger/Managers/LogHelper.cs +++ b/EonaCat.Logger/Managers/LogHelper.cs @@ -13,215 +13,251 @@ using Microsoft.Extensions.Logging; // 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 +namespace EonaCat.Logger.Managers; + +public class ErrorMessage { - public class ErrorMessage + public Exception Exception { get; set; } + public string Message { get; set; } +} + +internal static class LogHelper +{ + internal static event EventHandler OnException; + + internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage, + DateTime dateTime) { - public Exception Exception { get; set; } - public string Message { get; set; } + if (string.IsNullOrWhiteSpace(currentMessage)) + return currentMessage; + + var sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]"); + + sb.Replace("{ts}", + dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " + + (settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]")) + .Replace("{host}", $"[Host:{Dns.GetHostName()}]") + .Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]") + .Replace("{sev}", $"[{logType}]"); + + if (!settings?.RemoveMessagePrefix ?? (false && !currentMessage.Contains("[EonaCatLogger]"))) + sb.Insert(0, "[EonaCatLogger] "); + + sb.Append(" ").Append(currentMessage); + + return sb.ToString(); } - internal static class LogHelper + internal static void SendToConsole(LoggerSettings settings, ELogType logType, string message, bool writeToConsole) { - internal static event EventHandler OnException; + if (settings == null || !writeToConsole || string.IsNullOrWhiteSpace(message)) + return; - internal static string FormatMessageWithHeader(LoggerSettings settings, ELogType logType, string currentMessage, DateTime dateTime) + if (settings.EnableColors && settings.Colors != null) { - if (string.IsNullOrWhiteSpace(currentMessage)) - return currentMessage; + var prevForeground = Console.ForegroundColor; + var prevBackground = Console.BackgroundColor; - StringBuilder sb = new StringBuilder(settings?.HeaderFormat ?? "[EonaCatLogger]"); - - sb.Replace("{ts}", dateTime.ToString(settings?.TimestampFormat ?? "yyyy-MM-dd HH:mm:ss") + " " + (settings?.UseLocalTime ?? false ? "[LOCAL]" : "[UTC]")) - .Replace("{host}", $"[Host:{Dns.GetHostName()}]") - .Replace("{thread}", $"[ThreadId:{Environment.CurrentManagedThreadId}]") - .Replace("{sev}", $"[{logType}]"); - - if (!settings?.RemoveMessagePrefix ?? false && !currentMessage.Contains("[EonaCatLogger]")) - sb.Insert(0, "[EonaCatLogger] "); - - sb.Append(" ").Append(currentMessage); - - return sb.ToString(); - } - - 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) + ConsoleColor foregroundColor; + ConsoleColor backgroundColor; + switch (logType) { - ConsoleColor prevForeground = Console.ForegroundColor; - ConsoleColor prevBackground = Console.BackgroundColor; + case ELogType.DEBUG: + foregroundColor = settings.Colors.Debug.Foreground; + backgroundColor = settings.Colors.Debug.Background; + break; + case ELogType.INFO: + foregroundColor = settings.Colors.Info.Foreground; + backgroundColor = settings.Colors.Info.Background; + break; + case ELogType.WARNING: + foregroundColor = settings.Colors.Warning.Foreground; + backgroundColor = settings.Colors.Warning.Background; + break; + case ELogType.ERROR: + foregroundColor = settings.Colors.Error.Foreground; + backgroundColor = settings.Colors.Error.Background; + break; + case ELogType.TRAFFIC: + foregroundColor = settings.Colors.Traffic.Foreground; + backgroundColor = settings.Colors.Traffic.Background; + break; + case ELogType.CRITICAL: + foregroundColor = settings.Colors.Critical.Foreground; + backgroundColor = settings.Colors.Critical.Background; + break; + case ELogType.TRACE: + foregroundColor = settings.Colors.Trace.Foreground; + backgroundColor = settings.Colors.Trace.Background; + break; + default: + return; + } - ConsoleColor foregroundColor; - ConsoleColor backgroundColor; - switch (logType) + Console.ForegroundColor = foregroundColor; + Console.BackgroundColor = backgroundColor; + Console.WriteLine(message); + Console.ForegroundColor = prevForeground; + Console.BackgroundColor = prevBackground; + } + else + { + Console.WriteLine(message); + } + } + + internal static void SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message) + { + if (logger == null || settings == null || !settings.EnableFileLogging || + string.IsNullOrWhiteSpace(message)) return; + + var logLevel = logType.ToLogLevel(); + if (IsLogLevelEnabled(settings, logLevel)) Log(logger, logLevel, message); + } + + public static bool IsLogLevelEnabled(LoggerSettings settings, LogLevel logLevel) + { + return settings.MinLogType.ToLogLevel() <= logLevel; + } + + private static void Log(ILogger logger, LogLevel logLevel, string message) + { + logger.Log(logLevel, message); + } + + public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, + bool sendToSplunkServer) + { + if (settings == null || !sendToSplunkServer || splunkPayload == null) + return; + + if (settings.SplunkServers == null) settings.SplunkServers = new List(); + + foreach (var splunkServer in settings.SplunkServers) + { + if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken) + { + OnException?.Invoke(null, + new ErrorMessage + { + Message = + $"Splunk server HecUrl or HecToken not specified, skipping splunkServer '{splunkServer.SplunkHecUrl}'" + }); + continue; + } + + try + { + 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) + { + OnException?.Invoke(null, + new ErrorMessage + { + Exception = exception, + Message = + $"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}" + }); + } + } + } + + public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, + bool sendToSplunkServer) + { + if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message)) + return; + + var splunkPayload = new SplunkPayload + { + Host = Environment.MachineName, + EventData = message, + SourceType = logType + }; + + await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer); + } + + internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel, + 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 { - case ELogType.DEBUG: - foregroundColor = settings.Colors.Debug.Foreground; - backgroundColor = settings.Colors.Debug.Background; - break; - case ELogType.INFO: - foregroundColor = settings.Colors.Info.Foreground; - backgroundColor = settings.Colors.Info.Background; - break; - case ELogType.WARNING: - foregroundColor = settings.Colors.Warning.Foreground; - backgroundColor = settings.Colors.Warning.Background; - break; - case ELogType.ERROR: - foregroundColor = settings.Colors.Error.Foreground; - backgroundColor = settings.Colors.Error.Background; - break; - case ELogType.TRAFFIC: - foregroundColor = settings.Colors.Traffic.Foreground; - backgroundColor = settings.Colors.Traffic.Background; - break; - case ELogType.CRITICAL: - foregroundColor = settings.Colors.Critical.Foreground; - backgroundColor = settings.Colors.Critical.Background; - break; - case ELogType.TRACE: - foregroundColor = settings.Colors.Trace.Foreground; - backgroundColor = settings.Colors.Trace.Background; - break; - default: - return; - } + version, + host = Environment.MachineName, + short_message = message, + level = logLevel.ToGrayLogLevel(), + facility, + source, + timestamp = DateTime.UtcNow.ToUnixTimestamp() + }; - Console.ForegroundColor = foregroundColor; - Console.BackgroundColor = backgroundColor; - Console.WriteLine(message); - Console.ForegroundColor = prevForeground; - Console.BackgroundColor = prevBackground; + var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage)); + await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length, + new IPEndPoint(IPAddress.Parse(grayLogServer.Hostname), grayLogServer.Port)); } - else + catch (Exception exception) { - Console.WriteLine(message); + OnException?.Invoke(null, + new ErrorMessage + { + Exception = exception, + Message = + $"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}" + }); } - } + } - internal static async Task SendToFile(ILogger logger, LoggerSettings settings, ELogType logType, string message) - { - if (logger == null || settings == null || !settings.EnableFileLogging || string.IsNullOrWhiteSpace(message)) - return; + internal static async Task SendToSysLogServersAsync(LoggerSettings settings, string message, + bool sendToSyslogServers) + { + if (settings == null || !sendToSyslogServers || string.IsNullOrWhiteSpace(message)) + return; - LogLevel logLevel = logType.ToLogLevel(); - - if (logLevel >= settings.MaxLogType.ToLogLevel()) - await Task.Run(() => logger.Log(logLevel, message)); - } - - public static async Task SendToSplunkServersAsync(LoggerSettings settings, SplunkPayload splunkPayload, bool sendToSplunkServer) - { - if (settings == null || !sendToSplunkServer || splunkPayload == null) - return; - - if (settings.SplunkServers == null) + foreach (var server in settings.SysLogServers ?? new List { new("127.0.0.1") }) + try { - settings.SplunkServers = new List(); - } - - foreach (var splunkServer in settings.SplunkServers) - { - if (!splunkServer.HasHecUrl || !splunkServer.HasHecToken) + if (string.IsNullOrWhiteSpace(server.Hostname)) { - OnException?.Invoke(null, new ErrorMessage { Message = $"Splunk server HecUrl or HecToken not specified, skipping splunkServer '{splunkServer.SplunkHecUrl}'" }); + OnException?.Invoke(null, + new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" }); continue; } - try + if (server.Port < 0) { - var response = await splunkServer.SendAsync(splunkPayload); + OnException?.Invoke(null, + new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" }); + continue; + } - if (!response.IsSuccessStatusCode) - { - OnException?.Invoke(null, new ErrorMessage { Message = $"Failed to send log to Splunk '{splunkServer.SplunkHecUrl}'. Status code: {response.StatusCode}" }); - } - } - catch (Exception exception) - { - OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to Splunk Server '{splunkServer.SplunkHecUrl}': {exception.Message}" }); - } + var data = Encoding.UTF8.GetBytes(message); + await server.Udp.SendAsync(data, data.Length, + new IPEndPoint(IPAddress.Parse(server.Hostname), server.Port)); } - } - - public static async Task SendToSplunkServersAsync(LoggerSettings settings, string logType, string message, bool sendToSplunkServer) - { - if (settings == null || !sendToSplunkServer || string.IsNullOrWhiteSpace(message)) - return; - - var splunkPayload = new SplunkPayload + catch (Exception exception) { - Host = Environment.MachineName, - EventData = message, - SourceType = logType - }; - - await SendToSplunkServersAsync(settings, splunkPayload, sendToSplunkServer); - } - - internal static async Task SendToGrayLogServersAsync(LoggerSettings settings, string message, ELogType logLevel, 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 GrayLogServer("127.0.0.1", 12201) }) - { - try - { - var gelfMessage = new + OnException?.Invoke(null, + new ErrorMessage { - version, - host = Environment.MachineName, - short_message = message, - level = logLevel.ToGrayLogLevel(), - facility, - source, - timestamp = DateTime.UtcNow.ToUnixTimestamp(), - }; - - var messageBytes = Encoding.UTF8.GetBytes(JsonHelper.ToJson(gelfMessage)); - await grayLogServer.Udp.SendAsync(messageBytes, messageBytes.Length, new IPEndPoint(IPAddress.Parse(grayLogServer.Hostname), grayLogServer.Port)); - } - catch (Exception exception) - { - OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to GrayLog Server '{grayLogServer.Hostname}': {exception.Message}" }); - } + Exception = exception, + Message = $"Error while logging to SysLog Server '{server.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 SyslogServer("127.0.0.1", 514) }) - { - try - { - if (string.IsNullOrWhiteSpace(server.Hostname)) - { - OnException?.Invoke(null, new ErrorMessage { Message = "Server hostname not specified, skipping SysLog Server" }); - continue; - } - - if (server.Port < 0) - { - OnException?.Invoke(null, new ErrorMessage { Message = "Server port must be zero or greater, skipping SysLog Server" }); - continue; - } - - byte[] data = Encoding.UTF8.GetBytes(message); - await server.Udp.SendAsync(data, data.Length, new IPEndPoint(IPAddress.Parse(server.Hostname), server.Port)); - } - catch (Exception exception) - { - OnException?.Invoke(null, new ErrorMessage { Exception = exception, Message = $"Error while logging to SysLog Server '{server.Hostname}': {exception.Message}" }); - } - } - } } -} +} \ No newline at end of file diff --git a/EonaCat.Logger/Managers/LogManager.cs b/EonaCat.Logger/Managers/LogManager.cs index 20aa538..2d6d359 100644 --- a/EonaCat.Logger/Managers/LogManager.cs +++ b/EonaCat.Logger/Managers/LogManager.cs @@ -1,7 +1,4 @@ -using EonaCat.Logger.Syslog; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Threading; @@ -10,86 +7,139 @@ using EonaCat.Logger.EonaCatCoreLogger; using EonaCat.Logger.EonaCatCoreLogger.Extensions; using EonaCat.Logger.EonaCatCoreLogger.Models; using EonaCat.Logger.Extensions; +using EonaCat.Logger.Syslog; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; -namespace EonaCat.Logger.Managers +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 { - // 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 static LogManager _instance; - public partial class LogManager : ILogManager, IDisposable + private readonly CancellationTokenSource _tokenSource = new(); + private DateTime _logDate; + + public LogManager(LoggerSettings settings, string serverIp, int serverPort) { - public event EventHandler OnException; - private DateTime CurrentDateTme => Settings.UseLocalTime ? DateTime.Now : DateTime.UtcNow; - private DateTime _logDate; - 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; + if (string.IsNullOrEmpty(serverIp)) + throw new ArgumentNullException(nameof(serverIp)); - public bool IsRunning { get; private set; } - - private static LogManager _instance; + if (serverPort < 0) + throw new ArgumentException("Server port must be zero or greater."); - private readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); - - public static LogManager Instance => InstanceInit(); - - public LoggerSettings Settings { get; set; } = CreateDefaultSettings(); - - private static LogManager InstanceInit() + settings.SysLogServers = new List { - if (_instance == null) - { - _instance = new LogManager(CreateDefaultSettings()); - } - return _instance; - } + new(serverIp, serverPort) + }; - private static LoggerSettings CreateDefaultSettings() - { - var settings = new LoggerSettings(); - settings.Id = "EonaCatLogger"; - settings.MaxLogType = ELogType.TRACE; - return settings; - } + Settings = settings; + SetupLogManager(); + } - protected virtual async Task DisposeAsync(bool disposing) - { - if (disposing) - { - await StopLoggingAsync().ConfigureAwait(false); - } - } + public LogManager(LoggerSettings settings) + { + Settings = settings; + SetupFileLogger(settings); + SetupLogManager(); + LogHelper.OnException += LogHelper_OnException; + } - public async Task StartNewLogAsync() - { - if (_tokenSource.IsCancellationRequested) - { - return; - } + 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; } - if (IsRunning && CurrentDateTme.Date > _logDate.Date) - { - await StopLoggingAsync().ConfigureAwait(false); - } + public string CurrentLogFile => LoggerProvider is FileLoggerProvider fileLoggerProvider + ? fileLoggerProvider.LogFile + : string.Empty; - IsRunning = true; + public bool IsRunning { get; private set; } - CreateLogger(); + public static LogManager Instance => InstanceInit(); - Directory.CreateDirectory(Settings.FileLoggerOptions.LogDirectory); + public LoggerSettings Settings { get; set; } = CreateDefaultSettings(); - _logDate = CurrentDateTme; - } + public void Dispose() + { + DisposeAsync(true).GetAwaiter().GetResult(); + GC.SuppressFinalize(this); + } - private void CreateLogger() - { - // Dispose of previous ServiceProvider if it exists - (LoggerProvider as IDisposable)?.Dispose(); - (LoggerFactory as IDisposable)?.Dispose(); + public void Write(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; - IServiceCollection serviceCollection = new ServiceCollection(); - serviceCollection.AddLogging(builder => builder.SetMinimumLevel(Settings.MaxLogType.ToLogLevel()).AddEonaCatFileLogger(configuration => + Write(exception.FormatExceptionToMessage(module, method), + criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers, + sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, + grayLogVersion); + } + + public void Write(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; + + if (!IsRunning) StartNewLogAsync().ConfigureAwait(false); + InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, + customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion); + } + + 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.MinLogType = ELogType.INFO; + return settings; + } + + protected virtual async Task DisposeAsync(bool disposing) + { + if (disposing) await StopLoggingAsync().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.MinLogType.ToLogLevel()) + .AddEonaCatFileLogger(configuration => { var fileLoggerOptions = Settings.FileLoggerOptions; configuration.MaxWriteTries = fileLoggerOptions.MaxWriteTries; @@ -104,166 +154,108 @@ namespace EonaCat.Logger.Managers configuration.UseLocalTime = Settings.UseLocalTime; })); - var serviceProvider = serviceCollection.BuildServiceProvider(); - LoggerProvider = serviceProvider.GetService(); - LoggerFactory = serviceProvider.GetService(); - Logger = LoggerFactory.CreateLogger(Settings.Id); - - // Dispose of the current ServiceProvider - (serviceProvider as IDisposable)?.Dispose(); - } - - - 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; - } - - string messageWithHeader = LogHelper.FormatMessageWithHeader(Settings, logType, message, dateTime); - bool writeToConsoleValue = writeToConsole ?? Settings.EnableConsole; - bool sendToSyslogServersValue = sendToSyslogServers ?? Settings.SendToSyslogServers; - bool sendToSplunkServersValue = sendToSplunkServers ?? Settings.SendToSplunkServers; - bool sendToGrayLogServersValue = sendToGrayLogServers ?? Settings.SendToGrayLogServers; - - if (writeToConsoleValue) - { - LogHelper.SendToConsole(Settings, logType, messageWithHeader, true); - } - - if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue) - { - await Task.Run(async () => - { - if (sendToSyslogServersValue) - { - await LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true); - } - - if (sendToSplunkServersValue) - { - await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), messageWithHeader, true); - } - - if (sendToGrayLogServersValue) - { - await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, grayLogSource, true, grayLogVersion); - } - }); - } - - var logMessage = new EonaCatLogMessage - { - DateTime = dateTime, - Message = message, - LogType = logType, - Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin - }; - - Settings.OnLogEvent(logMessage); - } - - public void Reset() => Settings.ResetLogEvent(); - - public LogManager(LoggerSettings settings, string serverIp, int serverPort) - { - if (string.IsNullOrEmpty(serverIp)) - throw new ArgumentNullException(nameof(serverIp)); - - if (serverPort < 0) - throw new ArgumentException("Server port must be zero or greater."); - - settings.SysLogServers = new List - { - new SyslogServer(serverIp, serverPort) - }; - - Settings = settings; - SetupLogManager(); - } - - public LogManager(LoggerSettings settings) - { - Settings = settings; - SetupFileLogger(settings, null, true); - SetupLogManager(); - LogHelper.OnException += LogHelper_OnException; - } - - private void LogHelper_OnException(object sender, ErrorMessage e) - { - OnException?.Invoke(sender, e); - } - - private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true) - { - if (settings == null) - settings = Settings; - - if (!settings.EnableFileLogging) - return; - - if (logFolder != null) - settings.FileLoggerOptions.LogDirectory = logFolder; - - if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix)) - settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty; - } - - private void SetupLogManager() - { - AppDomain.CurrentDomain.ProcessExit += ProcessExit; - Console.CancelKeyPress += Console_CancelKeyPress; - _logDate = CurrentDateTme; - } - - private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) - { - Dispose(); - } - - private void ProcessExit(object sender, EventArgs e) - { - Dispose(); - } - - private async Task StopLoggingAsync() - { - IsRunning = false; - await InternalWriteAsync(CurrentDateTme, $"{DllInfo.ApplicationName} stopped.").ConfigureAwait(false); - return Task.Delay(500); - } - - public void DeleteCurrentLogFile() - { - if (CurrentLogFile != null) - File.Delete(CurrentLogFile); - } - - public void Dispose() - { - DisposeAsync(true).GetAwaiter().GetResult(); - GC.SuppressFinalize(this); - } - - public void Write(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; - - Write(exception.FormatExceptionToMessage(module, method), criticalException ? ELogType.CRITICAL : ELogType.ERROR, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion); - } - - public void Write(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; - - if (!IsRunning) - StartNewLogAsync().ConfigureAwait(false); - - InternalWriteAsync(CurrentDateTme, message, logType, writeToConsole, sendToSysLogServers, sendToSplunkServers, customSplunkSourceType, sendToGrayLogServers, grayLogFacility, grayLogSource, grayLogVersion); - } + var serviceProvider = serviceCollection.BuildServiceProvider(); + LoggerProvider = serviceProvider.GetService(); + LoggerFactory = serviceProvider.GetService(); + Logger = LoggerFactory.CreateLogger(Settings.Id); } -} + + + 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.MinLogType) return; + + 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; + + LogHelper.SendToFile(Logger, Settings, logType, message); + + if (writeToConsoleValue) LogHelper.SendToConsole(Settings, logType, messageWithHeader, true); + + if (sendToSyslogServersValue || sendToSplunkServersValue || sendToGrayLogServersValue) + await Task.Run(async () => + { + if (sendToSyslogServersValue) + await LogHelper.SendToSysLogServersAsync(Settings, messageWithHeader, true); + + if (sendToSplunkServersValue) + await LogHelper.SendToSplunkServersAsync(Settings, customSplunkSourceType ?? logType.ToString(), + messageWithHeader, true); + + if (sendToGrayLogServersValue) + await LogHelper.SendToGrayLogServersAsync(Settings, messageWithHeader, logType, grayLogFacility, + grayLogSource, true, grayLogVersion); + }); + + var logMessage = new EonaCatLogMessage + { + DateTime = dateTime, + Message = message, + LogType = logType, + Origin = string.IsNullOrWhiteSpace(Settings.LogOrigin) ? "LogManager" : Settings.LogOrigin + }; + + Settings.OnLogEvent(logMessage); + } + + public void Reset() + { + Settings.ResetLogEvent(); + } + + private void LogHelper_OnException(object sender, ErrorMessage e) + { + OnException?.Invoke(sender, e); + } + + private void SetupFileLogger(LoggerSettings settings = null, string logFolder = null, bool defaultPrefix = true) + { + if (settings == null) + settings = Settings; + + if (!settings.EnableFileLogging) + return; + + if (logFolder != null) + settings.FileLoggerOptions.LogDirectory = logFolder; + + if (string.IsNullOrWhiteSpace(settings.FileLoggerOptions.FileNamePrefix)) + settings.FileLoggerOptions.FileNamePrefix = defaultPrefix ? "EonaCat" : string.Empty; + } + + private void SetupLogManager() + { + AppDomain.CurrentDomain.ProcessExit += ProcessExit; + Console.CancelKeyPress += Console_CancelKeyPress; + _logDate = CurrentDateTme; + } + + private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) + { + Dispose(); + } + + private void ProcessExit(object sender, EventArgs e) + { + Dispose(); + } + + private Task StopLoggingAsync() + { + IsRunning = false; + return Task.CompletedTask; + } + + public void DeleteCurrentLogFile() + { + if (CurrentLogFile != null) + File.Delete(CurrentLogFile); + } +} \ No newline at end of file diff --git a/EonaCat.Logger/Managers/LoggerSettings.cs b/EonaCat.Logger/Managers/LoggerSettings.cs index 12e3ff7..5ffb40c 100644 --- a/EonaCat.Logger/Managers/LoggerSettings.cs +++ b/EonaCat.Logger/Managers/LoggerSettings.cs @@ -5,192 +5,172 @@ using EonaCat.Logger.EonaCatCoreLogger.Models; using EonaCat.Logger.GrayLog; using EonaCat.Logger.Syslog; -namespace EonaCat.Logger.Managers +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. + +/// +/// Logger settings. +/// +public class 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 delegate void LogDelegate(EonaCatLogMessage message); + + private ColorSchema _colors = new(); + private bool _enableConsole = true; + + private FileLoggerOptions _fileLoggerOptions; + + private string _headerFormat = "{ts} {host} {thread} {sev}"; + private int _maxMessageLength = 1024; + private string _timestampFormat = "yyyy-MM-dd HH:mm:ss"; /// - /// Logger settings. + /// Determines if we need to use the local time in the logging or UTC (default:false) /// - public class LoggerSettings + public bool UseLocalTime { get; set; } + + public string Id { get; set; } = "EonaCatLogger"; + + /// + /// Determines if we need to remove the prefix [EonaCatLogger] from each message (default: false) + /// + public bool RemoveMessagePrefix { get; set; } + + + /// + /// Header format. Provide a string that specifies how the preamble of each message should be structured. You can use + /// variables including: + /// {ts}: UTC timestamp + /// {host}: Hostname + /// {thread}: Thread ID + /// {sev}: Severity + /// Default: {ts} {host} {thread} {sev} + /// A space will be inserted between the header and the message. + /// + public string HeaderFormat { - public event LogDelegate OnLog; - public delegate void LogDelegate(EonaCatLogMessage message); - - /// - /// Determines if we need to use the local time in the logging or UTC (default:false) - /// - public bool UseLocalTime { get; set; } - - public string Id { get; set; } = "EonaCatLogger"; - - /// - /// Determines if we need to remove the prefix [EonaCatLogger] from each message (default: false) - /// - public bool RemoveMessagePrefix { get; set; } - - - /// - /// Header format. Provide a string that specifies how the preamble of each message should be structured. You can use variables including: - /// {ts}: UTC timestamp - /// {host}: Hostname - /// {thread}: Thread ID - /// {sev}: Severity - /// Default: {ts} {host} {thread} {sev} - /// A space will be inserted between the header and the message. - /// - public string HeaderFormat + get => _headerFormat; + set { - get - { - return _headerFormat; - } - set - { - if (string.IsNullOrEmpty(value)) _headerFormat = ""; - else _headerFormat = value; - } - } - - /// - /// Timestamp format. - /// - public string TimestampFormat - { - get - { - return _timestampFormat; - } - set - { - if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(HeaderFormat)); - _timestampFormat = value; - } - } - - /// - /// Enable or disable console logging. - /// Settings this to true will first validate if a console exists. - /// If a console is not available, it will be set to false. - /// - public bool EnableConsole - { - get - { - return _enableConsole; - } - set - { - if (value) _enableConsole = ConsoleExists(); - else _enableConsole = false; - } - } - - /// - /// Enable or disable use of color for console messages. - /// - public bool EnableColors { get; set; } = true; - - /// - /// Colors to use for console messages based on message severity. - /// - public ColorSchema Colors - { - get - { - return _colors; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(Colors)); - } - _colors = value; - } - } - - public ELogType MaxLogType { get; set; } = ELogType.TRACE; - - public bool SendToSyslogServers { get; set; } - - public List SysLogServers { get; set; } - - public bool SendToSplunkServers { get; set; } - - public bool SendToGrayLogServers { get; set; } - - public List SplunkServers { get; set; } - - public List GrayLogServers { get; set; } - - /// - /// Determines if the fileLogging is enabled - /// - public bool EnableFileLogging { get; set; } = true; - - private FileLoggerOptions _fileLoggerOptions; - - /// - /// FileLogger settings. - /// - public FileLoggerOptions FileLoggerOptions - { - get - { - if (_fileLoggerOptions == null) - { - _fileLoggerOptions = CreateDefaultFileLoggerOptions(); - } - return _fileLoggerOptions; - } - - set - { - _fileLoggerOptions = value; - } - } - - private static FileLoggerOptions CreateDefaultFileLoggerOptions() - { - return new FileLoggerOptions(); - } - - /// - /// Set the origin of where the OnLog event was initiated - /// - public string LogOrigin { get; set; } - - private string _headerFormat = "{ts} {host} {thread} {sev}"; - private string _timestampFormat = "yyyy-MM-dd HH:mm:ss"; - private bool _enableConsole = true; - private int _maxMessageLength = 1024; - private ColorSchema _colors = new ColorSchema(); - - - private static bool ConsoleExists() - { - try - { - bool test1 = Environment.UserInteractive; - bool test2 = Console.WindowHeight > 0; - return test1 && test2; - } - catch (Exception) - { - return false; - } - } - - internal void OnLogEvent(EonaCatLogMessage eonaCatLogMessage) - { - OnLog?.Invoke(eonaCatLogMessage); - } - - internal void ResetLogEvent() - { - OnLog = null; + if (string.IsNullOrEmpty(value)) _headerFormat = ""; + else _headerFormat = value; } } + + /// + /// Timestamp format. + /// + public string TimestampFormat + { + get => _timestampFormat; + set + { + if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(HeaderFormat)); + _timestampFormat = value; + } + } + + /// + /// Enable or disable console logging. + /// Settings this to true will first validate if a console exists. + /// If a console is not available, it will be set to false. + /// + public bool EnableConsole + { + get => _enableConsole; + set + { + if (value) _enableConsole = ConsoleExists(); + else _enableConsole = false; + } + } + + /// + /// Enable or disable use of color for console messages. + /// + public bool EnableColors { get; set; } = true; + + /// + /// Colors to use for console messages based on message severity. + /// + public ColorSchema Colors + { + get => _colors; + set + { + if (value == null) throw new ArgumentNullException(nameof(Colors)); + _colors = value; + } + } + + public ELogType MinLogType { get; set; } = ELogType.INFO; + + public bool SendToSyslogServers { get; set; } + + public List SysLogServers { get; set; } + + public bool SendToSplunkServers { get; set; } + + public bool SendToGrayLogServers { get; set; } + + public List SplunkServers { get; set; } + + public List GrayLogServers { get; set; } + + /// + /// Determines if the fileLogging is enabled + /// + public bool EnableFileLogging { get; set; } = true; + + /// + /// FileLogger settings. + /// + public FileLoggerOptions FileLoggerOptions + { + get + { + if (_fileLoggerOptions == null) _fileLoggerOptions = CreateDefaultFileLoggerOptions(); + return _fileLoggerOptions; + } + + set => _fileLoggerOptions = value; + } + + /// + /// Set the origin of where the OnLog event was initiated + /// + public string LogOrigin { get; set; } + + public event LogDelegate OnLog; + + private static FileLoggerOptions CreateDefaultFileLoggerOptions() + { + return new FileLoggerOptions(); + } + + + private static bool ConsoleExists() + { + try + { + var test1 = Environment.UserInteractive; + var test2 = Console.WindowHeight > 0; + return test1 && test2; + } + catch (Exception) + { + return false; + } + } + + internal void OnLogEvent(EonaCatLogMessage eonaCatLogMessage) + { + OnLog?.Invoke(eonaCatLogMessage); + } + + internal void ResetLogEvent() + { + OnLog = null; + } } \ No newline at end of file diff --git a/EonaCat.Logger/Splunk/Models/SplunkPayload.cs b/EonaCat.Logger/Splunk/Models/SplunkPayload.cs index ae82b85..73a2db9 100644 --- a/EonaCat.Logger/Splunk/Models/SplunkPayload.cs +++ b/EonaCat.Logger/Splunk/Models/SplunkPayload.cs @@ -1,24 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Text; +namespace EonaCat.Logger.Splunk.Models; -namespace EonaCat.Logger.Splunk.Models +public class SplunkPayload { - public class SplunkPayload - { - /// - /// Event data for splunk - /// - public string EventData { get; set; } + /// + /// Event data for splunk + /// + public string EventData { get; set; } - /// - /// SourceType for splunk - /// - public string SourceType { get; set; } + /// + /// SourceType for splunk + /// + public string SourceType { get; set; } - /// - /// Host for splunk - /// - public string Host { get; set; } - } -} + /// + /// Host for splunk + /// + public string Host { get; set; } +} \ No newline at end of file diff --git a/EonaCat.Logger/Splunk/SplunkServer.cs b/EonaCat.Logger/Splunk/SplunkServer.cs index 487d8a0..df6c75b 100644 --- a/EonaCat.Logger/Splunk/SplunkServer.cs +++ b/EonaCat.Logger/Splunk/SplunkServer.cs @@ -1,154 +1,129 @@ -using EonaCat.Json; -using EonaCat.Logger.Splunk.Models; -using System; +using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; +using EonaCat.Json; +using EonaCat.Logger.Splunk.Models; -namespace EonaCat.Logger.SplunkServer +namespace EonaCat.Logger.SplunkServer; +// 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. + +/// +/// Splunk Server. +/// +public class SplunkServer { - // 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. + internal readonly object SendLock = new(); + private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event"; + /// - /// Splunk Server. + /// Instantiate the object. /// - public class SplunkServer + /// splunkHecUrl. + /// splunkHecToken. + /// httpClientHandler. (optional) + public SplunkServer(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null) { - /// - /// SplunkHecUrl - /// - public string SplunkHecUrl + SplunkHecUrl = splunkHecUrl; + SplunkHecToken = splunkHecToken; + + if (httpClientHandler == null) httpClientHandler = new HttpClientHandler(); + + SplunkClientHandler = httpClientHandler; + CreateHttpClient(); + } + + /// + /// SplunkHecUrl + /// + public string SplunkHecUrl + { + get => _splunkHecUrl; + + set { - get - { - return _splunkHecUrl; - } - - set - { - if (!string.IsNullOrWhiteSpace(_splunkHecUrl) && !_splunkHecUrl.ToLower().Contains("http")) - { - value = $"http://{value}"; - } - _splunkHecUrl = value; - } - } - - public bool IsHttpsHecUrl => HasHecUrl && _splunkHecUrl.ToLower().StartsWith("https"); - - /// - /// SplunkHecToken - /// - public string SplunkHecToken - { - get - { - return _splunkHecToken; - } - set - { - _splunkHecToken = value; - } - } - - public HttpClientHandler SplunkClientHandler { get; internal set; } - public HttpClient HttpClient { get; private set; } - - internal readonly object SendLock = new object(); - private string _splunkHecUrl = "https://127.0.0.1:8088/services/collector/event"; - private string _splunkHecToken = "splunk-hec-token"; - - /// - /// Determines if a HEC token is available - /// - public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token"; - - /// - /// Determines if a HEC url is available - /// - public bool HasHecUrl => !string.IsNullOrWhiteSpace(SplunkHecUrl); - - /// - /// Determines if the splunk URL is local - /// - public bool IsLocalHost => HasHecUrl && (SplunkHecUrl.ToLower().Contains("127.0.0.1") || SplunkHecUrl.ToLower().Contains("localhost")); - - - /// - /// Instantiate the object. - /// - /// splunkHecUrl. - /// splunkHecToken. - /// httpClientHandler. (optional) - public SplunkServer(string splunkHecUrl, string splunkHecToken, HttpClientHandler httpClientHandler = null) - { - SplunkHecUrl = splunkHecUrl; - SplunkHecToken = splunkHecToken; - - if (httpClientHandler == null) - { - httpClientHandler = new HttpClientHandler(); - } - - SplunkClientHandler = httpClientHandler; - CreateHttpClient(); - } - - private void CreateHttpClient() - { - HttpClient = new HttpClient(SplunkClientHandler); - HttpClient.BaseAddress = new Uri(SplunkHecUrl); - - // Add the HEC token to the request headers for authorization - HttpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}"); - } - - /// - /// Disables SSL validation (can be used for insecure https urls) - /// This overwrites your own httpClientHandler - /// - public void DisableSSLValidation() - { - HttpClientHandler clientHandler = new HttpClientHandler() - { - ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true, - }; - - SplunkClientHandler = clientHandler; - CreateHttpClient(); - } - - public async Task SendAsync(SplunkPayload splunkPayload) - { - if (splunkPayload == null) - { - return null; - } - - // Create an event object with the required fields - var eventObject = new - { - @event = splunkPayload.EventData, - sourcetype = splunkPayload.SourceType, - host = splunkPayload.Host - }; - - // Serialize the event object to JSON - string eventJson = JsonHelper.ToJson(eventObject); - - if (!HasHecToken) - { - CreateHttpClient(); - } - - // Create an HTTP content with the event data - var content = new StringContent(eventJson, Encoding.UTF8, "application/json"); - - // Send the event to Splunk - HttpResponseMessage response = await HttpClient.PostAsync("/services/collector/event", content); - - return response; + if (!string.IsNullOrWhiteSpace(_splunkHecUrl) && !_splunkHecUrl.ToLower().Contains("http")) + value = $"http://{value}"; + _splunkHecUrl = value; } } -} + + public bool IsHttpsHecUrl => HasHecUrl && _splunkHecUrl.ToLower().StartsWith("https"); + + /// + /// SplunkHecToken + /// + public string SplunkHecToken { get; set; } = "splunk-hec-token"; + + public HttpClientHandler SplunkClientHandler { get; internal set; } + public HttpClient HttpClient { get; private set; } + + /// + /// Determines if a HEC token is available + /// + public bool HasHecToken => !string.IsNullOrWhiteSpace(SplunkHecToken) && SplunkHecToken != "splunk-hec-token"; + + /// + /// Determines if a HEC url is available + /// + public bool HasHecUrl => !string.IsNullOrWhiteSpace(SplunkHecUrl); + + /// + /// Determines if the splunk URL is local + /// + public bool IsLocalHost => HasHecUrl && + (SplunkHecUrl.ToLower().Contains("127.0.0.1") || + SplunkHecUrl.ToLower().Contains("localhost")); + + private void CreateHttpClient() + { + HttpClient = new HttpClient(SplunkClientHandler); + HttpClient.BaseAddress = new Uri(SplunkHecUrl); + + // Add the HEC token to the request headers for authorization + HttpClient.DefaultRequestHeaders.Add("Authorization", $"Splunk {SplunkHecToken}"); + } + + /// + /// Disables SSL validation (can be used for insecure https urls) + /// This overwrites your own httpClientHandler + /// + public void DisableSSLValidation() + { + var clientHandler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true + }; + + SplunkClientHandler = clientHandler; + CreateHttpClient(); + } + + public async Task SendAsync(SplunkPayload splunkPayload) + { + if (splunkPayload == null) return null; + + // Create an event object with the required fields + var eventObject = new + { + @event = splunkPayload.EventData, + sourcetype = splunkPayload.SourceType, + host = splunkPayload.Host + }; + + // Serialize the event object to JSON + var eventJson = JsonHelper.ToJson(eventObject); + + if (!HasHecToken) CreateHttpClient(); + + // Create an HTTP content with the event data + var content = new StringContent(eventJson, Encoding.UTF8, "application/json"); + + // Send the event to Splunk + var response = await HttpClient.PostAsync("/services/collector/event", content); + + return response; + } +} \ No newline at end of file diff --git a/EonaCat.Logger/Syslog/SyslogServer.cs b/EonaCat.Logger/Syslog/SyslogServer.cs index cf15696..383b898 100644 --- a/EonaCat.Logger/Syslog/SyslogServer.cs +++ b/EonaCat.Logger/Syslog/SyslogServer.cs @@ -1,92 +1,77 @@ using System; -using System.Collections.Generic; using System.Net.Sockets; -using System.Text; -namespace EonaCat.Logger.Syslog +namespace EonaCat.Logger.Syslog; +// 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. + +/// +/// Syslog server. +/// +public class SyslogServer { - // 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 string _Hostname = "127.0.0.1"; + private int _Port = 514; + + internal UdpClient Udp; /// - /// Syslog server. + /// Instantiate the object. /// - public class SyslogServer + public SyslogServer() { - /// - /// Hostname. - /// - public string Hostname + } + + /// + /// Instantiate the object. + /// + /// Hostname. + /// Port. + public SyslogServer(string hostname = "127.0.0.1", int port = 514) + { + Hostname = hostname; + Port = port; + } + + /// + /// Hostname. + /// + public string Hostname + { + get => _Hostname; + + set { - get - { - return _Hostname; - } - - set - { - if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname)); - _Hostname = value; + if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Hostname)); + _Hostname = value; - SetUdp(); - } - } - - /// - /// UDP port. - /// - public int Port - { - get - { - return _Port; - } - set - { - if (value < 0) throw new ArgumentException("Port must be zero or greater."); - _Port = value; - - SetUdp(); - } - } - - /// - /// IP:port of the server. - /// - public string IpPort - { - get - { - return _Hostname + ":" + _Port; - } - } - - internal UdpClient Udp = null; - private string _Hostname = "127.0.0.1"; - private int _Port = 514; - - /// - /// Instantiate the object. - /// - public SyslogServer() - { - } - - /// - /// Instantiate the object. - /// - /// Hostname. - /// Port. - public SyslogServer(string hostname = "127.0.0.1", int port = 514) - { - Hostname = hostname; - Port = port; - } - - private void SetUdp() - { - Udp = null; - Udp = new UdpClient(_Hostname, _Port); + SetUdp(); } } -} + + /// + /// UDP port. + /// + public int Port + { + get => _Port; + set + { + if (value < 0) throw new ArgumentException("Port must be zero or greater."); + _Port = value; + + SetUdp(); + } + } + + /// + /// IP:port of the server. + /// + public string IpPort => _Hostname + ":" + _Port; + + private void SetUdp() + { + Udp = null; + Udp = new UdpClient(_Hostname, _Port); + } +} \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Console/EonaCat.Logger.Test.Console.csproj b/Testers/EonaCat.Logger.Test.Console/EonaCat.Logger.Test.Console.csproj deleted file mode 100644 index f8a625c..0000000 --- a/Testers/EonaCat.Logger.Test.Console/EonaCat.Logger.Test.Console.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - Exe - net7.0 - enable - enable - - - - - - - diff --git a/Testers/EonaCat.Logger.Test.Console/Program.cs b/Testers/EonaCat.Logger.Test.Console/Program.cs deleted file mode 100644 index 0187bf7..0000000 --- a/Testers/EonaCat.Logger.Test.Console/Program.cs +++ /dev/null @@ -1,14 +0,0 @@ -using EonaCat.Logger; -using EonaCat.Logger.Managers; - -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); - -Console.WriteLine("Press a key to exit"); -Console.ReadKey(); \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Logger.cs b/Testers/EonaCat.Logger.Test.Web/Logger.cs index b8aef82..7c63b29 100644 --- a/Testers/EonaCat.Logger.Test.Web/Logger.cs +++ b/Testers/EonaCat.Logger.Test.Web/Logger.cs @@ -1,118 +1,120 @@ -using EonaCat.Logger.Managers; -using System.IO.Compression; +using System.IO.Compression; +using EonaCat.Logger.EonaCatCoreLogger; using EonaCat.Logger.Extensions; +using EonaCat.Logger.Managers; -namespace EonaCat.Logger.Test.Web +namespace EonaCat.Logger.Test.Web; + +public static class Logger { - public static class Logger + private static LogManager LogManager; + public static ELogType MinLogType { 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; } + + public static void DeleteCurrentLogFile() { - public static ELogType MaxLogType { get; set; } - public static bool UseLocalTime { get; set; } - private static LogManager LogManager; - public static string LogFolder => "Logs"; - public static string CurrentLogFile => LogManager.CurrentLogFile; - public static bool IsDisabled { get; set; } + if (IsDisabled) + return; - public static void DeleteCurrentLogFile() + LogManager.DeleteCurrentLogFile(); + } + + private static string ConvertToAbsolutePath(string path) + { + return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path); + } + + public static async Task DownloadLogAsync(HttpContext context, string logName, long limit) + { + var logFileName = logName + ".log"; + + await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open, + FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true); + var response = context.Response; + + response.ContentType = "text/plain"; + 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; + Stream stream; + + string acceptEncoding = request.Headers["Accept-Encoding"]; + if (string.IsNullOrEmpty(acceptEncoding)) { - if (IsDisabled) - return; - - LogManager.DeleteCurrentLogFile(); + stream = response.Body; } - - private static string ConvertToAbsolutePath(string path) + else { - return Path.IsPathRooted(path) ? path : Path.Combine(LogFolder, path); - } + var acceptEncodingParts = acceptEncoding.Split(new[] { ',' }, + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - public static async Task DownloadLogAsync(HttpContext context, string logName, long limit) - { - var logFileName = logName + ".log"; - - await using var fS = new FileStream(Path.Combine(ConvertToAbsolutePath(LogFolder), logFileName), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 64 * 1024, true); - var response = context.Response; - - response.ContentType = "text/plain"; - 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; - Stream stream; - - string acceptEncoding = request.Headers["Accept-Encoding"]; - if (string.IsNullOrEmpty(acceptEncoding)) + if (acceptEncodingParts.Contains("br")) { - stream = response.Body; + response.Headers.ContentEncoding = "br"; + stream = new BrotliStream(response.Body, CompressionMode.Compress); + } + else if (acceptEncodingParts.Contains("gzip")) + { + response.Headers.ContentEncoding = "gzip"; + stream = new GZipStream(response.Body, CompressionMode.Compress); + } + else if (acceptEncodingParts.Contains("deflate")) + { + response.Headers.ContentEncoding = "deflate"; + stream = new DeflateStream(response.Body, CompressionMode.Compress); } else { - string[] acceptEncodingParts = acceptEncoding.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); - - if (acceptEncodingParts.Contains("br")) - { - response.Headers.ContentEncoding = "br"; - stream = new BrotliStream(response.Body, CompressionMode.Compress); - } - else if (acceptEncodingParts.Contains("gzip")) - { - response.Headers.ContentEncoding = "gzip"; - stream = new GZipStream(response.Body, CompressionMode.Compress); - } - else if (acceptEncodingParts.Contains("deflate")) - { - response.Headers.ContentEncoding = "deflate"; - stream = new DeflateStream(response.Body, CompressionMode.Compress); - } - else - { - stream = response.Body; - } - } - - await using (stream) - { - await oFS.CopyToAsync(stream).ConfigureAwait(false); - - if (fS.Length > limit) - await stream.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false); + stream = response.Body; } } - public static void Log(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true) + await using (stream) { - if (IsDisabled) - return; + await oFS.CopyToAsync(stream).ConfigureAwait(false); - LogManager.Write(message, logType, writeToConsole); - } - - public static void Log(Exception exception, string message = "", bool writeToConsole = true) - { - if (IsDisabled) - return; - - LogManager.Write(exception, module: message, writeToConsole: writeToConsole); - } - - public static void Configure() - { - var loggerSettings = new LoggerSettings - { - Id = "EonaCatDnsLogger", - MaxLogType = ELogType.TRACE, - UseLocalTime = UseLocalTime, - FileLoggerOptions = - { - LogDirectory = "Logs", - FileSizeLimit = 20_000_000, // 20 MB, - UseLocalTime = UseLocalTime, - } - }; - LogManager = new LogManager(loggerSettings); + if (fS.Length > limit) + await stream.WriteAsync("\r\n####__EONACATLOGGER_TRUNCATED___####"u8.ToArray()).ConfigureAwait(false); } } -} + + public static void Log(string message, ELogType logType = ELogType.INFO, bool writeToConsole = true) + { + if (IsDisabled) + return; + + LogManager.Write(message, logType, writeToConsole); + } + + public static void Log(Exception exception, string message = "", bool writeToConsole = true) + { + if (IsDisabled) + return; + + LogManager.Write(exception, message, writeToConsole: writeToConsole); + } + + public static void Configure() + { + var loggerSettings = new LoggerSettings + { + Id = "EonaCatTestLogger", + MinLogType = 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/Pages/Error.cshtml b/Testers/EonaCat.Logger.Test.Web/Pages/Error.cshtml index 6f92b95..b5105b6 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Error.cshtml +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Error.cshtml @@ -23,4 +23,4 @@ It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app. -

+

\ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/Error.cshtml.cs b/Testers/EonaCat.Logger.Test.Web/Pages/Error.cshtml.cs index 01ee631..912801e 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Error.cshtml.cs +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Error.cshtml.cs @@ -1,27 +1,26 @@ +using System.Diagnostics; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using System.Diagnostics; -namespace EonaCat.Logger.Web.Pages +namespace EonaCat.Logger.Web.Pages; + +[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] +[IgnoreAntiforgeryToken] +public class ErrorModel : PageModel { - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - [IgnoreAntiforgeryToken] - public class ErrorModel : PageModel + private readonly ILogger _logger; + + public ErrorModel(ILogger logger) { - public string? RequestId { get; set; } + _logger = logger; + } - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + public string? RequestId { get; set; } - private readonly ILogger _logger; + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - public ErrorModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } + public void OnGet() + { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; } } \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml b/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml index b5f0c15..fd28464 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml @@ -7,4 +7,4 @@

Welcome

Learn about building Web apps with ASP.NET Core.

-
+ \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml.cs b/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml.cs index d23a77d..d1dbd9d 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml.cs +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Index.cshtml.cs @@ -1,19 +1,17 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -namespace EonaCat.Logger.Web.Pages +namespace EonaCat.Logger.Web.Pages; + +public class IndexModel : PageModel { - public class IndexModel : PageModel + private readonly ILogger _logger; + + public IndexModel(ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + } - public IndexModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - - } + public void OnGet() + { } } \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml b/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml index 46ba966..a92998a 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml @@ -5,4 +5,4 @@ }

@ViewData["Title"]

-

Use this page to detail your site's privacy policy.

+

Use this page to detail your site's privacy policy.

\ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml.cs b/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml.cs index 8b979e9..34cc43e 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml.cs +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Privacy.cshtml.cs @@ -1,19 +1,17 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.RazorPages; -namespace EonaCat.Logger.Web.Pages +namespace EonaCat.Logger.Web.Pages; + +public class PrivacyModel : PageModel { - public class PrivacyModel : PageModel + private readonly ILogger _logger; + + public PrivacyModel(ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + } - public PrivacyModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - } + public void OnGet() + { } } \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_Layout.cshtml b/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_Layout.cshtml index ee9d85b..80fcec8 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_Layout.cshtml +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_Layout.cshtml @@ -1,51 +1,51 @@  - - + + @ViewData["Title"] - EonaCat.Logger.Web - - - + + + -
- -
-
-
- @RenderBody() -
-
- - + + +
+
+ @RenderBody() +
+
- - - +
+
+ © 2022 - EonaCat.Logger.Web - Privacy +
+
- @await RenderSectionAsync("Scripts", required: false) + + + + +@await RenderSectionAsync("Scripts", false) \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_ValidationScriptsPartial.cshtml b/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_ValidationScriptsPartial.cshtml index 5a16d80..660f00c 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_ValidationScriptsPartial.cshtml +++ b/Testers/EonaCat.Logger.Test.Web/Pages/Shared/_ValidationScriptsPartial.cshtml @@ -1,2 +1,2 @@  - + \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/_ViewImports.cshtml b/Testers/EonaCat.Logger.Test.Web/Pages/_ViewImports.cshtml index 14e7f73..630f8c8 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/_ViewImports.cshtml +++ b/Testers/EonaCat.Logger.Test.Web/Pages/_ViewImports.cshtml @@ -1,3 +1,3 @@ @using EonaCat.Logger.Web @namespace EonaCat.Logger.Web.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers \ No newline at end of file diff --git a/Testers/EonaCat.Logger.Test.Web/Pages/_ViewStart.cshtml b/Testers/EonaCat.Logger.Test.Web/Pages/_ViewStart.cshtml index a5f1004..1af6e49 100644 --- a/Testers/EonaCat.Logger.Test.Web/Pages/_ViewStart.cshtml +++ b/Testers/EonaCat.Logger.Test.Web/Pages/_ViewStart.cshtml @@ -1,3 +1,3 @@ @{ Layout = "_Layout"; -} +} \ 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 9a33f6c..b27e8bd 100644 --- a/Testers/EonaCat.Logger.Test.Web/Program.cs +++ b/Testers/EonaCat.Logger.Test.Web/Program.cs @@ -10,11 +10,11 @@ using EonaCat.Web.Tracer.Extensions; var builder = WebApplication.CreateBuilder(args); // Add services to the container. -FileLoggerOptions options = new FileLoggerOptions(); +var options = new FileLoggerOptions(); options.MaxRolloverFiles = 5; options.FileSizeLimit = 1 * 1024 * 1024 / 4; options.UseLocalTime = true; -builder.Logging.AddEonaCatFileLogger(fileLoggerOptions: options, filenamePrefix:"web"); +builder.Logging.AddEonaCatFileLogger(fileLoggerOptions: options, filenamePrefix: "web"); builder.Services.AddRazorPages(); @@ -35,6 +35,7 @@ rateOptions.AddDefaultConfiguration(config => ); RateLimiterMiddleware.OnLimitResponseCreated += RateLimiterMiddlewareOnLimitResponseCreatedAsync; + async void RateLimiterMiddlewareOnLimitResponseCreatedAsync(object? sender, HttpContext httpContext) { await httpContext.Response.WriteAsync(" THIS IS MY CUSTOM RATE LIMIT MESSAGE").ConfigureAwait(false); @@ -61,7 +62,7 @@ builder.Services.AddMemoryCache(); var app = builder.Build(); Logger.UseLocalTime = true; -Logger.MaxLogType = ELogType.TRACE; +Logger.MinLogType = ELogType.TRACE; Logger.Configure(); // Configure the HTTP request pipeline. @@ -83,6 +84,7 @@ if (!app.Environment.IsDevelopment()) // await httpContext.Response.WriteAsync("THIS IS MY CUSTOM RATE LIMIT MESSAGE"); //} WebTracer.OnLog += WebTracer_OnLog; + void WebTracer_OnLog(object? sender, string e) { Console.WriteLine(e); @@ -103,10 +105,10 @@ void RunLoggingExceptionTests() var loggerSettings = new LoggerSettings(); loggerSettings.FileLoggerOptions.UseLocalTime = true; loggerSettings.UseLocalTime = true; - loggerSettings.MaxLogType = ELogType.TRACE; + loggerSettings.MinLogType = ELogType.INFO; var logger = new LogManager(loggerSettings); - for (int i = 0; i < 10; i++) + for (var i = 0; i < 10; i++) { try { @@ -117,19 +119,20 @@ void RunLoggingExceptionTests() logger.Write(exception); Console.WriteLine($"Normal ExceptionLogged: {i}"); } + Task.Delay(1); } } -Task.Run(RunWebLoggerTests); -Task.Run(RunWebLoggingTests); +//Task.Run(RunWebLoggerTests); +//Task.Run(RunWebLoggingTests); Task.Run(RunLoggingTests); -Task.Run(RunLoggingExceptionTests); -Task.Run(RunWebLoggingExceptionTests); +//Task.Run(RunLoggingExceptionTests); +//Task.Run(RunWebLoggingExceptionTests); void RunWebLoggingExceptionTests() { - for (int i = 0; i < 10; i++) + for (var i = 0; i < 10; i++) { try { @@ -145,18 +148,16 @@ void RunWebLoggingExceptionTests() app.Logger.LogInformation(exception, "INFORMATION"); Console.WriteLine($"WebExceptionLogged: {i}"); } + Task.Delay(1); } } void RunWebLoggingTests() { - if (!Directory.Exists(Logger.LogFolder)) - { - Directory.CreateDirectory(Logger.LogFolder); - } + if (!Directory.Exists(Logger.LogFolder)) Directory.CreateDirectory(Logger.LogFolder); - for (int i = 0; i < 9000000; i++) + for (var i = 0; i < 9000000; i++) { app.Logger.LogInformation($"web-test {i}"); File.AppendAllText(Path.Combine(Logger.LogFolder, "test.log"), $"WebLogged: {i}{Environment.NewLine}"); @@ -170,15 +171,15 @@ void RunLoggingTests() var loggerSettings = new LoggerSettings(); loggerSettings.UseLocalTime = true; loggerSettings.FileLoggerOptions.UseLocalTime = true; - loggerSettings.MaxLogType = ELogType.TRACE; + loggerSettings.MinLogType = ELogType.INFO; loggerSettings.FileLoggerOptions.FileSizeLimit = 1024 * 1024 * 1; loggerSettings.FileLoggerOptions.FileNamePrefix = "AllTypes"; loggerSettings.FileLoggerOptions.MaxRolloverFiles = 5; var logger = new LogManager(loggerSettings); - for (int i = 0; i < 9000000; i++) + for (var i = 0; i < 9000000; i++) { - logger.Write($"test to file {i} INFO", ELogType.INFO); + logger.Write($"test to file {i} 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); @@ -193,9 +194,9 @@ void RunLoggingTests() void RunWebLoggerTests() { - for (int i = 0; i < 9000000; i++) + for (var i = 0; i < 9000000; i++) { - Logger.Log($"test via logger {i} INFO", ELogType.INFO); + Logger.Log($"test via logger {i} INFO"); Logger.Log($"test via logger {i} CRITICAL", ELogType.CRITICAL); Logger.Log($"test via logger {i} DEBUG", ELogType.DEBUG); Logger.Log($"test via logger {i} ERROR", ELogType.ERROR); @@ -208,4 +209,4 @@ void RunWebLoggerTests() } } -app.Run(); +app.Run(); \ No newline at end of file