using ErsatzTV.Core.Health; using ErsatzTV.Core.Health.Checks; using Microsoft.Extensions.Logging; namespace ErsatzTV.Infrastructure.Health; public class HealthCheckService : IHealthCheckService { private readonly List _checks; // ReSharper disable SuggestBaseTypeForParameterInConstructor private readonly ILogger _logger; public HealthCheckService( IMacOsConfigFolderHealthCheck macOsConfigFolderHealthCheck, IFFmpegVersionHealthCheck ffmpegVersionHealthCheck, IFFmpegReportsHealthCheck ffmpegReportsHealthCheck, IHardwareAccelerationHealthCheck hardwareAccelerationHealthCheck, IMovieMetadataHealthCheck movieMetadataHealthCheck, IEpisodeMetadataHealthCheck episodeMetadataHealthCheck, IZeroDurationHealthCheck zeroDurationHealthCheck, IFileNotFoundHealthCheck fileNotFoundHealthCheck, IUnavailableHealthCheck unavailableHealthCheck, IVaapiDriverHealthCheck vaapiDriverHealthCheck, IErrorReportsHealthCheck errorReportsHealthCheck, ILogger logger) { _logger = logger; _checks = [ macOsConfigFolderHealthCheck, ffmpegVersionHealthCheck, ffmpegReportsHealthCheck, hardwareAccelerationHealthCheck, movieMetadataHealthCheck, episodeMetadataHealthCheck, zeroDurationHealthCheck, fileNotFoundHealthCheck, unavailableHealthCheck, vaapiDriverHealthCheck, errorReportsHealthCheck ]; } public Task> PerformHealthChecks(CancellationToken cancellationToken) => _checks.Map( c => { var failedResult = new HealthCheckResult( c.Title, HealthCheckStatus.Fail, "Health check failure; see logs", None); return TryAsync(() => c.Check(cancellationToken)).IfFail(ex => LogAndReturn(ex, failedResult)); }) .SequenceParallel() .Map(results => results.ToList()); private HealthCheckResult LogAndReturn(Exception ex, HealthCheckResult failedResult) { if (ex is not OperationCanceledException) { _logger.LogWarning(ex, "Failed to run health check {Title}", failedResult.Title); } return failedResult; } }