aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Server/Migrations/IMigrationRoutine.cs (renamed from Jellyfin.Server/Migrations/IUpdater.cs)8
-rw-r--r--Jellyfin.Server/Migrations/MigrationRunner.cs24
-rw-r--r--Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs93
-rw-r--r--Jellyfin.Server/Migrations/Routines/DisableTranscodingThrottling.cs2
-rw-r--r--Jellyfin.Server/Migrations/Routines/DisableZealousLogging.cs29
-rw-r--r--Jellyfin.Server/Program.cs14
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();
}