diff options
| -rw-r--r-- | Jellyfin.Server/Migrations/IMigrationRoutine.cs (renamed from Jellyfin.Server/Migrations/IUpdater.cs) | 8 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/MigrationRunner.cs | 24 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs | 93 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs | 2 | ||||
| -rw-r--r-- | Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs | 29 | ||||
| -rw-r--r-- | Jellyfin.Server/Program.cs | 14 |
6 files changed, 121 insertions, 49 deletions
diff --git a/Jellyfin.Server/Migrations/IUpdater.cs b/Jellyfin.Server/Migrations/IMigrationRoutine.cs index 9b749841c..20a3aa3d6 100644 --- a/Jellyfin.Server/Migrations/IUpdater.cs +++ b/Jellyfin.Server/Migrations/IMigrationRoutine.cs @@ -4,20 +4,20 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Migrations { /// <summary> - /// Interface that descibes a migration routine. + /// Interface that describes a migration routine. /// </summary> - internal interface IUpdater + internal interface IMigrationRoutine { /// <summary> /// Gets the name of the migration, must be unique. /// </summary> - public abstract string Name { get; } + public string Name { get; } /// <summary> /// Execute the migration routine. /// </summary> /// <param name="host">Host that hosts current version.</param> /// <param name="logger">Host logger.</param> - public abstract void Perform(CoreAppHost host, ILogger logger); + public void Perform(CoreAppHost host, ILogger logger); } } diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index ca4c79cfd..8bc29d8ac 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -13,9 +13,10 @@ namespace Jellyfin.Server.Migrations /// <summary> /// The list of known migrations, in order of applicability. /// </summary> - internal static readonly IUpdater[] Migrations = + internal static readonly IMigrationRoutine[] Migrations = { - new Routines.DisableTranscodingThrottling() + new Routines.DisableTranscodingThrottling(), + new Routines.CreateUserLoggingConfigFile() }; /// <summary> @@ -42,26 +43,27 @@ namespace Jellyfin.Server.Migrations for (var i = 0; i < Migrations.Length; i++) { - var updater = Migrations[i]; - if (applied.Contains(updater.Name)) + var migrationRoutine = Migrations[i]; + if (applied.Contains(migrationRoutine.Name)) { - logger.LogDebug("Skipping migration '{Name}' since it is already applied", updater.Name); + logger.LogDebug("Skipping migration {Name} as it is already applied", migrationRoutine.Name); continue; } - logger.LogInformation("Applying migration '{Name}'", updater.Name); + logger.LogInformation("Applying migration {Name}", migrationRoutine.Name); + try { - updater.Perform(host, logger); + migrationRoutine.Perform(host, logger); } catch (Exception ex) { - logger.LogError(ex, "Could not apply migration '{Name}'", updater.Name); - throw; + logger.LogError(ex, "Could not apply migration {Name}", migrationRoutine.Name); + continue; } - logger.LogInformation("Migration '{Name}' applied successfully", updater.Name); - applied.Add(updater.Name); + logger.LogInformation("Migration {Name} applied successfully", migrationRoutine.Name); + applied.Add(migrationRoutine.Name); } if (applied.Count > migrationOptions.Applied.Length) diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs new file mode 100644 index 000000000..6dbeb2776 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MediaBrowser.Common.Configuration; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// <summary> + /// Migration to initialize the user logging configuration file "logging.user.json". + /// If the deprecated logging.json file exists and has a custom config, it will be used as logging.user.json, + /// otherwise a blank file will be created. + /// </summary> + internal class CreateUserLoggingConfigFile : IMigrationRoutine + { + /// <summary> + /// An empty logging JSON configuration, which will be used as the default contents for the user settings config file. + /// </summary> + private const string EmptyLoggingConfig = @"{ ""Serilog"": { } }"; + + /// <summary> + /// File history for logging.json as existed during this migration creation. The contents for each has been minified. + /// </summary> + private readonly List<string> _defaultConfigHistory = new List<string> + { + // 9a6c27947353585391e211aa88b925f81e8cd7b9 + @"{""Serilog"":{""MinimumLevel"":{""Default"":""Information"",""Override"":{""Microsoft"":""Warning"",""System"":""Warning""}},""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // 71bdcd730705a714ee208eaad7290b7c68df3885 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // a44936f97f8afc2817d3491615a7cfe1e31c251c + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}", + // 7af3754a11ad5a4284f107997fb5419a010ce6f3 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}", + // 60691349a11f541958e0b2247c9abc13cb40c9fb + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}""}}]}}]}}", + // 65fe243afbcc4b596cf8726708c1965cd34b5f68 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + // 96c9af590494aa8137d5a061aaf1e68feee60b67 + @"{""Serilog"":{""MinimumLevel"":""Information"",""WriteTo"":[{""Name"":""Console"",""Args"":{""outputTemplate"":""[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}""}},{""Name"":""Async"",""Args"":{""configure"":[{""Name"":""File"",""Args"":{""path"":""%JELLYFIN_LOG_DIR%//log_.log"",""rollingInterval"":""Day"",""retainedFileCountLimit"":3,""rollOnFileSizeLimit"":true,""fileSizeLimitBytes"":100000000,""outputTemplate"":""[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}""}}]}}],""Enrich"":[""FromLogContext"",""WithThreadId""]}}", + }; + + /// <inheritdoc/> + public string Name => "CreateLoggingConfigHeirarchy"; + + /// <inheritdoc/> + public void Perform(CoreAppHost host, ILogger logger) + { + var logDirectory = host.Resolve<IApplicationPaths>().ConfigurationDirectoryPath; + var oldConfigPath = Path.Combine(logDirectory, "logging.json"); + var userConfigPath = Path.Combine(logDirectory, Program.LoggingConfigFileUser); + + // 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 + { + // Write an empty JSON file + File.WriteAllText(userConfigPath, EmptyLoggingConfig); + } + } + + /// <summary> + /// Check if the existing logging.json file should be migrated to logging.user.json. + /// </summary> + private bool ShouldKeepOldConfig(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 + .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/Migrations/Routines/DisableTranscodingThrottling.cs b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs index 936c3640e..279e7bbea 100644 --- a/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs +++ b/Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs @@ -10,7 +10,7 @@ namespace Jellyfin.Server.Migrations.Routines /// <summary> /// Disable transcode throttling for all installations since it is currently broken for certain video formats. /// </summary> - internal class DisableTranscodingThrottling : IUpdater + internal class DisableTranscodingThrottling : IMigrationRoutine { /// <inheritdoc/> public string Name => "DisableTranscodingThrottling"; diff --git a/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs b/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs deleted file mode 100644 index 501f8f865..000000000 --- a/Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs +++ /dev/null @@ -1,29 +0,0 @@ -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.Routines -{ - /// <summary> - /// Updater that takes care of bringing configuration up to 10.5.0 standards. - /// </summary> - internal class DisableZealousLogging : IUpdater - { - /// <inheritdoc/> - public string Name => "DisableZealousLogging"; - - /// <inheritdoc/> - // 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/Program.cs b/Jellyfin.Server/Program.cs index 027186105..2590fdb21 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -39,9 +39,14 @@ namespace Jellyfin.Server public static class Program { /// <summary> - /// The name of logging configuration file. + /// The name of logging configuration file containing application defaults. /// </summary> - public static readonly string LoggingConfigFile = "logging.json"; + public static readonly string LoggingConfigFileDefault = "logging.default.json"; + + /// <summary> + /// The name of the logging configuration file containing user override settings. + /// </summary> + 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<IConfiguration> 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,8 @@ namespace Jellyfin.Server return new ConfigurationBuilder() .SetBasePath(appPaths.ConfigurationDirectoryPath) .AddInMemoryCollection(ConfigurationOptions.Configuration) - .AddJsonFile(LoggingConfigFile, false, true) + .AddJsonFile(LoggingConfigFileDefault, optional: false, reloadOnChange: true) + .AddJsonFile(LoggingConfigFileUser, optional: true, reloadOnChange: true) .AddEnvironmentVariables("JELLYFIN_") .Build(); } |
