Browse Source

add more log level switches (#1582)

* label block and json playouts as experimental

* add more log level switches
pull/1583/head
Jason Dove 2 years ago committed by GitHub
parent
commit
6923b25177
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      CHANGELOG.md
  2. 20
      ErsatzTV.Application/Configuration/Commands/UpdateGeneralSettingsHandler.cs
  3. 5
      ErsatzTV.Application/Configuration/GeneralSettingsViewModel.cs
  4. 16
      ErsatzTV.Application/Configuration/Queries/GetGeneralSettingsHandler.cs
  5. 11
      ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs
  6. 18
      ErsatzTV.Application/Streaming/HlsSessionWorker.cs
  7. 3
      ErsatzTV.Core/Domain/ConfigElementKey.cs
  8. 14
      ErsatzTV.Core/LoggingLevelSwitches.cs
  9. 6
      ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs
  10. 4
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs
  11. 2
      ErsatzTV/Controllers/InternalController.cs
  12. 2
      ErsatzTV/Controllers/IptvController.cs
  13. 2
      ErsatzTV/Pages/Channels.razor
  14. 8
      ErsatzTV/Pages/Playouts.razor
  15. 37
      ErsatzTV/Pages/Settings.razor
  16. 31
      ErsatzTV/Program.cs
  17. 26
      ErsatzTV/Services/RunOnce/LoadLoggingLevelService.cs

5
CHANGELOG.md

@ -39,6 +39,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Channels MUST use `MPEG-TS` or `HLS Segmenter` streaming modes - Channels MUST use `MPEG-TS` or `HLS Segmenter` streaming modes
- Since `MPEG-TS` uses `HLS Segmenter` under the hood, the preview player will use `HLS Segmenter`, so it's not 100% equivalent, but it should be representative - Since `MPEG-TS` uses `HLS Segmenter` under the hood, the preview player will use `HLS Segmenter`, so it's not 100% equivalent, but it should be representative
- Add button to stop transcoding session for each channel that has an active session - Add button to stop transcoding session for each channel that has an active session
- Add more log levels to `Settings` page, allowing more specific debug logging as needed
- Default Minimum Log Level (applies when no other categories/level overrides match)
- Scanning Minimum Log Level
- Scheduling Minimum Log Level
- Streaming Minimum Log Level
### Fixed ### Fixed
- Fix error loading path replacements when using MySql - Fix error loading path replacements when using MySql

20
ErsatzTV.Application/Configuration/Commands/UpdateGeneralSettingsHandler.cs

@ -1,20 +1,19 @@
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
using Serilog.Core;
namespace ErsatzTV.Application.Configuration; namespace ErsatzTV.Application.Configuration;
public class UpdateGeneralSettingsHandler : IRequestHandler<UpdateGeneralSettings, Either<BaseError, Unit>> public class UpdateGeneralSettingsHandler : IRequestHandler<UpdateGeneralSettings, Either<BaseError, Unit>>
{ {
private readonly IConfigElementRepository _configElementRepository; private readonly IConfigElementRepository _configElementRepository;
private readonly LoggingLevelSwitch _loggingLevelSwitch; private readonly LoggingLevelSwitches _loggingLevelSwitches;
public UpdateGeneralSettingsHandler( public UpdateGeneralSettingsHandler(
LoggingLevelSwitch loggingLevelSwitch, LoggingLevelSwitches loggingLevelSwitches,
IConfigElementRepository configElementRepository) IConfigElementRepository configElementRepository)
{ {
_loggingLevelSwitch = loggingLevelSwitch; _loggingLevelSwitches = loggingLevelSwitches;
_configElementRepository = configElementRepository; _configElementRepository = configElementRepository;
} }
@ -24,8 +23,17 @@ public class UpdateGeneralSettingsHandler : IRequestHandler<UpdateGeneralSetting
private async Task<Unit> ApplyUpdate(GeneralSettingsViewModel generalSettings) private async Task<Unit> ApplyUpdate(GeneralSettingsViewModel generalSettings)
{ {
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, generalSettings.MinimumLogLevel); await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, generalSettings.DefaultMinimumLogLevel);
_loggingLevelSwitch.MinimumLevel = generalSettings.MinimumLogLevel; _loggingLevelSwitches.DefaultLevelSwitch.MinimumLevel = generalSettings.DefaultMinimumLogLevel;
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevelScanning, generalSettings.ScanningMinimumLogLevel);
_loggingLevelSwitches.ScanningLevelSwitch.MinimumLevel = generalSettings.ScanningMinimumLogLevel;
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevelScheduling, generalSettings.SchedulingMinimumLogLevel);
_loggingLevelSwitches.SchedulingLevelSwitch.MinimumLevel = generalSettings.SchedulingMinimumLogLevel;
await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevelStreaming, generalSettings.StreamingMinimumLogLevel);
_loggingLevelSwitches.StreamingLevelSwitch.MinimumLevel = generalSettings.StreamingMinimumLogLevel;
return Unit.Default; return Unit.Default;
} }

5
ErsatzTV.Application/Configuration/GeneralSettingsViewModel.cs

@ -4,5 +4,8 @@ namespace ErsatzTV.Application.Configuration;
public class GeneralSettingsViewModel public class GeneralSettingsViewModel
{ {
public LogEventLevel MinimumLogLevel { get; set; } public LogEventLevel DefaultMinimumLogLevel { get; set; }
public LogEventLevel ScanningMinimumLogLevel { get; set; }
public LogEventLevel SchedulingMinimumLogLevel { get; set; }
public LogEventLevel StreamingMinimumLogLevel { get; set; }
} }

16
ErsatzTV.Application/Configuration/Queries/GetGeneralSettingsHandler.cs

@ -13,12 +13,24 @@ public class GetGeneralSettingsHandler : IRequestHandler<GetGeneralSettings, Gen
public async Task<GeneralSettingsViewModel> Handle(GetGeneralSettings request, CancellationToken cancellationToken) public async Task<GeneralSettingsViewModel> Handle(GetGeneralSettings request, CancellationToken cancellationToken)
{ {
Option<LogEventLevel> maybeLogLevel = Option<LogEventLevel> maybeDefaultLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel); await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel);
Option<LogEventLevel> maybeScanningLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScanning);
Option<LogEventLevel> maybeSchedulingLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScheduling);
Option<LogEventLevel> maybeStreamingLevel =
await _configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelStreaming);
return new GeneralSettingsViewModel return new GeneralSettingsViewModel
{ {
MinimumLogLevel = await maybeLogLevel.IfNoneAsync(LogEventLevel.Information) DefaultMinimumLogLevel = await maybeDefaultLevel.IfNoneAsync(LogEventLevel.Information),
ScanningMinimumLogLevel = await maybeScanningLevel.IfNoneAsync(LogEventLevel.Information),
SchedulingMinimumLogLevel = await maybeSchedulingLevel.IfNoneAsync(LogEventLevel.Information),
StreamingMinimumLogLevel = await maybeStreamingLevel.IfNoneAsync(LogEventLevel.Information),
}; };
} }
} }

11
ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs

@ -84,7 +84,16 @@ public abstract class CallLibraryScannerHandler<TRequest>
// because the compact json writer used by the scanner // because the compact json writer used by the scanner
// writes in UTC // writes in UTC
LogEvent logEvent = LogEventReader.ReadFromString(s); LogEvent logEvent = LogEventReader.ReadFromString(s);
Log.Write(
ILogger log = Log.Logger;
if (logEvent.Properties.TryGetValue("SourceContext", out LogEventPropertyValue property))
{
log = log.ForContext(
Serilog.Core.Constants.SourceContextPropertyName,
property.ToString().Trim('"'));
}
log.Write(
new LogEvent( new LogEvent(
logEvent.Timestamp.ToLocalTime(), logEvent.Timestamp.ToLocalTime(),
logEvent.Level, logEvent.Level,

18
ErsatzTV.Application/Streaming/HlsSessionWorker.cs

@ -125,7 +125,7 @@ public class HlsSessionWorker : IHlsSessionWorker
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException) catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{ {
// do nothing // do nothing
_logger.LogInformation("HlsSessionWorker.TrimPlaylist was canceled"); _logger.LogDebug("HlsSessionWorker.TrimPlaylist was canceled");
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -302,7 +302,7 @@ public class HlsSessionWorker : IHlsSessionWorker
if (!realtime) if (!realtime)
{ {
Interlocked.Increment(ref _workAheadCount); Interlocked.Increment(ref _workAheadCount);
_logger.LogInformation("HLS segmenter will work ahead for channel {Channel}", _channelNumber); _logger.LogDebug("HLS segmenter will work ahead for channel {Channel}", _channelNumber);
HlsSessionState nextState = _state switch HlsSessionState nextState = _state switch
{ {
@ -319,7 +319,7 @@ public class HlsSessionWorker : IHlsSessionWorker
} }
else else
{ {
_logger.LogInformation( _logger.LogDebug(
"HLS segmenter will NOT work ahead for channel {Channel}", "HLS segmenter will NOT work ahead for channel {Channel}",
_channelNumber); _channelNumber);
@ -341,7 +341,7 @@ public class HlsSessionWorker : IHlsSessionWorker
long ptsOffset = await GetPtsOffset(_channelNumber, cancellationToken); long ptsOffset = await GetPtsOffset(_channelNumber, cancellationToken);
// _logger.LogInformation("PTS offset: {PtsOffset}", ptsOffset); // _logger.LogInformation("PTS offset: {PtsOffset}", ptsOffset);
_logger.LogInformation("HLS session state: {State}", _state); _logger.LogDebug("HLS session state: {State}", _state);
DateTimeOffset now = _state is HlsSessionState.SeekAndWorkAhead DateTimeOffset now = _state is HlsSessionState.SeekAndWorkAhead
? DateTimeOffset.Now ? DateTimeOffset.Now
@ -379,7 +379,7 @@ public class HlsSessionWorker : IHlsSessionWorker
Command process = processModel.Process; Command process = processModel.Process;
_logger.LogInformation("ffmpeg hls arguments {FFmpegArguments}", process.Arguments); _logger.LogDebug("ffmpeg hls arguments {FFmpegArguments}", process.Arguments);
try try
{ {
@ -389,7 +389,7 @@ public class HlsSessionWorker : IHlsSessionWorker
if (commandResult.ExitCode == 0) if (commandResult.ExitCode == 0)
{ {
_logger.LogInformation("HLS process has completed for channel {Channel}", _channelNumber); _logger.LogDebug("HLS process has completed for channel {Channel}", _channelNumber);
_logger.LogDebug("Transcoded until: {Until}", processModel.Until); _logger.LogDebug("Transcoded until: {Until}", processModel.Until);
_transcodedUntil = processModel.Until; _transcodedUntil = processModel.Until;
_state = NextState(_state, processModel); _state = NextState(_state, processModel);
@ -427,7 +427,7 @@ public class HlsSessionWorker : IHlsSessionWorker
{ {
Command errorProcess = errorProcessModel.Process; Command errorProcess = errorProcessModel.Process;
_logger.LogInformation( _logger.LogDebug(
"ffmpeg hls error arguments {FFmpegArguments}", "ffmpeg hls error arguments {FFmpegArguments}",
errorProcess.Arguments); errorProcess.Arguments);
@ -451,7 +451,7 @@ public class HlsSessionWorker : IHlsSessionWorker
} }
catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException) catch (Exception ex) when (ex is TaskCanceledException or OperationCanceledException)
{ {
_logger.LogInformation("Terminating HLS process for channel {Channel}", _channelNumber); _logger.LogInformation("Terminating HLS session for channel {Channel}", _channelNumber);
return false; return false;
} }
} }
@ -600,7 +600,7 @@ public class HlsSessionWorker : IHlsSessionWorker
return await File.ReadAllLinesAsync(fileName, cancellationToken); return await File.ReadAllLinesAsync(fileName, cancellationToken);
} }
_logger.LogInformation("Playlist does not exist at expected location {File}", fileName); _logger.LogDebug("Playlist does not exist at expected location {File}", fileName);
return None; return None;
} }

3
ErsatzTV.Core/Domain/ConfigElementKey.cs

@ -7,6 +7,9 @@ public class ConfigElementKey
public string Key { get; } public string Key { get; }
public static ConfigElementKey MinimumLogLevel => new("log.minimum_level"); public static ConfigElementKey MinimumLogLevel => new("log.minimum_level");
public static ConfigElementKey MinimumLogLevelScanning => new("log.minimum_level.scanning");
public static ConfigElementKey MinimumLogLevelScheduling => new("log.minimum_level.scheduling");
public static ConfigElementKey MinimumLogLevelStreaming => new("log.minimum_level.streaming");
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
ErsatzTV.Core/LoggingLevelSwitches.cs

@ -0,0 +1,14 @@
using Serilog.Core;
namespace ErsatzTV.Core;
public class LoggingLevelSwitches
{
public LoggingLevelSwitch DefaultLevelSwitch { get; } = new();
public LoggingLevelSwitch ScanningLevelSwitch { get; } = new();
public LoggingLevelSwitch SchedulingLevelSwitch { get; } = new();
public LoggingLevelSwitch StreamingLevelSwitch { get; } = new();
}

6
ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs

@ -293,7 +293,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
if (profileEntrypoints is not null && profileEntrypoints.Count != 0) if (profileEntrypoints is not null && profileEntrypoints.Count != 0)
{ {
_logger.LogInformation( _logger.LogDebug(
"Detected {Count} VAAPI profile entrypoints for using {Driver} {Device}", "Detected {Count} VAAPI profile entrypoints for using {Driver} {Device}",
profileEntrypoints.Count, profileEntrypoints.Count,
driver, driver,
@ -359,7 +359,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
if (profileEntrypoints is not null && profileEntrypoints.Count != 0) if (profileEntrypoints is not null && profileEntrypoints.Count != 0)
{ {
_logger.LogInformation( _logger.LogDebug(
"Detected {Count} VAAPI profile entrypoints for using QSV device {Device}", "Detected {Count} VAAPI profile entrypoints for using QSV device {Device}",
profileEntrypoints.Count, profileEntrypoints.Count,
device); device);
@ -408,7 +408,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
const string MODEL_PATTERN = @"(GTX\s+[0-9a-zA-Z]+[\sTtIi]+)"; const string MODEL_PATTERN = @"(GTX\s+[0-9a-zA-Z]+[\sTtIi]+)";
Match modelMatch = Regex.Match(line, MODEL_PATTERN); Match modelMatch = Regex.Match(line, MODEL_PATTERN);
string model = modelMatch.Success ? modelMatch.Groups[1].Value.Trim() : "unknown"; string model = modelMatch.Success ? modelMatch.Groups[1].Value.Trim() : "unknown";
_logger.LogInformation( _logger.LogDebug(
"Detected NVIDIA GPU model {Model} architecture SM {Architecture}", "Detected NVIDIA GPU model {Model} architecture SM {Architecture}",
model, model,
architecture); architecture);

4
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

@ -659,7 +659,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
if (ffmpegState.DecoderHardwareAccelerationMode != HardwareAccelerationMode.None || if (ffmpegState.DecoderHardwareAccelerationMode != HardwareAccelerationMode.None ||
ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.None) ffmpegState.EncoderHardwareAccelerationMode != HardwareAccelerationMode.None)
{ {
_logger.LogInformation( _logger.LogDebug(
"Forcing {Threads} ffmpeg thread when hardware acceleration is used", "Forcing {Threads} ffmpeg thread when hardware acceleration is used",
1); 1);
@ -667,7 +667,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
} }
else if (ffmpegState.Start.Exists(s => s > TimeSpan.Zero) && desiredState.Realtime) else if (ffmpegState.Start.Exists(s => s > TimeSpan.Zero) && desiredState.Realtime)
{ {
_logger.LogInformation( _logger.LogDebug(
"Forcing {Threads} ffmpeg thread due to buggy combination of stream seek and realtime output", "Forcing {Threads} ffmpeg thread due to buggy combination of stream seek and realtime output",
1); 1);

2
ErsatzTV/Controllers/InternalController.cs

@ -53,7 +53,7 @@ public class InternalController : ControllerBase
{ {
Command command = processModel.Process; Command command = processModel.Process;
_logger.LogInformation("ffmpeg arguments {FFmpegArguments}", command.Arguments); _logger.LogDebug("ffmpeg arguments {FFmpegArguments}", command.Arguments);
var process = new FFmpegProcess var process = new FFmpegProcess
{ {
StartInfo = new ProcessStartInfo StartInfo = new ProcessStartInfo

2
ErsatzTV/Controllers/IptvController.cs

@ -111,7 +111,7 @@ public class IptvController : ControllerBase
Command command = processModel.Process; Command command = processModel.Process;
_logger.LogInformation("Starting ts stream for channel {ChannelNumber}", channelNumber); _logger.LogInformation("Starting ts stream for channel {ChannelNumber}", channelNumber);
_logger.LogInformation("ffmpeg arguments {FFmpegArguments}", command.Arguments); _logger.LogDebug("ffmpeg arguments {FFmpegArguments}", command.Arguments);
var process = new FFmpegProcess var process = new FFmpegProcess
{ {
StartInfo = new ProcessStartInfo StartInfo = new ProcessStartInfo

2
ErsatzTV/Pages/Channels.razor

@ -183,8 +183,6 @@
string currentUri = NavigationManager.Uri; string currentUri = NavigationManager.Uri;
string streamUri = currentUri.Replace("/channels", $"/iptv/channel/{channel.Number}.m3u8?mode=segmenter"); string streamUri = currentUri.Replace("/channels", $"/iptv/channel/{channel.Number}.m3u8?mode=segmenter");
Serilog.Log.Logger.Information("Stream uri: {StreamUri}", streamUri);
var parameters = new DialogParameters { { "StreamUri", streamUri } }; var parameters = new DialogParameters { { "StreamUri", streamUri } };
var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraLarge }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraLarge };
await Dialog.ShowAsync<ChannelPreviewDialog>("Channel Preview", parameters, options); await Dialog.ShowAsync<ChannelPreviewDialog>("Channel Preview", parameters, options);

8
ErsatzTV/Pages/Playouts.razor

@ -14,12 +14,16 @@
<MudButton Variant="Variant.Filled" Color="Color.Primary" Link="playouts/add"> <MudButton Variant="Variant.Filled" Color="Color.Primary" Link="playouts/add">
Add Playout Add Playout
</MudButton> </MudButton>
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Primary" Link="@($"playouts/add/{PlayoutKind.Block}")"> <MudTooltip Text="This feature is experimental">
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Warning" Link="@($"playouts/add/{PlayoutKind.Block}")">
Add Block Playout Add Block Playout
</MudButton> </MudButton>
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Primary" Link="@($"playouts/add/{PlayoutKind.ExternalJson}")"> </MudTooltip>
<MudTooltip Text="This feature is experimental">
<MudButton Class="ml-3" Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Warning" Link="@($"playouts/add/{PlayoutKind.ExternalJson}")">
Add External Json Playout Add External Json Playout
</MudButton> </MudButton>
</MudTooltip>
</div> </div>
<MudTable Hover="true" <MudTable Hover="true"
Dense="true" Dense="true"

37
ErsatzTV/Pages/Settings.razor

@ -121,9 +121,36 @@
<MudCardContent> <MudCardContent>
<MudForm> <MudForm>
<MudSelect Class="mt-3" <MudSelect Class="mt-3"
Label="Minimum Log Level" Label="Default Minimum Log Level"
@bind-Value="_generalSettings.MinimumLogLevel" @bind-Value="_generalSettings.DefaultMinimumLogLevel"
For="@(() => _generalSettings.MinimumLogLevel)"> For="@(() => _generalSettings.DefaultMinimumLogLevel)">
<MudSelectItem Value="@LogEventLevel.Debug">Debug</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Information">Information</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Warning">Warning</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Error">Error</MudSelectItem>
</MudSelect>
<MudSelect Class="mt-3"
Label="Scanning Minimum Log Level"
@bind-Value="_generalSettings.ScanningMinimumLogLevel"
For="@(() => _generalSettings.ScanningMinimumLogLevel)">
<MudSelectItem Value="@LogEventLevel.Debug">Debug</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Information">Information</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Warning">Warning</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Error">Error</MudSelectItem>
</MudSelect>
<MudSelect Class="mt-3"
Label="Scheduling Minimum Log Level"
@bind-Value="_generalSettings.SchedulingMinimumLogLevel"
For="@(() => _generalSettings.SchedulingMinimumLogLevel)">
<MudSelectItem Value="@LogEventLevel.Debug">Debug</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Information">Information</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Warning">Warning</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Error">Error</MudSelectItem>
</MudSelect>
<MudSelect Class="mt-3"
Label="Streaming Minimum Log Level"
@bind-Value="_generalSettings.StreamingMinimumLogLevel"
For="@(() => _generalSettings.StreamingMinimumLogLevel)">
<MudSelectItem Value="@LogEventLevel.Debug">Debug</MudSelectItem> <MudSelectItem Value="@LogEventLevel.Debug">Debug</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Information">Information</MudSelectItem> <MudSelectItem Value="@LogEventLevel.Information">Information</MudSelectItem>
<MudSelectItem Value="@LogEventLevel.Warning">Warning</MudSelectItem> <MudSelectItem Value="@LogEventLevel.Warning">Warning</MudSelectItem>
@ -132,7 +159,9 @@
</MudForm> </MudForm>
</MudCardContent> </MudCardContent>
<MudCardActions> <MudCardActions>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => SaveGeneralSettings())">Save Settings</MudButton> <MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="@(_ => SaveGeneralSettings())" StartIcon="@Icons.Material.Filled.Save">
Save Settings
</MudButton>
</MudCardActions> </MudCardActions>
</MudCard> </MudCard>
<MudCard Class="mb-6" Style="width: 350px"> <MudCard Class="mb-6" Style="width: 350px">

31
ErsatzTV/Program.cs

@ -35,12 +35,12 @@ public class Program
.AddEnvironmentVariables() .AddEnvironmentVariables()
.Build(); .Build();
LoggingLevelSwitch = new LoggingLevelSwitch(); LoggingLevelSwitches = new LoggingLevelSwitches();
} }
private static IConfiguration Configuration { get; } private static IConfiguration Configuration { get; }
private static LoggingLevelSwitch LoggingLevelSwitch { get; } private static LoggingLevelSwitches LoggingLevelSwitches { get; }
public static async Task<int> Main(string[] args) public static async Task<int> Main(string[] args)
{ {
@ -54,11 +54,29 @@ public class Program
return 1; return 1;
} }
LoggingLevelSwitch.MinimumLevel = LogEventLevel.Information; LoggingLevelSwitches.DefaultLevelSwitch.MinimumLevel = LogEventLevel.Information;
LoggingLevelSwitches.ScanningLevelSwitch.MinimumLevel = LogEventLevel.Information;
LoggingLevelSwitches.SchedulingLevelSwitch.MinimumLevel = LogEventLevel.Information;
LoggingLevelSwitches.StreamingLevelSwitch.MinimumLevel = LogEventLevel.Information;
LoggerConfiguration loggerConfiguration = new LoggerConfiguration() LoggerConfiguration loggerConfiguration = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration) .ReadFrom.Configuration(Configuration)
.MinimumLevel.ControlledBy(LoggingLevelSwitch)
.MinimumLevel.ControlledBy(LoggingLevelSwitches.DefaultLevelSwitch)
// scanning
.MinimumLevel.Override("ErsatzTV.Services.ScannerService", LoggingLevelSwitches.ScanningLevelSwitch)
.MinimumLevel.Override("ErsatzTV.Scanner", LoggingLevelSwitches.ScanningLevelSwitch)
// scheduling
.MinimumLevel.Override("ErsatzTV.Core.Scheduling", LoggingLevelSwitches.SchedulingLevelSwitch)
.MinimumLevel.Override("ErsatzTV.Application.Subtitles.ExtractEmbeddedSubtitlesHandler", LoggingLevelSwitches.SchedulingLevelSwitch)
// streaming
.MinimumLevel.Override("ErsatzTV.Application.Streaming", LoggingLevelSwitches.StreamingLevelSwitch)
.MinimumLevel.Override("ErsatzTV.FFmpeg", LoggingLevelSwitches.StreamingLevelSwitch)
.MinimumLevel.Override("ErsatzTV.Controllers.IptvController", LoggingLevelSwitches.StreamingLevelSwitch)
.Destructure.UsingAttributes() .Destructure.UsingAttributes()
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.File( .WriteTo.File(
@ -78,7 +96,8 @@ public class Program
{ {
loggerConfiguration = loggerConfiguration.WriteTo.Console( loggerConfiguration = loggerConfiguration.WriteTo.Console(
theme: AnsiConsoleTheme.Code, theme: AnsiConsoleTheme.Code,
formatProvider: CultureInfo.InvariantCulture); formatProvider: CultureInfo.InvariantCulture,
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <{SourceContext:l}> {NewLine}{Exception}");
// for troubleshooting log category // for troubleshooting log category
// outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <{SourceContext:l}> {NewLine}{Exception}" // outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <{SourceContext:l}> {NewLine}{Exception}"
@ -104,7 +123,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)) .ConfigureServices(services => services.AddSingleton(LoggingLevelSwitches))
.ConfigureWebHostDefaults( .ConfigureWebHostDefaults(
webBuilder => webBuilder.UseStartup<Startup>() webBuilder => webBuilder.UseStartup<Startup>()
.UseConfiguration(Configuration) .UseConfiguration(Configuration)

26
ErsatzTV/Services/RunOnce/LoadLoggingLevelService.cs

@ -31,12 +31,28 @@ public class LoadLoggingLevelService : BackgroundService
IConfigElementRepository configElementRepository = IConfigElementRepository configElementRepository =
scope.ServiceProvider.GetRequiredService<IConfigElementRepository>(); scope.ServiceProvider.GetRequiredService<IConfigElementRepository>();
Option<LogEventLevel> maybeLogLevel = foreach (LogEventLevel logLevel in await configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel))
await configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevel);
foreach (LogEventLevel logLevel in maybeLogLevel)
{ {
LoggingLevelSwitch loggingLevelSwitch = scope.ServiceProvider.GetRequiredService<LoggingLevelSwitch>(); LoggingLevelSwitches loggingLevelSwitches = scope.ServiceProvider.GetRequiredService<LoggingLevelSwitches>();
loggingLevelSwitch.MinimumLevel = logLevel; loggingLevelSwitches.DefaultLevelSwitch.MinimumLevel = logLevel;
}
foreach (LogEventLevel logLevel in await configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScanning))
{
LoggingLevelSwitches loggingLevelSwitches = scope.ServiceProvider.GetRequiredService<LoggingLevelSwitches>();
loggingLevelSwitches.ScanningLevelSwitch.MinimumLevel = logLevel;
}
foreach (LogEventLevel logLevel in await configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelScheduling))
{
LoggingLevelSwitches loggingLevelSwitches = scope.ServiceProvider.GetRequiredService<LoggingLevelSwitches>();
loggingLevelSwitches.SchedulingLevelSwitch.MinimumLevel = logLevel;
}
foreach (LogEventLevel logLevel in await configElementRepository.GetValue<LogEventLevel>(ConfigElementKey.MinimumLogLevelStreaming))
{
LoggingLevelSwitches loggingLevelSwitches = scope.ServiceProvider.GetRequiredService<LoggingLevelSwitches>();
loggingLevelSwitches.StreamingLevelSwitch.MinimumLevel = logLevel;
} }
} }
} }

Loading…
Cancel
Save