add minimum log level setting (#877)
This commit is contained in:
@@ -11,6 +11,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- In previous versions, some libraries would incorrectly display only one item
|
- In previous versions, some libraries would incorrectly display only one item
|
||||||
- Properly display old versions of renamed items in trash
|
- Properly display old versions of renamed items in trash
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Add `Minimum Log Level` option to `Settings` page
|
||||||
|
- Other methods of configuring the log level will no longer work
|
||||||
|
|
||||||
## [0.6.2-beta] - 2022-06-18
|
## [0.6.2-beta] - 2022-06-18
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix content repeating for up to a minute near the top of every hour
|
- Fix content repeating for up to a minute near the top of every hour
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
using ErsatzTV.Core;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Configuration;
|
||||||
|
|
||||||
|
public record UpdateGeneralSettings(GeneralSettingsViewModel GeneralSettings) : IRequest<Either<BaseError, Unit>>;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using ErsatzTV.Core;
|
||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using Serilog.Core;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Configuration;
|
||||||
|
|
||||||
|
public class UpdateGeneralSettingsHandler : IRequestHandler<UpdateGeneralSettings, Either<BaseError, Unit>>
|
||||||
|
{
|
||||||
|
private readonly IConfigElementRepository _configElementRepository;
|
||||||
|
private readonly LoggingLevelSwitch _loggingLevelSwitch;
|
||||||
|
|
||||||
|
public UpdateGeneralSettingsHandler(
|
||||||
|
LoggingLevelSwitch loggingLevelSwitch,
|
||||||
|
IConfigElementRepository configElementRepository)
|
||||||
|
{
|
||||||
|
_loggingLevelSwitch = loggingLevelSwitch;
|
||||||
|
_configElementRepository = configElementRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Either<BaseError, Unit>> Handle(
|
||||||
|
UpdateGeneralSettings request,
|
||||||
|
CancellationToken cancellationToken) => await ApplyUpdate(request.GeneralSettings);
|
||||||
|
|
||||||
|
private async Task<Unit> ApplyUpdate(GeneralSettingsViewModel generalSettings)
|
||||||
|
{
|
||||||
|
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, generalSettings.MinimumLogLevel);
|
||||||
|
_loggingLevelSwitch.MinimumLevel = generalSettings.MinimumLogLevel;
|
||||||
|
|
||||||
|
return Unit.Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Configuration;
|
||||||
|
|
||||||
|
public class GeneralSettingsViewModel
|
||||||
|
{
|
||||||
|
public LogEventLevel MinimumLogLevel { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace ErsatzTV.Application.Configuration;
|
||||||
|
|
||||||
|
public record GetGeneralSettings : IRequest<GeneralSettingsViewModel>;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Configuration;
|
||||||
|
|
||||||
|
public class GetGeneralSettingsHandler : IRequestHandler<GetGeneralSettings, GeneralSettingsViewModel>
|
||||||
|
{
|
||||||
|
private readonly IConfigElementRepository _configElementRepository;
|
||||||
|
|
||||||
|
public GetGeneralSettingsHandler(IConfigElementRepository configElementRepository) =>
|
||||||
|
_configElementRepository = configElementRepository;
|
||||||
|
|
||||||
|
public async Task<GeneralSettingsViewModel> Handle(GetGeneralSettings request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Option<LogEventLevel> maybeLogLevel =
|
||||||
|
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel);
|
||||||
|
|
||||||
|
return new GeneralSettingsViewModel
|
||||||
|
{
|
||||||
|
MinimumLogLevel = await maybeLogLevel.IfNoneAsync(LogEventLevel.Information)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ public class ConfigElementKey
|
|||||||
|
|
||||||
public string Key { get; }
|
public string Key { get; }
|
||||||
|
|
||||||
|
public static ConfigElementKey MinimumLogLevel => new("log.minimum_level");
|
||||||
public static ConfigElementKey FFmpegPath => new("ffmpeg.ffmpeg_path");
|
public static ConfigElementKey FFmpegPath => new("ffmpeg.ffmpeg_path");
|
||||||
public static ConfigElementKey FFprobePath => new("ffmpeg.ffprobe_path");
|
public static ConfigElementKey FFprobePath => new("ffmpeg.ffprobe_path");
|
||||||
public static ConfigElementKey FFmpegDefaultProfileId => new("ffmpeg.default_profile_id");
|
public static ConfigElementKey FFmpegDefaultProfileId => new("ffmpeg.default_profile_id");
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class ConfigElementRepository : IConfigElementRepository
|
|||||||
|
|
||||||
public async Task<Unit> Upsert<T>(ConfigElementKey configElementKey, T value)
|
public async Task<Unit> Upsert<T>(ConfigElementKey configElementKey, T value)
|
||||||
{
|
{
|
||||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
Option<ConfigElement> maybeElement = await dbContext.ConfigElements
|
Option<ConfigElement> maybeElement = await dbContext.ConfigElements
|
||||||
.SelectOneAsync(c => c.Key, c => c.Key == configElementKey.Key);
|
.SelectOneAsync(c => c.Key, c => c.Key == configElementKey.Key);
|
||||||
@@ -42,7 +42,7 @@ public class ConfigElementRepository : IConfigElementRepository
|
|||||||
|
|
||||||
public async Task<Option<ConfigElement>> Get(ConfigElementKey key)
|
public async Task<Option<ConfigElement>> Get(ConfigElementKey key)
|
||||||
{
|
{
|
||||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
return await dbContext.ConfigElements
|
return await dbContext.ConfigElements
|
||||||
.OrderBy(ce => ce.Key)
|
.OrderBy(ce => ce.Key)
|
||||||
.SingleOrDefaultAsync(ce => ce.Key == key.Key)
|
.SingleOrDefaultAsync(ce => ce.Key == key.Key)
|
||||||
@@ -50,18 +50,27 @@ public class ConfigElementRepository : IConfigElementRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task<Option<T>> GetValue<T>(ConfigElementKey key) =>
|
public Task<Option<T>> GetValue<T>(ConfigElementKey key) =>
|
||||||
Get(key).MapT(ce => (T)Convert.ChangeType(ce.Value, typeof(T)));
|
Get(key).MapT(
|
||||||
|
ce =>
|
||||||
|
{
|
||||||
|
if (typeof(T).IsEnum)
|
||||||
|
{
|
||||||
|
return (T)Enum.Parse(typeof(T), ce.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T)Convert.ChangeType(ce.Value, typeof(T));
|
||||||
|
});
|
||||||
|
|
||||||
public async Task Delete(ConfigElement configElement)
|
public async Task Delete(ConfigElement configElement)
|
||||||
{
|
{
|
||||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
dbContext.ConfigElements.Remove(configElement);
|
dbContext.ConfigElements.Remove(configElement);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Unit> Delete(ConfigElementKey configElementKey)
|
public async Task<Unit> Delete(ConfigElementKey configElementKey)
|
||||||
{
|
{
|
||||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||||
Option<ConfigElement> maybeExisting = await dbContext.ConfigElements
|
Option<ConfigElement> maybeExisting = await dbContext.ConfigElements
|
||||||
.SelectOneAsync(ce => ce.Key, ce => ce.Key == configElementKey.Key);
|
.SelectOneAsync(ce => ce.Key, ce => ce.Key == configElementKey.Key);
|
||||||
foreach (ConfigElement element in maybeExisting)
|
foreach (ConfigElement element in maybeExisting)
|
||||||
|
|||||||
+108
-68
@@ -6,6 +6,7 @@
|
|||||||
@using ErsatzTV.Application.Watermarks
|
@using ErsatzTV.Application.Watermarks
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using ErsatzTV.Application.Configuration
|
@using ErsatzTV.Application.Configuration
|
||||||
|
@using Serilog.Events
|
||||||
@using ErsatzTV.Core.Domain.Filler
|
@using ErsatzTV.Core.Domain.Filler
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@inject IMediator _mediator
|
@inject IMediator _mediator
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
@inject ILogger<Settings> _logger
|
@inject ILogger<Settings> _logger
|
||||||
|
|
||||||
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8" Style="display: flex; flex-direction: row">
|
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8" Style="display: flex; flex-direction: row">
|
||||||
<div style="display: flex; flex-direction: row; flex-wrap: wrap">
|
<MudGrid>
|
||||||
<MudCard Class="mr-6 mb-6" Style="max-width: 400px">
|
<MudCard Class="mr-6 mb-6" Style="max-width: 400px">
|
||||||
<MudCardHeader>
|
<MudCardHeader>
|
||||||
<CardHeaderContent>
|
<CardHeaderContent>
|
||||||
@@ -100,73 +101,98 @@
|
|||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_success)" OnClick="@(_ => SaveFFmpegSettings())">Save Settings</MudButton>
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_success)" OnClick="@(_ => SaveFFmpegSettings())">Save Settings</MudButton>
|
||||||
</MudCardActions>
|
</MudCardActions>
|
||||||
</MudCard>
|
</MudCard>
|
||||||
<MudCard Class="mr-6 mb-auto" Style="width: 350px">
|
<MudStack Class="mr-6">
|
||||||
<MudCardHeader>
|
<MudCard Class="mb-6" Style="width: 350px">
|
||||||
<CardHeaderContent>
|
<MudCardHeader>
|
||||||
<MudText Typo="Typo.h6">HDHomeRun Settings</MudText>
|
<CardHeaderContent>
|
||||||
</CardHeaderContent>
|
<MudText Typo="Typo.h6">General Settings</MudText>
|
||||||
</MudCardHeader>
|
</CardHeaderContent>
|
||||||
<MudCardContent>
|
</MudCardHeader>
|
||||||
<MudForm @bind-IsValid="@_hdhrSuccess">
|
<MudCardContent>
|
||||||
<MudTextField T="int" Label="Tuner Count" @bind-Value="_tunerCount" Validation="@(new Func<int, string>(ValidateTunerCount))" Required="true" RequiredError="Tuner count is required!"/>
|
<MudForm>
|
||||||
</MudForm>
|
<MudSelect Class="mt-3"
|
||||||
</MudCardContent>
|
Label="Minimum Log Level"
|
||||||
<MudCardActions>
|
@bind-Value="_generalSettings.MinimumLogLevel"
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_hdhrSuccess)" OnClick="@(_ => SaveHDHRSettings())">Save Settings</MudButton>
|
For="@(() => _generalSettings.MinimumLogLevel)">
|
||||||
</MudCardActions>
|
<MudSelectItem Value="@LogEventLevel.Debug">Debug</MudSelectItem>
|
||||||
</MudCard>
|
<MudSelectItem Value="@LogEventLevel.Information">Information</MudSelectItem>
|
||||||
<MudCard Class="mr-6" Style="width: 350px">
|
<MudSelectItem Value="@LogEventLevel.Warning">Warning</MudSelectItem>
|
||||||
<MudCardHeader>
|
<MudSelectItem Value="@LogEventLevel.Error">Error</MudSelectItem>
|
||||||
<CardHeaderContent>
|
</MudSelect>
|
||||||
<MudText Typo="Typo.h6">Scanner Settings</MudText>
|
</MudForm>
|
||||||
</CardHeaderContent>
|
</MudCardContent>
|
||||||
</MudCardHeader>
|
<MudCardActions>
|
||||||
<MudCardContent>
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => SaveGeneralSettings())">Save Settings</MudButton>
|
||||||
<MudForm @bind-IsValid="@_scannerSuccess">
|
</MudCardActions>
|
||||||
<MudTextField T="int"
|
</MudCard>
|
||||||
Label="Library Refresh Interval"
|
<MudCard Class="mb-6" Style="width: 350px">
|
||||||
@bind-Value="_libraryRefreshInterval"
|
<MudCardHeader>
|
||||||
Validation="@(new Func<int, string>(ValidateLibraryRefreshInterval))"
|
<CardHeaderContent>
|
||||||
Required="true"
|
<MudText Typo="Typo.h6">HDHomeRun Settings</MudText>
|
||||||
RequiredError="Library refresh interval is required!"
|
</CardHeaderContent>
|
||||||
Adornment="Adornment.End"
|
</MudCardHeader>
|
||||||
AdornmentText="Hours"/>
|
<MudCardContent>
|
||||||
</MudForm>
|
<MudForm @bind-IsValid="@_hdhrSuccess">
|
||||||
</MudCardContent>
|
<MudTextField T="int" Label="Tuner Count" @bind-Value="_tunerCount" Validation="@(new Func<int, string>(ValidateTunerCount))" Required="true" RequiredError="Tuner count is required!"/>
|
||||||
<MudCardActions>
|
</MudForm>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_scannerSuccess)" OnClick="@(_ => SaveScannerSettings())">Save Settings</MudButton>
|
</MudCardContent>
|
||||||
</MudCardActions>
|
<MudCardActions>
|
||||||
</MudCard>
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_hdhrSuccess)" OnClick="@(_ => SaveHDHRSettings())">Save Settings</MudButton>
|
||||||
<MudCard Style="width: 350px">
|
</MudCardActions>
|
||||||
<MudCardHeader>
|
</MudCard>
|
||||||
<CardHeaderContent>
|
<MudCard Class="mb-6" Style="width: 350px">
|
||||||
<MudText Typo="Typo.h6">Playout Settings</MudText>
|
<MudCardHeader>
|
||||||
</CardHeaderContent>
|
<CardHeaderContent>
|
||||||
</MudCardHeader>
|
<MudText Typo="Typo.h6">Scanner Settings</MudText>
|
||||||
<MudCardContent>
|
</CardHeaderContent>
|
||||||
<MudForm @bind-IsValid="@_playoutSuccess">
|
</MudCardHeader>
|
||||||
<MudTextField T="int"
|
<MudCardContent>
|
||||||
Label="Days To Build"
|
<MudForm @bind-IsValid="@_scannerSuccess">
|
||||||
@bind-Value="_playoutSettings.DaysToBuild"
|
<MudTextField T="int"
|
||||||
Validation="@(new Func<int, string>(ValidatePlayoutDaysToBuild))"
|
Label="Library Refresh Interval"
|
||||||
Required="true"
|
@bind-Value="_libraryRefreshInterval"
|
||||||
RequiredError="Days to build is required!"
|
Validation="@(new Func<int, string>(ValidateLibraryRefreshInterval))"
|
||||||
Adornment="Adornment.End"
|
Required="true"
|
||||||
AdornmentText="Days"/>
|
RequiredError="Library refresh interval is required!"
|
||||||
<MudElement HtmlTag="div" Class="mt-3">
|
Adornment="Adornment.End"
|
||||||
<MudTooltip Text="Controls whether file-not-found or unavailable items should be included in playouts">
|
AdornmentText="Hours"/>
|
||||||
<MudCheckBox Label="Skip Missing Items"
|
</MudForm>
|
||||||
@bind-Checked="_playoutSettings.SkipMissingItems"
|
</MudCardContent>
|
||||||
For="@(() => _playoutSettings.SkipMissingItems)"/>
|
<MudCardActions>
|
||||||
</MudTooltip>
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_scannerSuccess)" OnClick="@(_ => SaveScannerSettings())">Save Settings</MudButton>
|
||||||
</MudElement>
|
</MudCardActions>
|
||||||
</MudForm>
|
</MudCard>
|
||||||
</MudCardContent>
|
<MudCard Class="mb-6" Style="width: 350px">
|
||||||
<MudCardActions>
|
<MudCardHeader>
|
||||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_playoutSuccess)" OnClick="@(_ => SavePlayoutSettings())">Save Settings</MudButton>
|
<CardHeaderContent>
|
||||||
</MudCardActions>
|
<MudText Typo="Typo.h6">Playout Settings</MudText>
|
||||||
</MudCard>
|
</CardHeaderContent>
|
||||||
</div>
|
</MudCardHeader>
|
||||||
|
<MudCardContent>
|
||||||
|
<MudForm @bind-IsValid="@_playoutSuccess">
|
||||||
|
<MudTextField T="int"
|
||||||
|
Label="Days To Build"
|
||||||
|
@bind-Value="_playoutSettings.DaysToBuild"
|
||||||
|
Validation="@(new Func<int, string>(ValidatePlayoutDaysToBuild))"
|
||||||
|
Required="true"
|
||||||
|
RequiredError="Days to build is required!"
|
||||||
|
Adornment="Adornment.End"
|
||||||
|
AdornmentText="Days"/>
|
||||||
|
<MudElement HtmlTag="div" Class="mt-3">
|
||||||
|
<MudTooltip Text="Controls whether file-not-found or unavailable items should be included in playouts">
|
||||||
|
<MudCheckBox Label="Skip Missing Items"
|
||||||
|
@bind-Checked="_playoutSettings.SkipMissingItems"
|
||||||
|
For="@(() => _playoutSettings.SkipMissingItems)"/>
|
||||||
|
</MudTooltip>
|
||||||
|
</MudElement>
|
||||||
|
</MudForm>
|
||||||
|
</MudCardContent>
|
||||||
|
<MudCardActions>
|
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" Disabled="@(!_playoutSuccess)" OnClick="@(_ => SavePlayoutSettings())">Save Settings</MudButton>
|
||||||
|
</MudCardActions>
|
||||||
|
</MudCard>
|
||||||
|
</MudStack>
|
||||||
|
</MudGrid>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
@@ -184,6 +210,7 @@
|
|||||||
private int _tunerCount;
|
private int _tunerCount;
|
||||||
private int _libraryRefreshInterval;
|
private int _libraryRefreshInterval;
|
||||||
private PlayoutSettingsViewModel _playoutSettings;
|
private PlayoutSettingsViewModel _playoutSettings;
|
||||||
|
private GeneralSettingsViewModel _generalSettings;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
@@ -207,6 +234,7 @@
|
|||||||
_scannerSuccess = _libraryRefreshInterval > 0;
|
_scannerSuccess = _libraryRefreshInterval > 0;
|
||||||
_playoutSettings = await _mediator.Send(new GetPlayoutSettings(), _cts.Token);
|
_playoutSettings = await _mediator.Send(new GetPlayoutSettings(), _cts.Token);
|
||||||
_playoutSuccess = _playoutSettings.DaysToBuild > 0;
|
_playoutSuccess = _playoutSettings.DaysToBuild > 0;
|
||||||
|
_generalSettings = await _mediator.Send(new GetGeneralSettings(), _cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ValidatePathExists(string path) => !File.Exists(path) ? "Path does not exist" : null;
|
private static string ValidatePathExists(string path) => !File.Exists(path) ? "Path does not exist" : null;
|
||||||
@@ -274,4 +302,16 @@
|
|||||||
Right: _ => _snackbar.Add("Successfully saved playout settings", Severity.Success));
|
Right: _ => _snackbar.Add("Successfully saved playout settings", Severity.Success));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveGeneralSettings()
|
||||||
|
{
|
||||||
|
Either<BaseError, Unit> result = await _mediator.Send(new UpdateGeneralSettings(_generalSettings), _cts.Token);
|
||||||
|
result.Match(
|
||||||
|
Left: error =>
|
||||||
|
{
|
||||||
|
_snackbar.Add(error.Value, Severity.Error);
|
||||||
|
_logger.LogError("Unexpected error saving general settings: {Error}", error.Value);
|
||||||
|
},
|
||||||
|
Right: _ => _snackbar.Add("Successfully saved general settings", Severity.Success));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,8 @@ using System.Diagnostics;
|
|||||||
using Destructurama;
|
using Destructurama;
|
||||||
using ErsatzTV.Core;
|
using ErsatzTV.Core;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace ErsatzTV;
|
namespace ErsatzTV;
|
||||||
|
|
||||||
@@ -29,14 +31,21 @@ public class Program
|
|||||||
true)
|
true)
|
||||||
.AddEnvironmentVariables()
|
.AddEnvironmentVariables()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
LoggingLevelSwitch = new LoggingLevelSwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IConfiguration Configuration { get; }
|
private static IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
private static LoggingLevelSwitch LoggingLevelSwitch { get; }
|
||||||
|
|
||||||
public static async Task<int> Main(string[] args)
|
public static async Task<int> Main(string[] args)
|
||||||
{
|
{
|
||||||
|
LoggingLevelSwitch.MinimumLevel = LogEventLevel.Information;
|
||||||
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
Log.Logger = new LoggerConfiguration()
|
||||||
.ReadFrom.Configuration(Configuration)
|
.ReadFrom.Configuration(Configuration)
|
||||||
|
.MinimumLevel.ControlledBy(LoggingLevelSwitch)
|
||||||
.Destructure.UsingAttributes()
|
.Destructure.UsingAttributes()
|
||||||
.Enrich.FromLogContext()
|
.Enrich.FromLogContext()
|
||||||
.WriteTo.SQLite(FileSystemLayout.LogDatabasePath, retentionPeriod: TimeSpan.FromDays(1))
|
.WriteTo.SQLite(FileSystemLayout.LogDatabasePath, retentionPeriod: TimeSpan.FromDays(1))
|
||||||
@@ -61,6 +70,7 @@ public class Program
|
|||||||
|
|
||||||
private static IHostBuilder CreateHostBuilder(string[] args) =>
|
private static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
Host.CreateDefaultBuilder(args)
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureServices(services => services.AddSingleton(LoggingLevelSwitch))
|
||||||
.ConfigureWebHostDefaults(
|
.ConfigureWebHostDefaults(
|
||||||
webBuilder => webBuilder.UseStartup<Startup>()
|
webBuilder => webBuilder.UseStartup<Startup>()
|
||||||
.UseConfiguration(Configuration)
|
.UseConfiguration(Configuration)
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using ErsatzTV.Core.Domain;
|
||||||
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace ErsatzTV.Services.RunOnce;
|
||||||
|
|
||||||
|
public class LoadLoggingLevelService : IHostedService
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
|
|
||||||
|
public LoadLoggingLevelService(IServiceScopeFactory serviceScopeFactory) =>
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using IServiceScope scope = _serviceScopeFactory.CreateScope();
|
||||||
|
IConfigElementRepository configElementRepository =
|
||||||
|
scope.ServiceProvider.GetRequiredService<IConfigElementRepository>();
|
||||||
|
|
||||||
|
Option<LogEventLevel> maybeLogLevel =
|
||||||
|
await configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel);
|
||||||
|
foreach (LogEventLevel logLevel in maybeLogLevel)
|
||||||
|
{
|
||||||
|
LoggingLevelSwitch loggingLevelSwitch = scope.ServiceProvider.GetRequiredService<LoggingLevelSwitch>();
|
||||||
|
loggingLevelSwitch.MinimumLevel = logLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
}
|
||||||
@@ -427,6 +427,7 @@ public class Startup
|
|||||||
// services.AddTransient(typeof(IRequestHandler<,>), typeof(GetRecentLogEntriesHandler<>));
|
// services.AddTransient(typeof(IRequestHandler<,>), typeof(GetRecentLogEntriesHandler<>));
|
||||||
|
|
||||||
// run-once/blocking startup services
|
// run-once/blocking startup services
|
||||||
|
services.AddHostedService<LoadLoggingLevelService>();
|
||||||
services.AddHostedService<EndpointValidatorService>();
|
services.AddHostedService<EndpointValidatorService>();
|
||||||
services.AddHostedService<DatabaseMigratorService>();
|
services.AddHostedService<DatabaseMigratorService>();
|
||||||
services.AddHostedService<CacheCleanerService>();
|
services.AddHostedService<CacheCleanerService>();
|
||||||
|
|||||||
Reference in New Issue
Block a user