< Summary - Jellyfin

Information
Class: Jellyfin.Server.ServerSetupApp.StartupLogger
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/ServerSetupApp/StartupLogger.cs
Line coverage
93%
Covered lines: 40
Uncovered lines: 3
Coverable lines: 43
Total lines: 155
Line coverage: 93%
Branch coverage
80%
Covered branches: 8
Total branches: 10
Branch coverage: 80%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/16/2026 - 12:15:55 AM Line coverage: 88.2% (30/34) Branch coverage: 60% (6/10) Total lines: 12410/29/2026 - 12:17:11 AM Line coverage: 93% (40/43) Branch coverage: 80% (8/10) Total lines: 155 5/16/2026 - 12:15:55 AM Line coverage: 88.2% (30/34) Branch coverage: 60% (6/10) Total lines: 12410/29/2026 - 12:17:11 AM Line coverage: 93% (40/43) Branch coverage: 80% (8/10) Total lines: 155

Coverage delta

Coverage delta 20 -20

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_Topic()100%11100%
BeginAmbientTopic(...)100%11100%
BeginGroup(...)100%11100%
With(...)100%11100%
With(...)100%210%
BeginGroup(...)100%11100%
AddToTopic(...)75%44100%
BeginScope(...)100%210%
IsEnabled(...)100%210%
Log(...)66.66%6691.66%
.ctor(...)100%11100%
Dispose()100%11100%

File(s)

/srv/git/jellyfin/Jellyfin.Server/ServerSetupApp/StartupLogger.cs

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.Threading;
 4using Microsoft.Extensions.Logging;
 5using Microsoft.Extensions.Logging.Abstractions;
 6
 7namespace Jellyfin.Server.ServerSetupApp;
 8
 9/// <inheritdoc/>
 10public class StartupLogger : IStartupLogger
 11{
 212    private static readonly AsyncLocal<StartupLogTopic?> _ambientTopic = new();
 13
 14    private readonly StartupLogTopic? _topic;
 15
 16    /// <summary>
 17    /// Initializes a new instance of the <see cref="StartupLogger"/> class.
 18    /// </summary>
 19    /// <param name="logger">The underlying base logger.</param>
 20    public StartupLogger(ILogger logger)
 21    {
 362722        BaseLogger = logger;
 362723        _topic = _ambientTopic.Value;
 362724    }
 25
 26    /// <summary>
 27    /// Initializes a new instance of the <see cref="StartupLogger"/> class.
 28    /// </summary>
 29    /// <param name="logger">The underlying base logger.</param>
 30    /// <param name="topic">The group for this logger.</param>
 356431    internal StartupLogger(ILogger logger, StartupLogTopic? topic) : this(logger)
 32    {
 356433        _topic = topic;
 356434    }
 35
 236    internal static IStartupLogger Logger { get; set; } = new StartupLogger(NullLogger.Instance);
 37
 38    /// <inheritdoc/>
 1487939    public StartupLogTopic? Topic => _topic;
 40
 41    /// <summary>
 42    /// Gets or Sets the underlying base logger.
 43    /// </summary>
 44    protected ILogger BaseLogger { get; set; }
 45
 46    /// <summary>
 47    /// Makes <paramref name="topic"/> the topic that loggers created on this execution context attach to.
 48    /// </summary>
 49    /// <param name="topic">The topic to nest newly created loggers under.</param>
 50    /// <returns>A scope that restores the previously ambient topic when disposed.</returns>
 51    internal static IDisposable BeginAmbientTopic(StartupLogTopic? topic)
 52    {
 14153        var scope = new AmbientTopicScope(_ambientTopic.Value);
 14154        _ambientTopic.Value = topic;
 14155        return scope;
 56    }
 57
 58    /// <inheritdoc/>
 59    public IStartupLogger BeginGroup(FormattableString logEntry)
 60    {
 177161        return new StartupLogger(BaseLogger, AddToTopic(logEntry));
 62    }
 63
 64    /// <inheritdoc/>
 65    public IStartupLogger With(ILogger logger)
 66    {
 176567        return new StartupLogger(logger, Topic);
 68    }
 69
 70    /// <inheritdoc/>
 71    public IStartupLogger<TCategory> With<TCategory>(ILogger logger)
 72    {
 073        return new StartupLogger<TCategory>(logger, Topic);
 74    }
 75
 76    /// <inheritdoc/>
 77    public IStartupLogger<TCategory> BeginGroup<TCategory>(FormattableString logEntry)
 78    {
 2879        return new StartupLogger<TCategory>(BaseLogger, AddToTopic(logEntry));
 80    }
 81
 82    private StartupLogTopic AddToTopic(FormattableString logEntry)
 83    {
 179984        var startupEntry = new StartupLogTopic()
 179985        {
 179986            Content = logEntry.ToString(CultureInfo.InvariantCulture),
 179987            DateOfCreation = DateTimeOffset.Now
 179988        };
 89
 179990        if (Topic is null)
 91        {
 9792            SetupServer.LogQueue?.Enqueue(startupEntry);
 93        }
 94        else
 95        {
 170296            Topic.Children.Add(startupEntry);
 97        }
 98
 179999        return startupEntry;
 100    }
 101
 102    /// <inheritdoc/>
 103    public IDisposable? BeginScope<TState>(TState state)
 104        where TState : notnull
 105    {
 0106        return null;
 107    }
 108
 109    /// <inheritdoc/>
 110    public bool IsEnabled(LogLevel logLevel)
 111    {
 0112        return true;
 113    }
 114
 115    /// <inheritdoc/>
 116    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Excepti
 117    {
 4736118        if (BaseLogger.IsEnabled(logLevel))
 119        {
 120            // if enabled allow the base logger also to receive the message
 1271121            BaseLogger.Log(logLevel, eventId, state, exception, formatter);
 122        }
 123
 4736124        var startupEntry = new StartupLogTopic()
 4736125        {
 4736126            LogLevel = logLevel,
 4736127            Content = formatter(state, exception),
 4736128            DateOfCreation = DateTimeOffset.Now
 4736129        };
 130
 4736131        if (Topic is null)
 132        {
 14133            SetupServer.LogQueue?.Enqueue(startupEntry);
 134        }
 135        else
 136        {
 4722137            Topic.Children.Add(startupEntry);
 138        }
 4722139    }
 140
 141    private sealed class AmbientTopicScope : IDisposable
 142    {
 143        private readonly StartupLogTopic? _previous;
 144
 145        public AmbientTopicScope(StartupLogTopic? previous)
 146        {
 141147            _previous = previous;
 141148        }
 149
 150        public void Dispose()
 151        {
 141152            _ambientTopic.Value = _previous;
 141153        }
 154    }
 155}