< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Logging.LoggerManager
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Logging/LoggerManager.cs
Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
34%
Covered lines: 21
Uncovered lines: 40
Coverable lines: 61
Total lines: 226
Line coverage: 34.4%
Branch coverage
37%
Covered branches: 12
Total branches: 32
Branch coverage: 37.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
EnsureSwitch(...)100%210%
Register(...)87.5%8887.5%
New(...)100%11100%
SetLevelSwitch(...)100%210%
GetLevelSwitch(...)0%620%
ListLevels()100%210%
CloseAndFlush(...)0%2040%
CloseAndFlush(...)0%2040%
GetName(...)0%2040%
TryGetName(...)100%210%
Contains(...)100%210%
Contains(...)100%210%
Contains(...)100%210%
get_DefaultLoggerName()100%210%
set_DefaultLoggerName(...)0%620%
get_DefaultLogger()100%210%
set_DefaultLogger(...)0%620%
Get(...)50%22100%
List()100%210%
Clear()100%44100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Logging/LoggerManager.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using Serilog;
 3using Serilog.Core;
 4using Serilog.Events;
 5
 6namespace Kestrun.Logging;
 7
 8/// <summary>
 9/// Manages a collection of named Serilog loggers and their configurations.
 10/// </summary>
 11public static class LoggerManager
 12{
 113    private static readonly ConcurrentDictionary<string, Serilog.ILogger> _loggers = new(StringComparer.OrdinalIgnoreCas
 114    private static readonly ConcurrentDictionary<string, LoggerConfiguration> _configs = new(StringComparer.OrdinalIgnor
 15
 16    /// <summary>
 17    /// A collection of named logging level switches for dynamic log level control.
 18    /// </summary>
 119    private static readonly ConcurrentDictionary<string, LoggingLevelSwitch> _switches = new(StringComparer.OrdinalIgnor
 20
 21
 22    internal static LoggingLevelSwitch EnsureSwitch(string name, LogEventLevel initial = LogEventLevel.Information)
 023        => _switches.GetOrAdd(name, _ => new LoggingLevelSwitch(initial));
 24
 25    /// <summary>
 26    /// Register an existing Serilog logger instance under a name.
 27    /// </summary>
 28    /// <param name="name">The name of the logger.</param>
 29    /// <param name="logger">The logger instance to register.</param>
 30    /// <param name="setAsDefault">If true, sets the registered logger as the Serilog default logger.</param>
 31    /// <param name="levelSwitch">An optional logging level switch to associate with the logger for dynamic level contro
 32    /// <returns>The registered logger instance.</returns>
 33    public static Serilog.ILogger Register(string name, Serilog.ILogger logger, bool setAsDefault = false, LoggingLevelS
 34    {
 335        if (_loggers.TryGetValue(name, out var oldLogger) && oldLogger is IDisposable d)
 36        {
 137            d.Dispose();
 38        }
 39
 340        _loggers[name] = logger;
 341        if (setAsDefault)
 42        {
 343            Log.Logger = logger;
 44        }
 345        if (levelSwitch != null)
 46        {
 047            _switches[name] = levelSwitch;
 48        }
 349        return logger;
 50    }
 51
 52    /// <summary>
 53    /// Create a new <see cref="LoggerConfiguration"/> associated with a name.
 54    /// </summary>
 55    /// <param name="name">The name of the logger configuration.</param>
 56    /// <returns>The new logger configuration.</returns>
 57    public static LoggerConfiguration New(string name)
 58    {
 159        var cfg = new LoggerConfiguration()
 160            .Enrich.WithProperty("LoggerName", name);
 161        _configs[name] = cfg;
 162        return cfg;
 63    }
 64
 65
 66    /// <summary>Set the minimum level for a named logger’s switch.</summary>
 67    /// <param name="name">The name of the logger.</param>
 68    /// <param name="level">The new minimum level to set.</param>
 69    /// <remarks>
 70    /// If the logger or switch does not exist, they will be created.
 71    /// </remarks>
 72    public static void SetLevelSwitch(string name, LogEventLevel level)
 73    {
 074        var sw = EnsureSwitch(name, level);
 075        sw.MinimumLevel = level;
 076    }
 77
 78    /// <summary>Get the current minimum level for a named logger’s switch.</summary>
 79    /// <param name="name">The name of the logger.</param>
 80    /// <returns>The current minimum level, or null if the logger or switch is not found.</returns>
 81    public static LogEventLevel? GetLevelSwitch(string name)
 082        => _switches.TryGetValue(name, out var sw) ? sw.MinimumLevel : null;
 83
 84    /// <summary>List all switches and their current levels.</summary>
 85    /// <returns>A dictionary of logger names and their minimum levels.</returns>
 86    public static IReadOnlyDictionary<string, LogEventLevel> ListLevels()
 087        => _switches.ToDictionary(kv => kv.Key, kv => kv.Value.MinimumLevel, StringComparer.OrdinalIgnoreCase);
 88
 89
 90    /// <summary>CloseAndFlush a logger by name.</summary>
 91    /// <param name="name">The name of the logger to close and flush.</param>
 92    /// <returns> True if the logger was found and closed; otherwise, false.</returns>
 93    public static bool CloseAndFlush(string name)
 94    {
 095        if (_loggers.TryRemove(name, out var logger))
 96        {
 097            if (logger is IDisposable d)
 98            {
 099                d.Dispose();
 100            }
 101
 0102            _ = _configs.TryRemove(name, out _);
 0103            _ = _switches.TryRemove(name, out _);
 0104            return true;
 105        }
 0106        return false;
 107    }
 108
 109    /// <summary>
 110    /// CloseAndFlush a logger instance.
 111    /// </summary>
 112    /// <param name="logger">The logger instance to close and flush.</param>
 113    /// <returns>True if the logger was found and closed; otherwise, false.</returns>
 114    public static bool CloseAndFlush(Serilog.ILogger logger)
 115    {
 0116        if (logger is IDisposable d)
 117        {
 0118            d.Dispose();
 119        }
 120
 0121        var removed = false;
 122        // Find all registered names that reference this logger and remove them.
 0123        var keys = _loggers.Where(kv => kv.Value == logger).Select(kv => kv.Key).ToList();
 0124        foreach (var key in keys)
 125        {
 0126            _ = _loggers.TryRemove(key, out _);
 0127            _ = _configs.TryRemove(key, out _);
 0128            _ = _switches.TryRemove(key, out _);
 0129            removed = true;
 130        }
 131
 0132        return removed;
 133    }
 134
 135    /// <summary>
 136    /// Get the name of a registered logger instance.
 137    /// </summary>
 138    /// <param name="logger">The logger instance.</param>
 139    /// <returns>The name of the logger, or null if not found.</returns>
 140    public static string? GetName(Serilog.ILogger logger)
 141    {
 0142        foreach (var kv in _loggers)
 143        {
 0144            if (ReferenceEquals(kv.Value, logger))
 145            {
 0146                return kv.Key;
 147            }
 148        }
 149
 0150        return null;
 0151    }
 152
 153    /// <summary>
 154    /// Try to get the name of a registered logger instance.
 155    /// </summary>
 156    /// <param name="logger">The logger instance.</param>
 157    /// <param name="name">The name of the logger, if found.</param>
 158    /// <returns>True if the name was found; otherwise, false.</returns>
 159    public static bool TryGetName(Serilog.ILogger logger, out string? name)
 160    {
 0161        name = GetName(logger);
 0162        return name is not null;
 163    }
 164    /// <summary>
 165    /// Check if a logger, configuration, or name exists.
 166    /// </summary>
 167    /// <param name="name">The name of the logger.</param>
 168    /// <returns> True if the logger exists; otherwise, false.</returns>
 0169    public static bool Contains(string name) => _loggers.ContainsKey(name);
 170
 171    /// <summary>
 172    /// Check if a logger, configuration, or name exists.
 173    /// </summary>
 174    /// <param name="logger">The logger instance.</param>
 175    /// <returns> True if the logger exists; otherwise, false.</returns>
 0176    public static bool Contains(Serilog.ILogger logger) => _loggers.Values.Contains(logger);
 177
 178    /// <summary>
 179    /// Check if a logger, configuration, or name exists.
 180    /// </summary>
 181    /// <param name="config">The logger configuration instance.</param>
 182    /// <returns> True if the configuration exists; otherwise, false.</returns>
 0183    public static bool Contains(LoggerConfiguration config) => _configs.Values.Contains(config);
 184
 185
 186    /// <summary>The name of the logger currently set as the Serilog default.</summary>
 187    /// <exception cref="ArgumentException">When the specified logger name is not found.</exception>
 188    public static string DefaultLoggerName
 189    {
 0190        get => _loggers.FirstOrDefault(x => x.Value == Log.Logger).Key;
 0191        set => Log.Logger = _loggers.TryGetValue(value, out var logger) ? logger :
 0192            throw new ArgumentException($"Logger '{value}' not found.", nameof(value));
 193    }
 194
 195    /// <summary>Access the Serilog default logger.</summary>
 196    /// <remarks>Setting this property to null resets the default logger to a new empty logger.</remarks>
 197    public static Serilog.ILogger DefaultLogger
 198    {
 0199        get => Log.Logger;
 0200        set => Log.Logger = value ?? new LoggerConfiguration().CreateLogger();
 201    }
 202
 203    /// <summary>Get a logger by name, or null if not found.</summary>
 204    /// <param name="name">The name of the logger.</param>
 205    /// <returns>The logger instance, or null if not found.</returns>
 2206    public static Serilog.ILogger? Get(string name) => _loggers.TryGetValue(name, out var logger) ? logger : null;
 207
 208    /// <summary>List all registered logger names.</summary>
 0209    public static string[] List() => [.. _loggers.Keys];
 210
 211    /// <summary>Remove and dispose all registered loggers.</summary>
 212    /// <remarks>Also clears the default logger.</remarks>
 213    public static void Clear()
 214    {
 14215        foreach (var (_, logger) in _loggers)
 216        {
 2217            if (logger is IDisposable d)
 218            {
 2219                d.Dispose();
 220            }
 221        }
 222
 5223        _loggers.Clear();
 5224        _configs.Clear();
 5225    }
 226}