From 2c4fdc28f53fd2e4d68ad429e69b645d17abcc26 Mon Sep 17 00:00:00 2001 From: AraHaan Date: Fri, 19 Jun 2026 08:24:02 -0400 Subject: [PATCH 1/5] Initial File Logger Implementation. Note: The FileLoggerOptions.AddFormatter method will need proper implementation and params to make it work. Note 2: Also the ConsoleColor's part of PrettierFormatterOptions *may* not be supported when logging to a file, but that depends on if the output file is a Rich Text Document or not. --- .../Logging/File/FileLogger.cs | 66 +++++++++++++++++++ .../Logging/File/FileLoggerOptions.cs | 63 ++++++++++++++++++ .../Logging/File/FileLoggerOptionsFilter.cs | 20 ++++++ .../Logging/File/FileLoggerProvider.cs | 31 +++++++++ .../Logging/FormatterExtensions.cs | 41 ++++++++++-- 5 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 src/Nimble.Extensions/Logging/File/FileLogger.cs create mode 100644 src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs create mode 100644 src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs create mode 100644 src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs diff --git a/src/Nimble.Extensions/Logging/File/FileLogger.cs b/src/Nimble.Extensions/Logging/File/FileLogger.cs new file mode 100644 index 0000000..7df9903 --- /dev/null +++ b/src/Nimble.Extensions/Logging/File/FileLogger.cs @@ -0,0 +1,66 @@ +namespace Nimble.Extensions.Logging.File; + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +internal class FileLogger : ILogger +{ + private readonly string _directoryName; + private readonly string categoryName; + private readonly IOptions _options; + private static readonly Lock _lock = new(); + + public FileLogger(string categoryName, IOptions options) + { + this._options = options; + this.categoryName = categoryName; + this._directoryName = Path.GetDirectoryName(options.Value.GetFilter(this.categoryName)?.FilePath) ?? string.Empty; + if (!string.IsNullOrEmpty(this._directoryName)) + { + // If the directory containing the target file name to write to does not exist; create it. + if (!Directory.Exists(this._directoryName)) + { + _ = Directory.CreateDirectory(this._directoryName); + } + } + } + + internal IExternalScopeProvider? ScopeProvider { get; set; } + + public bool IsEnabled(LogLevel logLevel) + { + var filter = this._options.Value.GetFilter(this.categoryName); + return filter != null && logLevel >= filter.Value.MinLevel; + } + + public void Log(LogLevel logLevel, EventId eventId, + TState state, Exception? exception, Func formatter) + { + if (!this.IsEnabled(logLevel)) + { + return; + } + + if (formatter != null) + { + if (_lock.TryEnter(Timeout.InfiniteTimeSpan)) + { + // We know for sure that if IsEnabled returns true, then filter is not null, + // but we need to get the filter again to get the file path. + var filter = this._options.Value.GetFilter(this.categoryName); + var message = $"[{this.categoryName}] {logLevel}: {DateTime.UtcNow} {formatter(state, exception)}{Environment.NewLine}"; + try + { + System.IO.File.AppendAllText(filter!.Value.FilePath, message); + } + finally + { + _lock.Exit(); + } + } + } + } + + public IDisposable? BeginScope(TState state) where TState : notnull + => this.ScopeProvider?.Push(state); +} diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs new file mode 100644 index 0000000..cf0e89c --- /dev/null +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs @@ -0,0 +1,63 @@ +namespace Nimble.Extensions.Logging.File; + +using System.Collections.Concurrent; +using System.Diagnostics; +using Microsoft.Extensions.Logging; + +/// +/// The options for the FileLogger. +/// +[DebuggerDisplay("{DebuggerToString(),nq}")] +public sealed class FileLoggerOptions +{ + /// + /// Initializes a new instance of the class. + /// + public FileLoggerOptions() + { + } + + private ConcurrentDictionary Filters { get; } = new(); + + /// + /// Adds a new logging filter to this options instance. + /// + /// The category for the filter. + /// The file path (including file name) for the log file to output to. + /// The minimum level of log messages for the filter. + /// To enable capturing of logging scopes in the filter. + /// The same options so that multiple calls can be chained. + public FileLoggerOptions AddFilter(string categoryName, string filePath, LogLevel minLevel = LogLevel.Debug, bool captureScopes = true) + { + _ = this.Filters.TryAdd(categoryName, new FileLoggerOptionsFilter + { + CaptureScopes = captureScopes, + MinLevel = minLevel, + FilePath = filePath, + }); + return this; + } + + // TODO: Add the ability to configure the formatter in this Options instance for the file logger. + public FileLoggerOptions AddFormatter() + { + return this; + } + + internal FileLoggerOptionsFilter? GetFilter(string categoryName) + { + foreach (var filter in this.Filters) + { + if (categoryName.StartsWith(filter.Key, StringComparison.Ordinal)) + { + return filter.Value; + } + } + + // in the code that uses this method, if the return value from this is null, then it will be logged anyways. + return null; + } + + internal string DebuggerToString() + => string.Join(", ", this.Filters.Values.Select(static flof => flof.DebuggerToString())); +} diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs new file mode 100644 index 0000000..82a26b1 --- /dev/null +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs @@ -0,0 +1,20 @@ +namespace Nimble.Extensions.Logging.File; + +using Microsoft.Extensions.Logging; + +internal struct FileLoggerOptionsFilter +{ + public FileLoggerOptionsFilter() + { + } + + public required bool CaptureScopes { get; set; } = true; + + public required LogLevel MinLevel { get; set; } + + public required string FilePath { get; set; } = string.Empty; + + internal readonly string DebuggerToString() + => $"CaptureScopes = {this.CaptureScopes}, {( + this.MinLevel != LogLevel.None ? $"MinLevel = {this.MinLevel}" : "Enabled = false")}"; +} diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs b/src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs new file mode 100644 index 0000000..751b811 --- /dev/null +++ b/src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs @@ -0,0 +1,31 @@ +namespace Nimble.Extensions.Logging.File; + +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +internal class FileLoggerProvider : ILoggerProvider, ISupportExternalScope +{ + private readonly IOptions _options; + private readonly ConcurrentDictionary _loggers = new(); + private IExternalScopeProvider _scopeProvider = null!; + + public FileLoggerProvider(IOptions options) + => this._options = options; + + public ILogger CreateLogger(string name) + => this._loggers.GetOrAdd(name, n => new FileLogger(n, this._options)); + + public void SetScopeProvider(IExternalScopeProvider scopeProvider) + { + this._scopeProvider = scopeProvider; + foreach (var logger in this._loggers) + { + logger.Value.ScopeProvider = this._scopeProvider; + } + } + + public void Dispose() + { + } +} diff --git a/src/Nimble.Extensions/Logging/FormatterExtensions.cs b/src/Nimble.Extensions/Logging/FormatterExtensions.cs index a51c0e2..2d9535a 100644 --- a/src/Nimble.Extensions/Logging/FormatterExtensions.cs +++ b/src/Nimble.Extensions/Logging/FormatterExtensions.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Nimble.Extensions.Logging.Console; +using Nimble.Extensions.Logging.File; using System.ComponentModel; namespace Nimble.Extensions.Logging; @@ -42,12 +43,44 @@ public static ILoggingBuilder AddPrettierConsole(this ILoggingBuilder builder) public static ILoggingBuilder AddPrettierConsole(this ILoggingBuilder builder, Action configure) { ArgumentNullException.ThrowIfNull(configure); + _ = builder.AddConsoleFormatter() + .AddConsole(static (options) => options.FormatterName = nameof(PrettierFormatter)); + _ = builder.Services.AddSingleton() + .Configure(configure); + return builder; + } + + /// + /// Adds a file logging provider to the . + /// + /// The to add the provider to. + /// The action used to configure the logger. + /// If the existing providers added to the should be removed. + /// The so that additional calls can be chained. + public static ILoggingBuilder AddFile(this ILoggingBuilder builder, Action configureOptions, bool clearExistingProviders = false) + => AddFileLoggerProvider(builder, configureOptions, clearExistingProviders); + + /// + /// Adds a file logging provider to the . + /// + /// The to add the provider to. + /// The action used to configure the logger. + /// If the existing providers added to the should be removed. + /// The so that additional calls can be chained. + public static ILoggingBuilder AddFileLoggerProvider(this ILoggingBuilder builder, Action configureOptions, bool clearExistingProviders = false) + { + if (clearExistingProviders) + { + _ = builder.ClearProviders(); + } - builder.AddConsoleFormatter(); - builder.AddConsole((options) => options.FormatterName = nameof(PrettierFormatter)); - builder.Services.AddSingleton(); - builder.Services.Configure(configure); + _ = builder.Services + .Configure(configureOptions) + .AddSingleton(); + // here in my own original code I would register a "static logger" to use for general + // logs in my discord bot to possibly find bugs/issues in my code, but left it out here + // as not everyone happens to need said "logger". return builder; } From d2945f913a6079cd54a6853d3ec050b02a2ad79e Mon Sep 17 00:00:00 2001 From: AraHaan Date: Fri, 19 Jun 2026 11:04:44 -0400 Subject: [PATCH 2/5] sOptimize Directory Creation for file logs. --- src/Nimble.Extensions/Logging/File/FileLogger.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Nimble.Extensions/Logging/File/FileLogger.cs b/src/Nimble.Extensions/Logging/File/FileLogger.cs index 7df9903..331af4b 100644 --- a/src/Nimble.Extensions/Logging/File/FileLogger.cs +++ b/src/Nimble.Extensions/Logging/File/FileLogger.cs @@ -18,10 +18,13 @@ public FileLogger(string categoryName, IOptions options) if (!string.IsNullOrEmpty(this._directoryName)) { // If the directory containing the target file name to write to does not exist; create it. - if (!Directory.Exists(this._directoryName)) + try { _ = Directory.CreateDirectory(this._directoryName); } + catch + { + } } } From 2801fb13f3d87009f65ee86bb5ff27ca6cbc2070 Mon Sep 17 00:00:00 2001 From: AraHaan Date: Fri, 19 Jun 2026 12:24:48 -0400 Subject: [PATCH 3/5] Start on basic log file rolling. --- .../Logging/File/FileLogRollingMethod.cs | 32 +++++++++++++++++ .../Logging/File/FileLogger.cs | 20 ++++------- .../Logging/File/FileLoggerOptions.cs | 8 ++++- .../Logging/File/FileLoggerOptionsFilter.cs | 36 +++++++++++++++++++ 4 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs diff --git a/src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs b/src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs new file mode 100644 index 0000000..e3a4d03 --- /dev/null +++ b/src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs @@ -0,0 +1,32 @@ +namespace Nimble.Extensions.Logging.File; + +/// +/// This determines the type of rolling for file logging. +/// +public enum FileLogRollingMethod +{ + /// + /// No file rolling is performed. + /// + None = 0, + + /// + /// File rolling is performed Daily. + /// + Daily = 1, + + /// + /// File rolling is performed Weekly. + /// + Weekly = 2, + + /// + /// File rolling is performed Monthly. + /// + Monthly = 3, + + /// + /// File rolling is performed Yearly. + /// + Yearly = 4, +} \ No newline at end of file diff --git a/src/Nimble.Extensions/Logging/File/FileLogger.cs b/src/Nimble.Extensions/Logging/File/FileLogger.cs index 331af4b..ff6136b 100644 --- a/src/Nimble.Extensions/Logging/File/FileLogger.cs +++ b/src/Nimble.Extensions/Logging/File/FileLogger.cs @@ -5,7 +5,6 @@ internal class FileLogger : ILogger { - private readonly string _directoryName; private readonly string categoryName; private readonly IOptions _options; private static readonly Lock _lock = new(); @@ -14,18 +13,10 @@ public FileLogger(string categoryName, IOptions options) { this._options = options; this.categoryName = categoryName; - this._directoryName = Path.GetDirectoryName(options.Value.GetFilter(this.categoryName)?.FilePath) ?? string.Empty; - if (!string.IsNullOrEmpty(this._directoryName)) - { - // If the directory containing the target file name to write to does not exist; create it. - try - { - _ = Directory.CreateDirectory(this._directoryName); - } - catch - { - } - } + + // Create the log folder, this optionally includes the datestamped rolling + // log folders when rolling logging are enabled. + options.Value.GetFilter(this.categoryName)?.CheckFileRolling(); } internal IExternalScopeProvider? ScopeProvider { get; set; } @@ -52,6 +43,9 @@ public void Log(LogLevel logLevel, EventId eventId, // but we need to get the filter again to get the file path. var filter = this._options.Value.GetFilter(this.categoryName); var message = $"[{this.categoryName}] {logLevel}: {DateTime.UtcNow} {formatter(state, exception)}{Environment.NewLine}"; + + // if file rolling is enabled this will update the file path in the filter to the new log file. + filter!.Value.CheckFileRolling(); try { System.IO.File.AppendAllText(filter!.Value.FilePath, message); diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs index cf0e89c..ddae61c 100644 --- a/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs @@ -27,13 +27,19 @@ public FileLoggerOptions() /// The minimum level of log messages for the filter. /// To enable capturing of logging scopes in the filter. /// The same options so that multiple calls can be chained. - public FileLoggerOptions AddFilter(string categoryName, string filePath, LogLevel minLevel = LogLevel.Debug, bool captureScopes = true) + public FileLoggerOptions AddFilter( + string categoryName, + string filePath, + LogLevel minLevel = LogLevel.Debug, + bool captureScopes = true, + FileLogRollingMethod rollingMethod = FileLogRollingMethod.None) { _ = this.Filters.TryAdd(categoryName, new FileLoggerOptionsFilter { CaptureScopes = captureScopes, MinLevel = minLevel, FilePath = filePath, + RollingMethod = rollingMethod, }); return this; } diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs index 82a26b1..87d4dab 100644 --- a/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs @@ -14,7 +14,43 @@ public FileLoggerOptionsFilter() public required string FilePath { get; set; } = string.Empty; + public required FileLogRollingMethod RollingMethod { get; set; } = FileLogRollingMethod.None; + + private string? OriginalLogFolderName { get; set; } + internal readonly string DebuggerToString() => $"CaptureScopes = {this.CaptureScopes}, {( this.MinLevel != LogLevel.None ? $"MinLevel = {this.MinLevel}" : "Enabled = false")}"; + + internal void CheckFileRolling() + { + this.OriginalLogFolderName ??= Path.GetDirectoryName(this.FilePath) ?? string.Empty; + var directoryName = this.OriginalLogFolderName; + var fileName = Path.GetFileName(this.FilePath) ?? string.Empty; + if (this.RollingMethod is not FileLogRollingMethod.None) + { + // This should ideally compare the current date with a private stored date so that way there + // are no attempts to roll the file early in the case of the Monthly/Yearly file rolling + // options being used. + directoryName = Path.Combine(this.OriginalLogFolderName, DateTime.UtcNow.ToString("yyyy-MM-dd")); + } + + CreateDirectory(directoryName); + this.FilePath = Path.Combine(directoryName, fileName); + } + + private static void CreateDirectory(string namePath) + { + if (!string.IsNullOrEmpty(namePath)) + { + // If the directory containing the target file name to write to does not exist; create it. + try + { + _ = Directory.CreateDirectory(namePath); + } + catch + { + } + } + } } From 1874b694e20173046c7d5c3ce466f51defe1a743 Mon Sep 17 00:00:00 2001 From: AraHaan Date: Sun, 21 Jun 2026 03:18:09 -0400 Subject: [PATCH 4/5] Added more impl to rolling file logs + a few fixes. --- .../Logging/File/FileLogRollingMethod.cs | 4 +- .../Logging/File/FileLogger.cs | 12 +++--- .../Logging/File/FileLoggerOptions.cs | 6 +-- .../Logging/File/FileLoggerOptionsFilter.cs | 39 +++++++++++++++---- .../Logging/File/FileLoggerProvider.cs | 10 ++--- src/Nimble.Tests.Extensions/Program.cs | 6 +-- 6 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs b/src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs index e3a4d03..ccdbe69 100644 --- a/src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs +++ b/src/Nimble.Extensions/Logging/File/FileLogRollingMethod.cs @@ -28,5 +28,5 @@ public enum FileLogRollingMethod /// /// File rolling is performed Yearly. /// - Yearly = 4, -} \ No newline at end of file + Yearly = 4 +} diff --git a/src/Nimble.Extensions/Logging/File/FileLogger.cs b/src/Nimble.Extensions/Logging/File/FileLogger.cs index ff6136b..18d2639 100644 --- a/src/Nimble.Extensions/Logging/File/FileLogger.cs +++ b/src/Nimble.Extensions/Logging/File/FileLogger.cs @@ -11,7 +11,7 @@ internal class FileLogger : ILogger public FileLogger(string categoryName, IOptions options) { - this._options = options; + _options = options; this.categoryName = categoryName; // Create the log folder, this optionally includes the datestamped rolling @@ -23,14 +23,14 @@ public FileLogger(string categoryName, IOptions options) public bool IsEnabled(LogLevel logLevel) { - var filter = this._options.Value.GetFilter(this.categoryName); + var filter = _options.Value.GetFilter(categoryName); return filter != null && logLevel >= filter.Value.MinLevel; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - if (!this.IsEnabled(logLevel)) + if (!IsEnabled(logLevel)) { return; } @@ -41,8 +41,8 @@ public void Log(LogLevel logLevel, EventId eventId, { // We know for sure that if IsEnabled returns true, then filter is not null, // but we need to get the filter again to get the file path. - var filter = this._options.Value.GetFilter(this.categoryName); - var message = $"[{this.categoryName}] {logLevel}: {DateTime.UtcNow} {formatter(state, exception)}{Environment.NewLine}"; + var filter = _options.Value.GetFilter(categoryName); + var message = $"[{categoryName}] {logLevel}: {DateTime.UtcNow} {formatter(state, exception)}{Environment.NewLine}"; // if file rolling is enabled this will update the file path in the filter to the new log file. filter!.Value.CheckFileRolling(); @@ -59,5 +59,5 @@ public void Log(LogLevel logLevel, EventId eventId, } public IDisposable? BeginScope(TState state) where TState : notnull - => this.ScopeProvider?.Push(state); + => ScopeProvider?.Push(state); } diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs index ddae61c..0a4ddf7 100644 --- a/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs @@ -34,7 +34,7 @@ public FileLoggerOptions AddFilter( bool captureScopes = true, FileLogRollingMethod rollingMethod = FileLogRollingMethod.None) { - _ = this.Filters.TryAdd(categoryName, new FileLoggerOptionsFilter + _ = Filters.TryAdd(categoryName, new FileLoggerOptionsFilter { CaptureScopes = captureScopes, MinLevel = minLevel, @@ -52,7 +52,7 @@ public FileLoggerOptions AddFormatter() internal FileLoggerOptionsFilter? GetFilter(string categoryName) { - foreach (var filter in this.Filters) + foreach (var filter in Filters) { if (categoryName.StartsWith(filter.Key, StringComparison.Ordinal)) { @@ -65,5 +65,5 @@ public FileLoggerOptions AddFormatter() } internal string DebuggerToString() - => string.Join(", ", this.Filters.Values.Select(static flof => flof.DebuggerToString())); + => string.Join(", ", Filters.Values.Select(static flof => flof.DebuggerToString())); } diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs index 87d4dab..6ae53ea 100644 --- a/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs @@ -18,25 +18,48 @@ public FileLoggerOptionsFilter() private string? OriginalLogFolderName { get; set; } + private DateOnly? CurrentRollingDate { get; set; } + internal readonly string DebuggerToString() - => $"CaptureScopes = {this.CaptureScopes}, {( - this.MinLevel != LogLevel.None ? $"MinLevel = {this.MinLevel}" : "Enabled = false")}"; + => $"CaptureScopes = {CaptureScopes}, {( + MinLevel != LogLevel.None ? $"MinLevel = {MinLevel}" : "Enabled = false")}"; internal void CheckFileRolling() { - this.OriginalLogFolderName ??= Path.GetDirectoryName(this.FilePath) ?? string.Empty; - var directoryName = this.OriginalLogFolderName; - var fileName = Path.GetFileName(this.FilePath) ?? string.Empty; - if (this.RollingMethod is not FileLogRollingMethod.None) + OriginalLogFolderName ??= Path.GetDirectoryName(FilePath) ?? string.Empty; + var directoryName = OriginalLogFolderName; + var fileName = Path.GetFileName(FilePath) ?? string.Empty; + if (RollingMethod is not FileLogRollingMethod.None) { + var currentDate = DateOnly.FromDateTime(DateTime.UtcNow); + + // if null or when the current date is greater than or equal to the current rolling date. + if (CurrentRollingDate is null || currentDate >= CurrentRollingDate) + { + CurrentRollingDate = RollingMethod switch + { + FileLogRollingMethod.Daily => currentDate.AddDays(1), + FileLogRollingMethod.Weekly => currentDate.AddDays(7), + FileLogRollingMethod.Monthly => currentDate.AddMonths(1), + FileLogRollingMethod.Yearly => currentDate.AddYears(1), + _ => throw new InvalidOperationException("bug by design.") + }; + } + // This should ideally compare the current date with a private stored date so that way there // are no attempts to roll the file early in the case of the Monthly/Yearly file rolling // options being used. - directoryName = Path.Combine(this.OriginalLogFolderName, DateTime.UtcNow.ToString("yyyy-MM-dd")); + directoryName = RollingMethod switch + { + FileLogRollingMethod.Daily or FileLogRollingMethod.Weekly => Path.Combine(OriginalLogFolderName, currentDate.ToString("yyyy-MM-dd")), + FileLogRollingMethod.Monthly => Path.Combine(OriginalLogFolderName, currentDate.ToString("yyyy-MM")), + FileLogRollingMethod.Yearly => Path.Combine(OriginalLogFolderName, currentDate.ToString("yyyy")), + _ => throw new InvalidOperationException("bug by design.") + }; } CreateDirectory(directoryName); - this.FilePath = Path.Combine(directoryName, fileName); + FilePath = Path.Combine(directoryName, fileName); } private static void CreateDirectory(string namePath) diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs b/src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs index 751b811..84902f8 100644 --- a/src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs +++ b/src/Nimble.Extensions/Logging/File/FileLoggerProvider.cs @@ -11,17 +11,17 @@ internal class FileLoggerProvider : ILoggerProvider, ISupportExternalScope private IExternalScopeProvider _scopeProvider = null!; public FileLoggerProvider(IOptions options) - => this._options = options; + => _options = options; public ILogger CreateLogger(string name) - => this._loggers.GetOrAdd(name, n => new FileLogger(n, this._options)); + => _loggers.GetOrAdd(name, n => new FileLogger(n, _options)); public void SetScopeProvider(IExternalScopeProvider scopeProvider) { - this._scopeProvider = scopeProvider; - foreach (var logger in this._loggers) + _scopeProvider = scopeProvider; + foreach (var logger in _loggers) { - logger.Value.ScopeProvider = this._scopeProvider; + logger.Value.ScopeProvider = _scopeProvider; } } diff --git a/src/Nimble.Tests.Extensions/Program.cs b/src/Nimble.Tests.Extensions/Program.cs index 63e7a45..1f7e335 100644 --- a/src/Nimble.Tests.Extensions/Program.cs +++ b/src/Nimble.Tests.Extensions/Program.cs @@ -7,13 +7,13 @@ builder.Logging .SetMinimumLevel(LogLevel.Trace) - .AddPrettierConsole(configure => + .AddPrettierConsole(static configure => { configure.MaxLogWidth = 120; configure.TimestampColor = ConsoleColor.DarkCyan; configure.SpecialCategoryPrefix = "Nimble"; }) - .AddConsoleListener(configure => + .AddConsoleListener(static configure => { configure.CreateScopes = true; configure.OnReadlineCompleted = HandleCommand; @@ -37,4 +37,4 @@ static void HandleCommand(string input, IServiceProvider srv, CancellationToken } else logger.LogWarning("Unrecognized command: {Input}", input); -} \ No newline at end of file +} From f711b66110d03e19e510cb530cc52fd91aef793a Mon Sep 17 00:00:00 2001 From: AraHaan Date: Sun, 21 Jun 2026 09:49:17 -0400 Subject: [PATCH 5/5] Remove discards. --- .../Logging/File/FileLoggerOptions.cs | 2 +- .../Logging/File/FileLoggerOptionsFilter.cs | 2 +- .../Logging/FormatterExtensions.cs | 27 +++++++++---------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs index 0a4ddf7..784e713 100644 --- a/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptions.cs @@ -34,7 +34,7 @@ public FileLoggerOptions AddFilter( bool captureScopes = true, FileLogRollingMethod rollingMethod = FileLogRollingMethod.None) { - _ = Filters.TryAdd(categoryName, new FileLoggerOptionsFilter + Filters.TryAdd(categoryName, new FileLoggerOptionsFilter { CaptureScopes = captureScopes, MinLevel = minLevel, diff --git a/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs index 6ae53ea..adfff47 100644 --- a/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs +++ b/src/Nimble.Extensions/Logging/File/FileLoggerOptionsFilter.cs @@ -69,7 +69,7 @@ private static void CreateDirectory(string namePath) // If the directory containing the target file name to write to does not exist; create it. try { - _ = Directory.CreateDirectory(namePath); + Directory.CreateDirectory(namePath); } catch { diff --git a/src/Nimble.Extensions/Logging/FormatterExtensions.cs b/src/Nimble.Extensions/Logging/FormatterExtensions.cs index 2d9535a..9823ff8 100644 --- a/src/Nimble.Extensions/Logging/FormatterExtensions.cs +++ b/src/Nimble.Extensions/Logging/FormatterExtensions.cs @@ -13,16 +13,12 @@ namespace Nimble.Extensions.Logging; public static class FormatterExtensions { public static PrettierFormatterOptions ResetColors(this PrettierFormatterOptions options) - { - options.LogLevelColors[LogLevel.Trace] = ConsoleColor.Gray; - options.LogLevelColors[LogLevel.Debug] = ConsoleColor.Cyan; - options.LogLevelColors[LogLevel.Information] = ConsoleColor.Green; - options.LogLevelColors[LogLevel.Warning] = ConsoleColor.Yellow; - options.LogLevelColors[LogLevel.Error] = ConsoleColor.Red; - options.LogLevelColors[LogLevel.Critical] = ConsoleColor.Magenta; - - return options; - } + => options.SetColor(LogLevel.Trace, ConsoleColor.Gray) + .SetColor(LogLevel.Debug, ConsoleColor.Cyan) + .SetColor(LogLevel.Information, ConsoleColor.Green) + .SetColor(LogLevel.Warning, ConsoleColor.Yellow) + .SetColor(LogLevel.Error, ConsoleColor.Red) + .SetColor(LogLevel.Critical, ConsoleColor.Magenta); public static PrettierFormatterOptions SetColor(this PrettierFormatterOptions options, LogLevel level, ConsoleColor color) { @@ -43,9 +39,9 @@ public static ILoggingBuilder AddPrettierConsole(this ILoggingBuilder builder) public static ILoggingBuilder AddPrettierConsole(this ILoggingBuilder builder, Action configure) { ArgumentNullException.ThrowIfNull(configure); - _ = builder.AddConsoleFormatter() + builder.AddConsoleFormatter() .AddConsole(static (options) => options.FormatterName = nameof(PrettierFormatter)); - _ = builder.Services.AddSingleton() + builder.Services.AddSingleton() .Configure(configure); return builder; } @@ -71,10 +67,10 @@ public static ILoggingBuilder AddFileLoggerProvider(this ILoggingBuilder builder { if (clearExistingProviders) { - _ = builder.ClearProviders(); + builder.ClearProviders(); } - _ = builder.Services + builder.Services .Configure(configureOptions) .AddSingleton(); @@ -88,7 +84,8 @@ public static ILoggingBuilder AddConsoleListener(this ILoggingBuilder builder, A { ArgumentNullException.ThrowIfNull(configure); - builder.Services.AddOptionsWithValidateOnStart() + builder.Services + .AddOptionsWithValidateOnStart() .Configure(configure); builder.Services.AddHostedService();