From a4e5a5ab314e10f0141dd6af0a1e1b08728a12fc Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 28 Feb 2020 23:18:22 +0100 Subject: Register configuration correctly with application using 'ConfigureAppConfiguration()' in WebHostBuilder Without this, the correct instance of IConfiguration is not injected into services that rely on it --- Jellyfin.Server/Program.cs | 74 ++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 26 deletions(-) (limited to 'Jellyfin.Server/Program.cs') diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 1dd598236..1ca6a8a77 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -101,10 +101,12 @@ namespace Jellyfin.Server // $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath); - IConfiguration appConfig = await CreateConfiguration(appPaths).ConfigureAwait(false); - - CreateLogger(appConfig, appPaths); + // Create an instance of the application configuration to use for application startup + await InitLoggingConfigFile(appPaths).ConfigureAwait(false); + IConfiguration appConfig = CreateAppConfiguration(appPaths); + // Initialize logging framework + InitializeLoggingFramework(appConfig, appPaths); _logger = _loggerFactory.CreateLogger("Main"); // Log uncaught exceptions to the logging instead of std error @@ -176,15 +178,15 @@ namespace Jellyfin.Server ServiceCollection serviceCollection = new ServiceCollection(); await appHost.InitAsync(serviceCollection).ConfigureAwait(false); - var host = CreateWebHostBuilder(appHost, serviceCollection).Build(); + var webHost = CreateWebHostBuilder(appHost, serviceCollection, appPaths).Build(); // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection. - appHost.ServiceProvider = host.Services; + appHost.ServiceProvider = webHost.Services; appHost.FindParts(); try { - await host.StartAsync().ConfigureAwait(false); + await webHost.StartAsync().ConfigureAwait(false); } catch { @@ -220,7 +222,7 @@ namespace Jellyfin.Server } } - private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection) + private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection, IApplicationPaths appPaths) { return new WebHostBuilder() .UseKestrel(options => @@ -260,6 +262,7 @@ namespace Jellyfin.Server } } }) + .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(appPaths)) .UseContentRoot(appHost.ContentRoot) .ConfigureServices(services => { @@ -432,37 +435,56 @@ namespace Jellyfin.Server return new ServerApplicationPaths(dataDir, logDir, configDir, cacheDir, webDir); } - private static async Task CreateConfiguration(IApplicationPaths appPaths) + /// + /// Initialize the logging configuration file using the bundled resource file as a default if it doesn't exist + /// already. + /// + private static async Task InitLoggingConfigFile(IApplicationPaths appPaths) { - const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; + // Do nothing if the config file already exists string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json"); - - if (!File.Exists(configPath)) + if (File.Exists(configPath)) { - // For some reason the csproj name is used instead of the assembly name - await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath); - if (resource == null) - { - throw new InvalidOperationException( - string.Format( - CultureInfo.InvariantCulture, - "Invalid resource path: '{0}'", - ResourcePath)); - } + return; + } - await using Stream dst = File.Open(configPath, FileMode.CreateNew); - await resource.CopyToAsync(dst).ConfigureAwait(false); + // Get a stream of the resource contents + // NOTE: The .csproj name is used instead of the assembly name in the resource path + const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; + await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath); + + // Fail if the resource does not exist + if (resource == null) + { + throw new InvalidOperationException( + string.Format(CultureInfo.InvariantCulture, "Invalid resource path: '{0}'", ResourcePath)); } + // Copy the resource contents to the expected file path for the config file + await using Stream dst = File.Open(configPath, FileMode.CreateNew); + await resource.CopyToAsync(dst).ConfigureAwait(false); + } + + private static IConfiguration CreateAppConfiguration(IApplicationPaths appPaths) + { return new ConfigurationBuilder() + .ConfigureAppConfiguration(appPaths) + .Build(); + } + + private static IConfigurationBuilder ConfigureAppConfiguration(this IConfigurationBuilder config, IApplicationPaths appPaths) + { + return config .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) .AddJsonFile("logging.json", false, true) - .AddEnvironmentVariables("JELLYFIN_") - .Build(); + .AddEnvironmentVariables("JELLYFIN_"); } - private static void CreateLogger(IConfiguration configuration, IApplicationPaths appPaths) + /// + /// Initialize Serilog using configuration and fall back to defaults on failure. + /// + private static void InitializeLoggingFramework(IConfiguration configuration, IApplicationPaths appPaths) { try { -- cgit v1.2.3 From 48f81180726df5a0ef6a64572a4277f3c6af6f9c Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 28 Feb 2020 23:28:15 +0100 Subject: Do not save a reference to the startup config in ApplicationHost --- Emby.Server.Implementations/ApplicationHost.cs | 18 +++++++----------- Jellyfin.Server/CoreAppHost.cs | 7 ++----- Jellyfin.Server/Program.cs | 9 ++++----- MediaBrowser.Common/IApplicationHost.cs | 6 ++++-- 4 files changed, 17 insertions(+), 23 deletions(-) (limited to 'Jellyfin.Server/Program.cs') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8ea188724..f3abf2a3e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -328,8 +328,6 @@ namespace Emby.Server.Implementations private IMediaSourceManager MediaSourceManager { get; set; } - private readonly IConfiguration _configuration; - /// /// Gets the installation manager. /// @@ -367,11 +365,8 @@ namespace Emby.Server.Implementations IStartupOptions options, IFileSystem fileSystem, IImageEncoder imageEncoder, - INetworkManager networkManager, - IConfiguration configuration) + INetworkManager networkManager) { - _configuration = configuration; - XmlSerializer = new MyXmlSerializer(); NetworkManager = networkManager; @@ -587,7 +582,8 @@ namespace Emby.Server.Implementations } } - public async Task InitAsync(IServiceCollection serviceCollection) + /// + public async Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig) { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -620,7 +616,7 @@ namespace Emby.Server.Implementations DiscoverTypes(); - await RegisterResources(serviceCollection).ConfigureAwait(false); + await RegisterResources(serviceCollection, startupConfig).ConfigureAwait(false); ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath; if (string.IsNullOrEmpty(ContentRoot)) @@ -659,7 +655,7 @@ namespace Emby.Server.Implementations /// /// Registers resources that classes will depend on /// - protected async Task RegisterResources(IServiceCollection serviceCollection) + protected async Task RegisterResources(IServiceCollection serviceCollection, IConfiguration startupConfig) { serviceCollection.AddMemoryCache(); @@ -762,7 +758,7 @@ namespace Emby.Server.Implementations ProcessFactory, LocalizationManager, () => SubtitleEncoder, - _configuration, + startupConfig, StartupOptions.FFmpegPath); serviceCollection.AddSingleton(MediaEncoder); @@ -784,7 +780,7 @@ namespace Emby.Server.Implementations this, LoggerFactory.CreateLogger(), ServerConfigurationManager, - _configuration, + startupConfig, NetworkManager, JsonSerializer, XmlSerializer, diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 8b4b61e29..ed5968ad6 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -23,23 +23,20 @@ namespace Jellyfin.Server /// The to be used by the . /// The to be used by the . /// The to be used by the . - /// The to be used by the . public CoreAppHost( ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory, StartupOptions options, IFileSystem fileSystem, IImageEncoder imageEncoder, - INetworkManager networkManager, - IConfiguration configuration) + INetworkManager networkManager) : base( applicationPaths, loggerFactory, options, fileSystem, imageEncoder, - networkManager, - configuration) + networkManager) { } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 1ca6a8a77..7ac3b0efb 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -103,10 +103,10 @@ namespace Jellyfin.Server // Create an instance of the application configuration to use for application startup await InitLoggingConfigFile(appPaths).ConfigureAwait(false); - IConfiguration appConfig = CreateAppConfiguration(appPaths); + IConfiguration startupConfig = CreateAppConfiguration(appPaths); // Initialize logging framework - InitializeLoggingFramework(appConfig, appPaths); + InitializeLoggingFramework(startupConfig, appPaths); _logger = _loggerFactory.CreateLogger("Main"); // Log uncaught exceptions to the logging instead of std error @@ -171,12 +171,11 @@ namespace Jellyfin.Server options, new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), GetImageEncoder(appPaths), - new NetworkManager(_loggerFactory.CreateLogger()), - appConfig); + new NetworkManager(_loggerFactory.CreateLogger())); try { ServiceCollection serviceCollection = new ServiceCollection(); - await appHost.InitAsync(serviceCollection).ConfigureAwait(false); + await appHost.InitAsync(serviceCollection, startupConfig).ConfigureAwait(false); var webHost = CreateWebHostBuilder(appHost, serviceCollection, appPaths).Build(); diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 68a24aaba..0e282cf53 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Updates; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace MediaBrowser.Common @@ -121,11 +122,12 @@ namespace MediaBrowser.Common void RemovePlugin(IPlugin plugin); /// - /// Inits this instance. + /// Initializes this instance. /// /// The service collection. + /// The configuration to use for initialization. /// A task. - Task InitAsync(IServiceCollection serviceCollection); + Task InitAsync(IServiceCollection serviceCollection, IConfiguration startupConfig); /// /// Creates the instance. -- cgit v1.2.3 From c376f4ca51b55db4dff8d5aa50b7a847870e41f5 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Tue, 3 Mar 2020 00:35:41 +0100 Subject: Register Serilog logging services correctly --- Emby.Server.Implementations/ApplicationHost.cs | 5 ++--- Jellyfin.Server/Program.cs | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'Jellyfin.Server/Program.cs') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8ea188724..1e7bbd704 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -672,9 +672,8 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(JsonSerializer); - serviceCollection.AddSingleton(LoggerFactory); - serviceCollection.AddLogging(); - serviceCollection.AddSingleton(Logger); + // TODO: Support for injecting ILogger should be deprecated in favour of ILogger and this removed + serviceCollection.AddSingleton(_logger); serviceCollection.AddSingleton(FileSystemManager); serviceCollection.AddSingleton(); diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 1dd598236..484e507a2 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -26,6 +26,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Serilog; +using Serilog.Events; using Serilog.Extensions.Logging; using SQLitePCL; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -260,6 +261,7 @@ namespace Jellyfin.Server } } }) + .UseSerilog() .UseContentRoot(appHost.ContentRoot) .ConfigureServices(services => { -- cgit v1.2.3 From 9eef0e8ca0e3359239ab68fcadbf2d65084f12e6 Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 17:41:32 +0300 Subject: Implement EnableThrottling migration for pre-10.5.0 to 10.5.0 or newer --- Jellyfin.Server/CoreAppHost.cs | 38 ++++++++++++----- Jellyfin.Server/Migrations.cs | 92 ++++++++++++++++++++++++++++++++++++++++++ Jellyfin.Server/Program.cs | 1 + 3 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 Jellyfin.Server/Migrations.cs (limited to 'Jellyfin.Server/Program.cs') diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 59285a510..cd5a2ce85 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -41,16 +41,6 @@ namespace Jellyfin.Server networkManager, configuration) { - var previousVersion = ConfigurationManager.CommonConfiguration.PreviousVersion; - if (ApplicationVersion.CompareTo(previousVersion) > 0) - { - Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); - - // TODO: run update routines - - ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; - ConfigurationManager.SaveConfiguration(); - } } /// @@ -67,5 +57,33 @@ namespace Jellyfin.Server /// protected override void ShutdownInternal() => Program.Shutdown(); + + /// + /// Runs the migration routines if necessary. + /// + public void TryMigrate() + { + var previousVersion = ConfigurationManager.CommonConfiguration.PreviousVersion; + switch (ApplicationVersion.CompareTo(previousVersion)) + { + case 1: + Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); + + Migrations.Run(this, Logger); + + ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; + ConfigurationManager.SaveConfiguration(); + break; + case 0: + // nothing to do, versions match + break; + case -1: + Logger.LogWarning("Version check shows Jellyfin was rolled back, use at your own risk: previous version={0}, current version={1}", previousVersion, ApplicationVersion); + // no "rollback" routines for now + ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; + ConfigurationManager.SaveConfiguration(); + break; + } + } } } diff --git a/Jellyfin.Server/Migrations.cs b/Jellyfin.Server/Migrations.cs new file mode 100644 index 000000000..95fea4ea5 --- /dev/null +++ b/Jellyfin.Server/Migrations.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server +{ + /// + /// The class that knows how migrate between different Jellyfin versions. + /// + internal static class Migrations + { + private static readonly IUpdater[] _migrations = + { + new Pre10_5() + }; + + /// + /// Interface that descibes a migration routine. + /// + private interface IUpdater + { + /// + /// Gets maximum version this Updater applies to. + /// If current version is greater or equal to it, skip the updater. + /// + public abstract Version Maximum { get; } + + /// + /// Execute the migration from version "from". + /// + /// Host that hosts current version. + /// Host logger. + /// Version to migrate from. + /// Whether configuration was changed. + public abstract bool Perform(CoreAppHost host, ILogger logger, Version from); + } + + /// + /// Run all needed migrations. + /// + /// CoreAppHost that hosts current version. + /// AppHost logger. + /// Whether anything was changed. + public static bool Run(CoreAppHost host, ILogger logger) + { + bool updated = false; + var version = host.ServerConfigurationManager.CommonConfiguration.PreviousVersion; + + for (var i = 0; i < _migrations.Length; i++) + { + var updater = _migrations[i]; + if (version.CompareTo(updater.Maximum) >= 0) + { + logger.LogDebug("Skipping updater {0} as current version {1} >= its maximum applicable version {2}", updater, version, updater.Maximum); + continue; + } + + if (updater.Perform(host, logger, version)) + { + updated = true; + } + + version = updater.Maximum; + } + + return updated; + } + + private class Pre10_5 : IUpdater + { + public Version Maximum { get => Version.Parse("10.5.0"); } + + public bool Perform(CoreAppHost host, ILogger logger, Version from) + { + // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues + var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); + if (encoding.EnableThrottling) + { + logger.LogInformation("Disabling transcoding throttling during migration"); + encoding.EnableThrottling = false; + + host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); + return true; + } + + return false; + } + } + } +} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 484e507a2..aa1bdb169 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -182,6 +182,7 @@ namespace Jellyfin.Server // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection. appHost.ServiceProvider = host.Services; appHost.FindParts(); + appHost.TryMigrate(); try { -- cgit v1.2.3 From ecaa7f8014666a474c87481471ce7cda7006165a Mon Sep 17 00:00:00 2001 From: Vasily Date: Thu, 5 Mar 2020 20:09:33 +0300 Subject: Improve migration logic --- Jellyfin.Server/CoreAppHost.cs | 28 ------------- .../Migrations/DisableTranscodingThrottling.cs | 32 ++++++++++++++ .../Migrations/DisableZealousLogging.cs | 29 +++++++++++++ Jellyfin.Server/Migrations/IUpdater.cs | 31 +++++++------- Jellyfin.Server/Migrations/MigrationOptions.cs | 23 ++++++++++ Jellyfin.Server/Migrations/MigrationRunner.cs | 49 +++++++++++++++------- Jellyfin.Server/Migrations/MigrationsFactory.cs | 20 +++++++++ Jellyfin.Server/Migrations/MigrationsListStore.cs | 19 +++++++++ Jellyfin.Server/Migrations/Pre_10_5.cs | 33 --------------- Jellyfin.Server/Program.cs | 11 +++-- 10 files changed, 178 insertions(+), 97 deletions(-) create mode 100644 Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs create mode 100644 Jellyfin.Server/Migrations/DisableZealousLogging.cs create mode 100644 Jellyfin.Server/Migrations/MigrationOptions.cs create mode 100644 Jellyfin.Server/Migrations/MigrationsFactory.cs create mode 100644 Jellyfin.Server/Migrations/MigrationsListStore.cs delete mode 100644 Jellyfin.Server/Migrations/Pre_10_5.cs (limited to 'Jellyfin.Server/Program.cs') diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 7f4bd3dea..8b4b61e29 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -57,33 +57,5 @@ namespace Jellyfin.Server /// protected override void ShutdownInternal() => Program.Shutdown(); - - /// - /// Runs the migration routines if necessary. - /// - public void TryMigrate() - { - var previousVersion = ConfigurationManager.CommonConfiguration.PreviousVersion; - switch (ApplicationVersion.CompareTo(previousVersion)) - { - case 1: - Logger.LogWarning("Version check shows Jellyfin was updated: previous version={0}, current version={1}", previousVersion, ApplicationVersion); - - Migrations.MigrationRunner.Run(this, Logger); - - ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; - ConfigurationManager.SaveConfiguration(); - break; - case 0: - // nothing to do, versions match - break; - case -1: - Logger.LogWarning("Version check shows Jellyfin was rolled back, use at your own risk: previous version={0}, current version={1}", previousVersion, ApplicationVersion); - // no "rollback" routines for now - ConfigurationManager.CommonConfiguration.PreviousVersion = ApplicationVersion; - ConfigurationManager.SaveConfiguration(); - break; - } - } } } diff --git a/Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs new file mode 100644 index 000000000..83624bdad --- /dev/null +++ b/Jellyfin.Server/Migrations/DisableTranscodingThrottling.cs @@ -0,0 +1,32 @@ +using System; +using System.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations +{ + /// + /// Updater that takes care of bringing configuration up to 10.5.0 standards. + /// + internal class DisableTranscodingThrottling : IUpdater + { + /// + public string Name => "DisableTranscodingThrottling"; + + /// + public void Perform(CoreAppHost host, ILogger logger) + { + // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues + var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); + if (encoding.EnableThrottling) + { + logger.LogInformation("Disabling transcoding throttling during migration"); + encoding.EnableThrottling = false; + + host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); + } + } + } +} diff --git a/Jellyfin.Server/Migrations/DisableZealousLogging.cs b/Jellyfin.Server/Migrations/DisableZealousLogging.cs new file mode 100644 index 000000000..a0a934d4a --- /dev/null +++ b/Jellyfin.Server/Migrations/DisableZealousLogging.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Serilog; +using ILogger = Microsoft.Extensions.Logging.ILogger; + +namespace Jellyfin.Server.Migrations +{ + /// + /// Updater that takes care of bringing configuration up to 10.5.0 standards. + /// + internal class DisableZealousLogging : IUpdater + { + /// + public string Name => "DisableZealousLogging"; + + /// + // This tones down logging from some components + public void Perform(CoreAppHost host, ILogger logger) + { + string configPath = Path.Combine(host.ServerConfigurationManager.ApplicationPaths.ConfigurationDirectoryPath, Program.LoggingConfigFile); + // TODO: fix up the config + throw new NotImplementedException("don't know how to fix logging yet"); + } + } +} diff --git a/Jellyfin.Server/Migrations/IUpdater.cs b/Jellyfin.Server/Migrations/IUpdater.cs index 60d970256..10ada73d5 100644 --- a/Jellyfin.Server/Migrations/IUpdater.cs +++ b/Jellyfin.Server/Migrations/IUpdater.cs @@ -3,24 +3,21 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations { + /// + /// Interface that descibes a migration routine. + /// + internal interface IUpdater + { /// - /// Interface that descibes a migration routine. + /// Gets the name of the migration, must be unique. /// - internal interface IUpdater - { - /// - /// Gets maximum version this Updater applies to. - /// If current version is greater or equal to it, skip the updater. - /// - public abstract Version Maximum { get; } + public abstract string Name { get; } - /// - /// Execute the migration from version "from". - /// - /// Host that hosts current version. - /// Host logger. - /// Version to migrate from. - /// Whether configuration was changed. - public abstract bool Perform(CoreAppHost host, ILogger logger, Version from); - } + /// + /// Execute the migration from version "from". + /// + /// Host that hosts current version. + /// Host logger. + public abstract void Perform(CoreAppHost host, ILogger logger); + } } diff --git a/Jellyfin.Server/Migrations/MigrationOptions.cs b/Jellyfin.Server/Migrations/MigrationOptions.cs new file mode 100644 index 000000000..b96288cc1 --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationOptions.cs @@ -0,0 +1,23 @@ +namespace Jellyfin.Server.Migrations +{ + /// + /// Configuration part that holds all migrations that were applied. + /// + public class MigrationOptions + { + /// + /// Initializes a new instance of the class. + /// + public MigrationOptions() + { + Applied = System.Array.Empty(); + } + +#pragma warning disable CA1819 // Properties should not return arrays + /// + /// Gets or sets he list of applied migration routine names. + /// + public string[] Applied { get; set; } +#pragma warning restore CA1819 // Properties should not return arrays + } +} diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index ad54fa38e..04d037852 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -1,3 +1,6 @@ +using System; +using System.Linq; +using MediaBrowser.Common.Configuration; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations @@ -5,42 +8,56 @@ namespace Jellyfin.Server.Migrations /// /// The class that knows how migrate between different Jellyfin versions. /// - public static class MigrationRunner + public sealed class MigrationRunner { - private static readonly IUpdater[] _migrations = + /// + /// The list of known migrations, in order of applicability. + /// + internal static readonly IUpdater[] Migrations = { - new Pre_10_5() + new DisableTranscodingThrottling(), + new DisableZealousLogging() }; /// /// Run all needed migrations. /// /// CoreAppHost that hosts current version. - /// AppHost logger. - /// Whether anything was changed. - public static bool Run(CoreAppHost host, ILogger logger) + /// Factory for making the logger. + public static void Run(CoreAppHost host, ILoggerFactory loggerFactory) { - bool updated = false; - var version = host.ServerConfigurationManager.CommonConfiguration.PreviousVersion; + var logger = loggerFactory.CreateLogger(); + var migrationOptions = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("migrations"); + var applied = migrationOptions.Applied.ToList(); - for (var i = 0; i < _migrations.Length; i++) + for (var i = 0; i < Migrations.Length; i++) { - var updater = _migrations[i]; - if (version.CompareTo(updater.Maximum) >= 0) + var updater = Migrations[i]; + if (applied.Contains(updater.Name)) { - logger.LogDebug("Skipping updater {0} as current version {1} >= its maximum applicable version {2}", updater, version, updater.Maximum); + logger.LogDebug("Skipping migration {0} as it is already applied", updater.Name); continue; } - if (updater.Perform(host, logger, version)) + try { - updated = true; + updater.Perform(host, logger); + } + catch (Exception ex) + { + logger.LogError(ex, "Cannot apply migration {0}", updater.Name); + continue; } - version = updater.Maximum; + applied.Add(updater.Name); } - return updated; + if (applied.Count > migrationOptions.Applied.Length) + { + logger.LogInformation("Some migrations were run, saving the state"); + migrationOptions.Applied = applied.ToArray(); + host.ServerConfigurationManager.SaveConfiguration("migrations", migrationOptions); + } } } } diff --git a/Jellyfin.Server/Migrations/MigrationsFactory.cs b/Jellyfin.Server/Migrations/MigrationsFactory.cs new file mode 100644 index 000000000..ed01dc646 --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationsFactory.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; + +namespace Jellyfin.Server.Migrations +{ + /// + /// A factory that teachs Jellyfin how to find a peristent file which lists all applied migrations. + /// + public class MigrationsFactory : IConfigurationFactory + { + /// + public IEnumerable GetConfigurations() + { + return new[] + { + new MigrationsListStore() + }; + } + } +} diff --git a/Jellyfin.Server/Migrations/MigrationsListStore.cs b/Jellyfin.Server/Migrations/MigrationsListStore.cs new file mode 100644 index 000000000..d91d602c1 --- /dev/null +++ b/Jellyfin.Server/Migrations/MigrationsListStore.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Common.Configuration; + +namespace Jellyfin.Server.Migrations +{ + /// + /// A configuration that lists all the migration routines that were applied. + /// + public class MigrationsListStore : ConfigurationStore + { + /// + /// Initializes a new instance of the class. + /// + public MigrationsListStore() + { + ConfigurationType = typeof(MigrationOptions); + Key = "migrations"; + } + } +} diff --git a/Jellyfin.Server/Migrations/Pre_10_5.cs b/Jellyfin.Server/Migrations/Pre_10_5.cs deleted file mode 100644 index 5389a2ad9..000000000 --- a/Jellyfin.Server/Migrations/Pre_10_5.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Server.Migrations -{ - /// - /// Updater that takes care of bringing configuration up to 10.5.0 standards. - /// - internal class Pre_10_5 : IUpdater - { - /// - public Version Maximum { get => Version.Parse("10.5.0"); } - - /// - public bool Perform(CoreAppHost host, ILogger logger, Version from) - { - // Set EnableThrottling to false as it wasn't used before, and in 10.5.0 it may introduce issues - var encoding = ((IConfigurationManager)host.ServerConfigurationManager).GetConfiguration("encoding"); - if (encoding.EnableThrottling) - { - logger.LogInformation("Disabling transcoding throttling during migration"); - encoding.EnableThrottling = false; - - host.ServerConfigurationManager.SaveConfiguration("encoding", encoding); - return true; - } - - return false; - } - } -} diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index aa1bdb169..027186105 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -38,6 +38,11 @@ namespace Jellyfin.Server /// public static class Program { + /// + /// The name of logging configuration file. + /// + public static readonly string LoggingConfigFile = "logging.json"; + private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); private static ILogger _logger = NullLogger.Instance; @@ -182,7 +187,7 @@ namespace Jellyfin.Server // A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection. appHost.ServiceProvider = host.Services; appHost.FindParts(); - appHost.TryMigrate(); + Migrations.MigrationRunner.Run(appHost, _loggerFactory); try { @@ -438,7 +443,7 @@ namespace Jellyfin.Server private static async Task CreateConfiguration(IApplicationPaths appPaths) { const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; - string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, "logging.json"); + string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFile); if (!File.Exists(configPath)) { @@ -460,7 +465,7 @@ namespace Jellyfin.Server return new ConfigurationBuilder() .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) - .AddJsonFile("logging.json", false, true) + .AddJsonFile(LoggingConfigFile, false, true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } -- cgit v1.2.3 From f2fdf50b3b7138f47e1777e31ef97b737775fb63 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 6 Mar 2020 19:07:34 +0100 Subject: Create separate constants for the two logging file names --- Jellyfin.Server/Program.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server/Program.cs') diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 027186105..970443c8b 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -39,9 +39,14 @@ namespace Jellyfin.Server public static class Program { /// - /// The name of logging configuration file. + /// The name of logging configuration file containing application defaults. /// - public static readonly string LoggingConfigFile = "logging.json"; + public static readonly string LoggingConfigFileDefault = "logging.default.json"; + + /// + /// The name of the logging configuration file containing user override settings. + /// + public static readonly string LoggingConfigFileUser = "logging.user.json"; private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); @@ -443,7 +448,7 @@ namespace Jellyfin.Server private static async Task CreateConfiguration(IApplicationPaths appPaths) { const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; - string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFile); + string configPath = Path.Combine(appPaths.ConfigurationDirectoryPath, LoggingConfigFileDefault); if (!File.Exists(configPath)) { @@ -465,7 +470,7 @@ namespace Jellyfin.Server return new ConfigurationBuilder() .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) - .AddJsonFile(LoggingConfigFile, false, true) + .AddJsonFile(LoggingConfigFileDefault, false, true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } -- cgit v1.2.3 From 6660006f01aee44ea33d1539000c5e4ea06e1115 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Fri, 6 Mar 2020 19:28:36 +0100 Subject: Load user logging config file into application configuration --- Jellyfin.Server/Program.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server/Program.cs') diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 970443c8b..2590fdb21 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -470,7 +470,8 @@ namespace Jellyfin.Server return new ConfigurationBuilder() .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) - .AddJsonFile(LoggingConfigFileDefault, false, true) + .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true) + .AddJsonFile(LoggingConfigFileUser, optional: true, reloadOnChange: true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } -- cgit v1.2.3 From 8dbb1c92573ba5cf3e4f8d953ffd6083a8ccbde4 Mon Sep 17 00:00:00 2001 From: Mark Monteiro Date: Sun, 8 Mar 2020 15:46:13 +0100 Subject: Use logging.json instead of logging.user.json for override settings --- .../Routines/CreateUserLoggingConfigFile.cs | 45 ++++++---------------- Jellyfin.Server/Program.cs | 6 +-- 2 files changed, 14 insertions(+), 37 deletions(-) (limited to 'Jellyfin.Server/Program.cs') diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 6dbeb2776..834099ea0 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -15,11 +15,6 @@ namespace Jellyfin.Server.Migrations.Routines /// internal class CreateUserLoggingConfigFile : IMigrationRoutine { - /// - /// An empty logging JSON configuration, which will be used as the default contents for the user settings config file. - /// - private const string EmptyLoggingConfig = @"{ ""Serilog"": { } }"; - /// /// File history for logging.json as existed during this migration creation. The contents for each has been minified. /// @@ -48,46 +43,28 @@ namespace Jellyfin.Server.Migrations.Routines public void Perform(CoreAppHost host, ILogger logger) { var logDirectory = host.Resolve().ConfigurationDirectoryPath; - var oldConfigPath = Path.Combine(logDirectory, "logging.json"); - var userConfigPath = Path.Combine(logDirectory, Program.LoggingConfigFileUser); + var existingConfigPath = Path.Combine(logDirectory, "logging.json"); - // Check if there are existing settings in the old "logging.json" file that should be migrated - bool shouldMigrateOldFile = ShouldKeepOldConfig(oldConfigPath); - - // Create the user settings file "logging.user.json" - if (shouldMigrateOldFile) - { - // Use the existing logging.json file - File.Copy(oldConfigPath, userConfigPath); - } - else + // If the existing logging.json config file is unmodified, then 'reset' it by moving it to 'logging.old.json' + // NOTE: This config file has 'reloadOnChange: true', so this change will take effect immediately even though it has already been loaded + if (File.Exists(existingConfigPath) && ExistingConfigUnmodified(existingConfigPath)) { - // Write an empty JSON file - File.WriteAllText(userConfigPath, EmptyLoggingConfig); + File.Move(existingConfigPath, Path.Combine(logDirectory, "logging.old.json")); } } /// - /// Check if the existing logging.json file should be migrated to logging.user.json. + /// Check if the existing logging.json file has not been modified by the user by comparing it to all the + /// versions in our git history. Until now, the file has never been migrated after first creation so users + /// could have any version from the git history. /// - private bool ShouldKeepOldConfig(string oldConfigPath) + /// does not exist or could not be read. + private bool ExistingConfigUnmodified(string oldConfigPath) { - // Cannot keep the old logging file if it doesn't exist - if (!File.Exists(oldConfigPath)) - { - return false; - } - - // Check if the existing logging.json file has been modified by the user by comparing it to all the - // versions in our git history. Until now, the file has never been migrated after first creation so users - // could have any version from the git history. var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath)); - var existingConfigIsUnmodified = _defaultConfigHistory + return _defaultConfigHistory .Select(historicalConfigText => JToken.Parse(historicalConfigText)) .Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson)); - - // The existing config file should be kept and used only if it has been modified by the user - return !existingConfigIsUnmodified; } } } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 2590fdb21..7c3d0f277 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -44,9 +44,9 @@ namespace Jellyfin.Server public static readonly string LoggingConfigFileDefault = "logging.default.json"; /// - /// The name of the logging configuration file containing user override settings. + /// The name of the logging configuration file containing the system-specific override settings. /// - public static readonly string LoggingConfigFileUser = "logging.user.json"; + public static readonly string LoggingConfigFileSystem = "logging.json"; private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); @@ -471,7 +471,7 @@ namespace Jellyfin.Server .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true) - .AddJsonFile(LoggingConfigFileUser, optional: true, reloadOnChange: true) + .AddJsonFile(LoggingConfigFileSystem, optional: true, reloadOnChange: true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } -- cgit v1.2.3