From 6e6e5ee9f24862c3d31b4679ed8786b2a0abb460 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Sat, 13 Mar 2021 17:44:29 -0600 Subject: [PATCH] Implemented a startup task that will instantiate all the DI so first API isn't having to eat that overhead. --- API/Controllers/SettingsController.cs | 26 ++++++++++- API/Data/LogLevelOptions.cs | 9 ++++ .../ApplicationServiceExtensions.cs | 4 ++ API/Interfaces/Services/IStartupTask.cs | 10 +++++ API/Services/TaskScheduler.cs | 4 +- API/Services/WarmupServiceStartupTask.cs | 44 +++++++++++++++++++ API/Startup.cs | 10 +++-- 7 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 API/Data/LogLevelOptions.cs create mode 100644 API/Interfaces/Services/IStartupTask.cs create mode 100644 API/Services/WarmupServiceStartupTask.cs diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs index 883c65a40..2e0bbbfeb 100644 --- a/API/Controllers/SettingsController.cs +++ b/API/Controllers/SettingsController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using API.Data; using API.DTOs; using API.Entities.Enums; using API.Extensions; @@ -9,6 +10,8 @@ using API.Helpers.Converters; using API.Interfaces; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace API.Controllers @@ -19,12 +22,14 @@ namespace API.Controllers private readonly ILogger _logger; private readonly IUnitOfWork _unitOfWork; private readonly ITaskScheduler _taskScheduler; + private readonly IConfiguration _configuration; - public SettingsController(ILogger logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler) + public SettingsController(ILogger logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler, IConfiguration configuration) { _logger = logger; _unitOfWork = unitOfWork; _taskScheduler = taskScheduler; + _configuration = configuration; } [HttpGet("")] @@ -51,6 +56,9 @@ namespace API.Controllers // We do not allow CacheDirectory changes, so we will ignore. var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync(); + + var logLevelOptions = new LogLevelOptions(); + _configuration.GetSection("Logging:LogLevel").Bind(logLevelOptions); foreach (var setting in currentSettings) { @@ -72,8 +80,15 @@ namespace API.Controllers Environment.SetEnvironmentVariable("KAVITA_PORT", setting.Value); _unitOfWork.SettingsRepository.Update(setting); } + + if (setting.Key == ServerSettingKey.LoggingLevel && updateSettingsDto.LoggingLevel + "" != setting.Value) + { + setting.Value = updateSettingsDto.LoggingLevel + ""; + _unitOfWork.SettingsRepository.Update(setting); + } } - + + _configuration.GetSection("Logging:LogLevel:Default").Value = updateSettingsDto.LoggingLevel + ""; if (!_unitOfWork.HasChanges()) return Ok("Nothing was updated"); if (!_unitOfWork.HasChanges() || !await _unitOfWork.Complete()) @@ -90,5 +105,12 @@ namespace API.Controllers { return Ok(CronConverter.Options); } + + [Authorize(Policy = "RequireAdminRole")] + [HttpGet("log-levels")] + public ActionResult> GetLogLevels() + { + return Ok(new string[] {"Trace", "Debug", "Information", "Warning", "Critical", "None"}); + } } } \ No newline at end of file diff --git a/API/Data/LogLevelOptions.cs b/API/Data/LogLevelOptions.cs new file mode 100644 index 000000000..dfdfd111f --- /dev/null +++ b/API/Data/LogLevelOptions.cs @@ -0,0 +1,9 @@ +namespace API.Data +{ + public class LogLevelOptions + { + public const string Logging = "LogLevel"; + + public string Default { get; set; } + } +} \ No newline at end of file diff --git a/API/Extensions/ApplicationServiceExtensions.cs b/API/Extensions/ApplicationServiceExtensions.cs index 09cc03a0d..f8b10a442 100644 --- a/API/Extensions/ApplicationServiceExtensions.cs +++ b/API/Extensions/ApplicationServiceExtensions.cs @@ -56,5 +56,9 @@ namespace API.Extensions return services; } + + public static IServiceCollection AddStartupTask(this IServiceCollection services) + where T : class, IStartupTask + => services.AddTransient(); } } \ No newline at end of file diff --git a/API/Interfaces/Services/IStartupTask.cs b/API/Interfaces/Services/IStartupTask.cs new file mode 100644 index 000000000..e2a99ecad --- /dev/null +++ b/API/Interfaces/Services/IStartupTask.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace API.Interfaces.Services +{ + public interface IStartupTask + { + Task ExecuteAsync(CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs index 42c45d465..e1c9912be 100644 --- a/API/Services/TaskScheduler.cs +++ b/API/Services/TaskScheduler.cs @@ -39,9 +39,9 @@ namespace API.Services _cleanupService = cleanupService; _directoryService = directoryService; - + //Hangfire.RecurringJob.RemoveIfExists(); ScheduleTasks(); - //JobStorage.Current.GetMonitoringApi(). + //JobStorage.Current.GetMonitoringApi().EnqueuedJobs() } diff --git a/API/Services/WarmupServiceStartupTask.cs b/API/Services/WarmupServiceStartupTask.cs new file mode 100644 index 000000000..fd9b1745b --- /dev/null +++ b/API/Services/WarmupServiceStartupTask.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using API.Interfaces.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace API.Services +{ + public class WarmupServicesStartupTask : IStartupTask + { + private readonly IServiceCollection _services; + private readonly IServiceProvider _provider; + public WarmupServicesStartupTask(IServiceCollection services, IServiceProvider provider) + { + _services = services; + _provider = provider; + } + + public Task ExecuteAsync(CancellationToken cancellationToken) + { + using (var scope = _provider.CreateScope()) + { + foreach (var singleton in GetServices(_services)) + { + scope.ServiceProvider.GetServices(singleton); + } + } + + return Task.CompletedTask; + } + + static IEnumerable GetServices(IServiceCollection services) + { + return services + .Where(descriptor => descriptor.ImplementationType != typeof(WarmupServicesStartupTask)) + .Where(descriptor => !descriptor.ServiceType.ContainsGenericParameters) + .Select(descriptor => descriptor.ServiceType) + .Distinct(); + } + } + +} \ No newline at end of file diff --git a/API/Startup.cs b/API/Startup.cs index e0333217c..4e9319b37 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -1,8 +1,10 @@ using System; using System.IO.Compression; using System.Linq; +using API.Data; using API.Extensions; using API.Middleware; +using API.Services; using Hangfire; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -12,7 +14,9 @@ using Microsoft.AspNetCore.ResponseCompression; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; namespace API @@ -42,7 +46,6 @@ namespace API { c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" }); }); - // This doesn't seem to work. services.AddResponseCompression(options => { options.Providers.Add(); @@ -60,7 +63,9 @@ namespace API services.AddResponseCaching(); - + services + .AddStartupTask() + .TryAddSingleton(services); } @@ -113,7 +118,6 @@ namespace API await next(); }); - app.UseEndpoints(endpoints => {