< Summary

Information
Class: SwiftCollections.Diagnostics.DiagnosticLogger
Assembly: SwiftCollections
File(s): /home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Utility/Diagnostics/DiagnosticLogger.cs
Line coverage
100%
Covered lines: 47
Uncovered lines: 0
Coverable lines: 47
Total lines: 174
Line coverage: 100%
Branch coverage
100%
Covered branches: 12
Total branches: 12
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%11100%
get_Channel()100%11100%
get_DebugChannel()100%11100%
get_EnableDebugLogging()100%11100%
set_EnableDebugLogging(...)100%11100%
get_MinimumLevel()100%11100%
set_MinimumLevel(...)100%11100%
get_LogHandler()100%11100%
set_LogHandler(...)100%22100%
get_CustomFormatter()100%11100%
set_CustomFormatter(...)100%22100%
IsEnabled(...)100%11100%
DefaultLogHandler(...)100%22100%
DefaultLogFormatter(...)100%22100%
HandleDiagnosticEvent(...)100%11100%
ResolveSource(...)100%22100%
CreateChannel(...)100%11100%
RefreshDebugMinimumLevel()100%22100%

File(s)

/home/runner/work/SwiftCollections/SwiftCollections/src/SwiftCollections/Utility/Diagnostics/DiagnosticLogger.cs

#LineLine coverage
 1namespace SwiftCollections.Diagnostics;
 2
 3using System;
 4using System.Diagnostics.CodeAnalysis;
 5
 6/// <summary>
 7/// Provides reusable logger state and diagnostic channels for libraries built on SwiftCollections diagnostics.
 8/// </summary>
 9/// <remarks>
 10/// Static logger facades can delegate to a derived instance of this type while keeping their existing public API.
 11/// </remarks>
 12public abstract class DiagnosticLogger
 13{
 14    private readonly DiagnosticChannel _channel;
 15    private readonly DiagnosticChannel _debugChannel;
 16    private Action<DiagnosticLevel, string, string> _logHandler;
 17    private Func<DiagnosticLevel, string, string, string> _customFormatter;
 18    private bool _enableDebugLogging;
 19
 20    /// <summary>
 21    /// Initializes a new instance of the <see cref="DiagnosticLogger"/> class.
 22    /// </summary>
 23    /// <param name="channelName">The diagnostic channel name.</param>
 724    protected DiagnosticLogger(string channelName)
 25    {
 726        _channel = CreateChannel(channelName);
 727        _debugChannel = CreateChannel(channelName);
 728        Name = _channel.Name;
 729        _logHandler = DefaultLogHandler;
 730        _customFormatter = DefaultLogFormatter;
 731        MinimumLevel = DiagnosticLevel.Warning;
 732        RefreshDebugMinimumLevel();
 733    }
 34
 35    /// <summary>
 36    /// Gets the logger name.
 37    /// </summary>
 638    public string Name { get; }
 39
 40    /// <summary>
 41    /// Gets the diagnostic channel used for normal diagnostics.
 42    /// </summary>
 343    public DiagnosticChannel Channel => _channel;
 44
 45    /// <summary>
 46    /// Gets the diagnostic channel used for verbose debug diagnostics.
 47    /// </summary>
 548    public DiagnosticChannel DebugChannel => _debugChannel;
 49
 50    /// <summary>
 51    /// Gets or sets a value indicating whether verbose debug diagnostics should be emitted.
 52    /// </summary>
 53    public bool EnableDebugLogging
 54    {
 155        get => _enableDebugLogging;
 56        set
 57        {
 158            _enableDebugLogging = value;
 159            RefreshDebugMinimumLevel();
 160        }
 61    }
 62
 63    /// <summary>
 64    /// Gets or sets the minimum severity required for normal diagnostics to be emitted.
 65    /// </summary>
 66    public DiagnosticLevel MinimumLevel
 67    {
 168        get => _channel.MinimumLevel;
 69        set
 70        {
 1171            _channel.MinimumLevel = value;
 1172            RefreshDebugMinimumLevel();
 1173        }
 74    }
 75
 76    /// <summary>
 77    /// Gets or sets the delegate used to write formatted log messages.
 78    /// Assigning <see langword="null"/> restores <see cref="DefaultLogHandler"/>.
 79    /// </summary>
 80    [AllowNull]
 81    public Action<DiagnosticLevel, string, string> LogHandler
 82    {
 183        get => _logHandler;
 484        set => _logHandler = value ?? DefaultLogHandler;
 85    }
 86
 87    /// <summary>
 88    /// Gets or sets the formatter used to transform log arguments into a final log entry.
 89    /// Assigning <see langword="null"/> restores <see cref="DefaultLogFormatter"/>.
 90    /// </summary>
 91    [AllowNull]
 92    public Func<DiagnosticLevel, string, string, string> CustomFormatter
 93    {
 494        get => _customFormatter;
 295        set => _customFormatter = value ?? DefaultLogFormatter;
 96    }
 97
 98    /// <summary>
 99    /// Determines whether normal diagnostics at the specified level are currently enabled.
 100    /// </summary>
 101    /// <param name="level">The diagnostic level to evaluate.</param>
 102    /// <returns><see langword="true"/> when messages at <paramref name="level"/> will be emitted; otherwise, <see langw
 103    public bool IsEnabled(DiagnosticLevel level)
 104    {
 2105        return _channel.IsEnabled(level);
 106    }
 107
 108    /// <summary>
 109    /// Writes a log message using the current <see cref="CustomFormatter"/>.
 110    /// </summary>
 111    /// <param name="level">The severity level of the log message.</param>
 112    /// <param name="message">The log message.</param>
 113    /// <param name="source">The source of the log message.</param>
 114    public virtual void DefaultLogHandler(DiagnosticLevel level, string message, string source)
 115    {
 2116        string entry = CustomFormatter(level, message, source);
 2117        if (level == DiagnosticLevel.Error)
 1118            Console.Error.WriteLine(entry);
 119        else
 1120            Console.WriteLine(entry);
 1121    }
 122
 123    /// <summary>
 124    /// Formats a log entry using a deterministic, source-first layout.
 125    /// </summary>
 126    /// <param name="level">The severity level of the log message.</param>
 127    /// <param name="message">The log message.</param>
 128    /// <param name="source">The source of the log message.</param>
 129    /// <returns>A formatted log entry.</returns>
 130    public virtual string DefaultLogFormatter(DiagnosticLevel level, string message, string source)
 131    {
 5132        if (string.IsNullOrWhiteSpace(source))
 2133            return $"[{level}] {Name}: {message}";
 134
 3135        return $"[{level}] {Name}.{source}: {message}";
 136    }
 137
 138    /// <summary>
 139    /// Receives diagnostics emitted by either channel.
 140    /// </summary>
 141    /// <param name="diagnostic">The emitted diagnostic event.</param>
 142    protected virtual void HandleDiagnosticEvent(in DiagnosticEvent diagnostic)
 143    {
 2144        _logHandler(diagnostic.Level, diagnostic.Message, ResolveSource(diagnostic));
 2145    }
 146
 147    /// <summary>
 148    /// Resolves the source value passed to <see cref="LogHandler"/>.
 149    /// </summary>
 150    /// <param name="diagnostic">The emitted diagnostic event.</param>
 151    /// <returns>The resolved source.</returns>
 152    protected virtual string ResolveSource(in DiagnosticEvent diagnostic)
 153    {
 2154        return string.IsNullOrWhiteSpace(diagnostic.Source)
 2155            ? diagnostic.Channel
 2156            : diagnostic.Source;
 157    }
 158
 159    private DiagnosticChannel CreateChannel(string channelName)
 160    {
 14161        return new DiagnosticChannel(channelName)
 14162        {
 14163            MinimumLevel = DiagnosticLevel.Warning,
 14164            Sink = HandleDiagnosticEvent
 14165        };
 166    }
 167
 168    private void RefreshDebugMinimumLevel()
 169    {
 19170        _debugChannel.MinimumLevel = _enableDebugLogging
 19171            ? _channel.MinimumLevel
 19172            : DiagnosticLevel.None;
 19173    }
 174}