diff options
Diffstat (limited to 'Jellyfin.Server/ServerSetupApp')
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/IStartupLogger.cs | 43 | ||||
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/SetupServer.cs | 32 | ||||
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/StartupLogTopic.cs | 31 | ||||
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/StartupLogger.cs | 78 | ||||
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/StartupLoggerExtensions.cs | 18 | ||||
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/StartupLoggerOfCategory.cs | 56 | ||||
| -rw-r--r-- | Jellyfin.Server/ServerSetupApp/index.mstemplate.html | 5 |
7 files changed, 217 insertions, 46 deletions
diff --git a/Jellyfin.Server/ServerSetupApp/IStartupLogger.cs b/Jellyfin.Server/ServerSetupApp/IStartupLogger.cs index 2c2ef05f8..e7c193936 100644 --- a/Jellyfin.Server/ServerSetupApp/IStartupLogger.cs +++ b/Jellyfin.Server/ServerSetupApp/IStartupLogger.cs @@ -1,5 +1,4 @@ using System; -using Morestachio.Helper.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Jellyfin.Server.ServerSetupApp; @@ -10,6 +9,11 @@ namespace Jellyfin.Server.ServerSetupApp; public interface IStartupLogger : ILogger { /// <summary> + /// Gets the topic this logger is assigned to. + /// </summary> + StartupLogTopic? Topic { get; } + + /// <summary> /// Adds another logger instance to this logger for combined logging. /// </summary> /// <param name="logger">Other logger to rely messages to.</param> @@ -22,4 +26,41 @@ public interface IStartupLogger : ILogger /// <param name="logEntry">Defines the log message that introduces the new group.</param> /// <returns>A new logger that can write to the group.</returns> IStartupLogger BeginGroup(FormattableString logEntry); + + /// <summary> + /// Adds another logger instance to this logger for combined logging. + /// </summary> + /// <param name="logger">Other logger to rely messages to.</param> + /// <returns>A combined logger.</returns> + /// <typeparam name="TCategory">The logger cateogry.</typeparam> + IStartupLogger<TCategory> With<TCategory>(ILogger logger); + + /// <summary> + /// Opens a new Group logger within the parent logger. + /// </summary> + /// <param name="logEntry">Defines the log message that introduces the new group.</param> + /// <returns>A new logger that can write to the group.</returns> + /// <typeparam name="TCategory">The logger cateogry.</typeparam> + IStartupLogger<TCategory> BeginGroup<TCategory>(FormattableString logEntry); +} + +/// <summary> +/// Defines a logger that can be injected via DI to get a startup logger initialised with an logger framework connected <see cref="ILogger"/>. +/// </summary> +/// <typeparam name="TCategory">The logger cateogry.</typeparam> +public interface IStartupLogger<TCategory> : IStartupLogger +{ + /// <summary> + /// Adds another logger instance to this logger for combined logging. + /// </summary> + /// <param name="logger">Other logger to rely messages to.</param> + /// <returns>A combined logger.</returns> + new IStartupLogger<TCategory> With(ILogger logger); + + /// <summary> + /// Opens a new Group logger within the parent logger. + /// </summary> + /// <param name="logEntry">Defines the log message that introduces the new group.</param> + /// <returns>A new logger that can write to the group.</returns> + new IStartupLogger<TCategory> BeginGroup(FormattableString logEntry); } diff --git a/Jellyfin.Server/ServerSetupApp/SetupServer.cs b/Jellyfin.Server/ServerSetupApp/SetupServer.cs index d88dbee57..92e012940 100644 --- a/Jellyfin.Server/ServerSetupApp/SetupServer.cs +++ b/Jellyfin.Server/ServerSetupApp/SetupServer.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Serialization; using Jellyfin.Networking.Manager; +using Jellyfin.Server.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -27,6 +28,8 @@ using Microsoft.Extensions.Primitives; using Morestachio; using Morestachio.Framework.IO.SingleStream; using Morestachio.Rendering; +using Serilog; +using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Jellyfin.Server.ServerSetupApp; @@ -71,7 +74,7 @@ public sealed class SetupServer : IDisposable _configurationManager.RegisterConfiguration<NetworkConfigurationFactory>(); } - internal static ConcurrentQueue<StartupLogEntry>? LogQueue { get; set; } = new(); + internal static ConcurrentQueue<StartupLogTopic>? LogQueue { get; set; } = new(); /// <summary> /// Gets a value indicating whether Startup server is currently running. @@ -88,12 +91,12 @@ public sealed class SetupServer : IDisposable _startupUiRenderer = (await ParserOptionsBuilder.New() .WithTemplate(fileTemplate) .WithFormatter( - (StartupLogEntry logEntry, IEnumerable<StartupLogEntry> children) => + (StartupLogTopic logEntry, IEnumerable<StartupLogTopic> children) => { if (children.Any()) { var maxLevel = logEntry.LogLevel; - var stack = new Stack<StartupLogEntry>(children); + var stack = new Stack<StartupLogTopic>(children); while (maxLevel != LogLevel.Error && stack.Count > 0 && (logEntry = stack.Pop()) != null) // error is the highest inherted error level. { @@ -138,19 +141,25 @@ public sealed class SetupServer : IDisposable ThrowIfDisposed(); var retryAfterValue = TimeSpan.FromSeconds(5); - _startupServer = Host.CreateDefaultBuilder() + var config = _configurationManager.GetNetworkConfiguration()!; + _startupServer = Host.CreateDefaultBuilder(["hostBuilder:reloadConfigOnChange=false"]) .UseConsoleLifetime() + .UseSerilog() .ConfigureServices(serv => { + serv.AddSingleton(this); serv.AddHealthChecks() .AddCheck<SetupHealthcheck>("StartupCheck"); + serv.Configure<ForwardedHeadersOptions>(options => + { + ApiServiceCollectionExtensions.ConfigureForwardHeaders(config, options); + }); }) .ConfigureWebHostDefaults(webHostBuilder => { webHostBuilder .UseKestrel((builderContext, options) => { - var config = _configurationManager.GetNetworkConfiguration()!; var knownBindInterfaces = NetworkManager.GetInterfacesCore(_loggerFactory.CreateLogger<SetupServer>(), config.EnableIPv4, config.EnableIPv6); knownBindInterfaces = NetworkManager.FilterBindSettings(config, knownBindInterfaces.ToList(), config.EnableIPv4, config.EnableIPv6); var bindInterfaces = NetworkManager.GetAllBindInterfaces(false, _configurationManager, knownBindInterfaces, config.EnableIPv4, config.EnableIPv6); @@ -168,7 +177,7 @@ public sealed class SetupServer : IDisposable .Configure(app => { app.UseHealthChecks("/health"); - + app.UseForwardedHeaders(); app.Map("/startup/logger", loggerRoute => { loggerRoute.Run(async context => @@ -362,15 +371,4 @@ public sealed class SetupServer : IDisposable }); } } - - internal class StartupLogEntry - { - public LogLevel LogLevel { get; set; } - - public string? Content { get; set; } - - public DateTimeOffset DateOfCreation { get; set; } - - public List<StartupLogEntry> Children { get; set; } = []; - } } diff --git a/Jellyfin.Server/ServerSetupApp/StartupLogTopic.cs b/Jellyfin.Server/ServerSetupApp/StartupLogTopic.cs new file mode 100644 index 000000000..cd440a9b5 --- /dev/null +++ b/Jellyfin.Server/ServerSetupApp/StartupLogTopic.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.ObjectModel; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.ServerSetupApp; + +/// <summary> +/// Defines a topic for the Startup UI. +/// </summary> +public class StartupLogTopic +{ + /// <summary> + /// Gets or Sets the LogLevel. + /// </summary> + public LogLevel LogLevel { get; set; } + + /// <summary> + /// Gets or Sets the descriptor for the topic. + /// </summary> + public string? Content { get; set; } + + /// <summary> + /// Gets or sets the time the topic was created. + /// </summary> + public DateTimeOffset DateOfCreation { get; set; } + + /// <summary> + /// Gets the child items of this topic. + /// </summary> + public Collection<StartupLogTopic> Children { get; } = []; +} diff --git a/Jellyfin.Server/ServerSetupApp/StartupLogger.cs b/Jellyfin.Server/ServerSetupApp/StartupLogger.cs index 2b86dc0c1..0121854ce 100644 --- a/Jellyfin.Server/ServerSetupApp/StartupLogger.cs +++ b/Jellyfin.Server/ServerSetupApp/StartupLogger.cs @@ -1,56 +1,86 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using Jellyfin.Server.Migrations.Routines; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Jellyfin.Server.ServerSetupApp; /// <inheritdoc/> public class StartupLogger : IStartupLogger { - private readonly SetupServer.StartupLogEntry? _groupEntry; + private readonly StartupLogTopic? _topic; /// <summary> /// Initializes a new instance of the <see cref="StartupLogger"/> class. /// </summary> - public StartupLogger() + /// <param name="logger">The underlying base logger.</param> + public StartupLogger(ILogger logger) { - Loggers = []; + BaseLogger = logger; } /// <summary> /// Initializes a new instance of the <see cref="StartupLogger"/> class. /// </summary> - private StartupLogger(SetupServer.StartupLogEntry? groupEntry) : this() + /// <param name="logger">The underlying base logger.</param> + /// <param name="topic">The group for this logger.</param> + internal StartupLogger(ILogger logger, StartupLogTopic? topic) : this(logger) { - _groupEntry = groupEntry; + _topic = topic; } - internal static IStartupLogger Logger { get; } = new StartupLogger(); + internal static IStartupLogger Logger { get; set; } = new StartupLogger(NullLogger.Instance); - private List<ILogger> Loggers { get; set; } + /// <inheritdoc/> + public StartupLogTopic? Topic => _topic; + + /// <summary> + /// Gets or Sets the underlying base logger. + /// </summary> + protected ILogger BaseLogger { get; set; } /// <inheritdoc/> public IStartupLogger BeginGroup(FormattableString logEntry) { - var startupEntry = new SetupServer.StartupLogEntry() + return new StartupLogger(BaseLogger, AddToTopic(logEntry)); + } + + /// <inheritdoc/> + public IStartupLogger With(ILogger logger) + { + return new StartupLogger(logger, Topic); + } + + /// <inheritdoc/> + public IStartupLogger<TCategory> With<TCategory>(ILogger logger) + { + return new StartupLogger<TCategory>(logger, Topic); + } + + /// <inheritdoc/> + public IStartupLogger<TCategory> BeginGroup<TCategory>(FormattableString logEntry) + { + return new StartupLogger<TCategory>(BaseLogger, AddToTopic(logEntry)); + } + + private StartupLogTopic AddToTopic(FormattableString logEntry) + { + var startupEntry = new StartupLogTopic() { Content = logEntry.ToString(CultureInfo.InvariantCulture), DateOfCreation = DateTimeOffset.Now }; - if (_groupEntry is null) + if (Topic is null) { SetupServer.LogQueue?.Enqueue(startupEntry); } else { - _groupEntry.Children.Add(startupEntry); + Topic.Children.Add(startupEntry); } - return new StartupLogger(startupEntry); + return startupEntry; } /// <inheritdoc/> @@ -69,34 +99,26 @@ public class StartupLogger : IStartupLogger /// <inheritdoc/> public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { - foreach (var item in Loggers.Where(e => e.IsEnabled(logLevel))) + if (BaseLogger.IsEnabled(logLevel)) { - item.Log(logLevel, eventId, state, exception, formatter); + // if enabled allow the base logger also to receive the message + BaseLogger.Log(logLevel, eventId, state, exception, formatter); } - var startupEntry = new SetupServer.StartupLogEntry() + var startupEntry = new StartupLogTopic() { LogLevel = logLevel, Content = formatter(state, exception), DateOfCreation = DateTimeOffset.Now }; - if (_groupEntry is null) + if (Topic is null) { SetupServer.LogQueue?.Enqueue(startupEntry); } else { - _groupEntry.Children.Add(startupEntry); + Topic.Children.Add(startupEntry); } } - - /// <inheritdoc/> - public IStartupLogger With(ILogger logger) - { - return new StartupLogger(_groupEntry) - { - Loggers = [.. Loggers, logger] - }; - } } diff --git a/Jellyfin.Server/ServerSetupApp/StartupLoggerExtensions.cs b/Jellyfin.Server/ServerSetupApp/StartupLoggerExtensions.cs new file mode 100644 index 000000000..ada4b56a7 --- /dev/null +++ b/Jellyfin.Server/ServerSetupApp/StartupLoggerExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Globalization; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Jellyfin.Server.ServerSetupApp; + +internal static class StartupLoggerExtensions +{ + public static IServiceCollection RegisterStartupLogger(this IServiceCollection services) + { + return services + .AddTransient<IStartupLogger, StartupLogger<Startup>>() + .AddTransient(typeof(IStartupLogger<>), typeof(StartupLogger<>)); + } +} diff --git a/Jellyfin.Server/ServerSetupApp/StartupLoggerOfCategory.cs b/Jellyfin.Server/ServerSetupApp/StartupLoggerOfCategory.cs new file mode 100644 index 000000000..64da0ce88 --- /dev/null +++ b/Jellyfin.Server/ServerSetupApp/StartupLoggerOfCategory.cs @@ -0,0 +1,56 @@ +using System; +using System.Globalization; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.ServerSetupApp; + +/// <summary> +/// Startup logger for usage with DI that utilises an underlying logger from the DI. +/// </summary> +/// <typeparam name="TCategory">The category of the underlying logger.</typeparam> +#pragma warning disable SA1649 // File name should match first type name +public class StartupLogger<TCategory> : StartupLogger, IStartupLogger<TCategory> +#pragma warning restore SA1649 // File name should match first type name +{ + /// <summary> + /// Initializes a new instance of the <see cref="StartupLogger{TCategory}"/> class. + /// </summary> + /// <param name="logger">The injected base logger.</param> + public StartupLogger(ILogger<TCategory> logger) : base(logger) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="StartupLogger{TCategory}"/> class. + /// </summary> + /// <param name="logger">The underlying base logger.</param> + /// <param name="groupEntry">The group for this logger.</param> + internal StartupLogger(ILogger logger, StartupLogTopic? groupEntry) : base(logger, groupEntry) + { + } + + IStartupLogger<TCategory> IStartupLogger<TCategory>.BeginGroup(FormattableString logEntry) + { + var startupEntry = new StartupLogTopic() + { + Content = logEntry.ToString(CultureInfo.InvariantCulture), + DateOfCreation = DateTimeOffset.Now + }; + + if (Topic is null) + { + SetupServer.LogQueue?.Enqueue(startupEntry); + } + else + { + Topic.Children.Add(startupEntry); + } + + return new StartupLogger<TCategory>(BaseLogger, startupEntry); + } + + IStartupLogger<TCategory> IStartupLogger<TCategory>.With(ILogger logger) + { + return new StartupLogger<TCategory>(logger, Topic); + } +} diff --git a/Jellyfin.Server/ServerSetupApp/index.mstemplate.html b/Jellyfin.Server/ServerSetupApp/index.mstemplate.html index 747835b2a..523f38d74 100644 --- a/Jellyfin.Server/ServerSetupApp/index.mstemplate.html +++ b/Jellyfin.Server/ServerSetupApp/index.mstemplate.html @@ -204,6 +204,7 @@ </li> {{--| /DECLARE}} + {{#IF localNetworkRequest}} <div class="flex-col"> <ol class="action-list"> {{#FOREACH log IN logs.Reverse()}} @@ -211,6 +212,10 @@ {{/FOREACH}} </ol> </div> + {{#ELSE}} + <p>Please visit this page from your local network to view detailed startup logs.</p> + {{/ELSE}} + {{/IF}} </div> </body> |
