public CacheableHealthChecksBuilder(IHealthChecksBuilder builder, CacheableHealthChecksServiceOptions options) { this.builder = builder; this.options = options; Services = builder.Services; }
private HealthReportEntry BuildAndCacheReportEntry(string cacheKey, HealthCheckRegistration registration, CacheableHealthChecksServiceOptions cacheOptionsValue, IOptionsSnapshot <RegistrationOptions> optionsSnapshot, HealthCheckResult result, TimeSpan duration) { var opts = optionsSnapshot.Get(registration.Name); var expiresIn = opts.CacheDuration ?? cacheOptionsValue.DefaultCacheDuration; return(BuildAndCacheReportEntry(cacheKey, expiresIn, result.Status, result.Description, duration, result.Exception, result.Data)); }
private HealthReportEntry BuildAndCacheExceptionReport(string cacheKey, HealthCheckRegistration registration, CacheableHealthChecksServiceOptions cacheOptionsValue, IOptionsSnapshot <RegistrationOptions> optionsSnapshot, Exception exception, TimeSpan duration) { var opts = optionsSnapshot.Get(registration.Name); var expiresIn = opts.ExceptionCacheDuration ?? cacheOptionsValue.CachedExceptionsDuration ?? cacheOptionsValue.DefaultCacheDuration; return(BuildAndCacheReportEntry(cacheKey, expiresIn, HealthStatus.Unhealthy, exception.Message, duration, exception, new Dictionary <string, object>())); }
/// <summary> /// Add the <see cref="CacheableHealthCheckService"/> /// </summary> /// <param name="services"></param> /// <param name="setup"></param> /// <returns></returns> public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services, Action <CacheableHealthChecksServiceOptions> setup) { var builder = services.AddHealthChecks(); services.RemoveAll <HealthCheckService>(); services.AddSingleton <HealthCheckService, CacheableHealthCheckService>(); services.Configure(setup); var options = new CacheableHealthChecksServiceOptions(); setup(options); return(new CacheableHealthChecksBuilder(builder, options)); }
private async Task <HealthReportEntry> RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CacheableHealthChecksServiceOptions cacheOptionsValue, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // If the health check does things like make Database queries using EF or backend HTTP calls, // it may be valuable to know that logs it generates are part of a health check. So we start a scope. using (logger.BeginScope(new HealthCheckLogScope(registration.Name))) { var cacheKey = $"{cacheOptionsValue.CachePrefix}{registration.Name}"; if (memoryCache.TryGetValue(cacheKey, out HealthReportEntry entry)) { Log.HealthCheckFromCache(logger, registration, entry); Log.HealthCheckData(logger, registration, entry); } else { var cacheable = registration.Tags.Any(a => a == cacheOptionsValue.Tag); var optionsSnapshot = scope.ServiceProvider.GetService <IOptionsSnapshot <RegistrationOptions> >(); var healthCheck = registration.Factory(scope.ServiceProvider); var stopwatch = ValueStopwatch.StartNew(); var context = new HealthCheckContext { Registration = registration }; Log.HealthCheckBegin(logger, registration); CancellationTokenSource timeoutCancellationTokenSource = null; try { HealthCheckResult result; var checkCancellationToken = cancellationToken; if (registration.Timeout > TimeSpan.Zero) { timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); timeoutCancellationTokenSource.CancelAfter(registration.Timeout); checkCancellationToken = timeoutCancellationTokenSource.Token; } result = await healthCheck.CheckHealthAsync(context, checkCancellationToken) .ConfigureAwait(false); var duration = stopwatch.GetElapsedTime(); if (cacheable) { entry = BuildAndCacheReportEntry(cacheKey, registration, cacheOptionsValue, optionsSnapshot, result, duration); } else { entry = new HealthReportEntry( status: result.Status, description: result.Description, duration: duration, exception: result.Exception, data: result.Data, tags: registration.Tags); } Log.HealthCheckEnd(logger, registration, entry, duration); Log.HealthCheckData(logger, registration, entry); } catch (OperationCanceledException ex) when(!cancellationToken.IsCancellationRequested) { var duration = stopwatch.GetElapsedTime(); if (cacheable && cacheOptionsValue.CacheExceptions) { entry = BuildAndCacheExceptionReport(cacheKey, registration, cacheOptionsValue, optionsSnapshot, ex, duration); } else { entry = new HealthReportEntry( status: HealthStatus.Unhealthy, description: "A timeout occured while running check.", duration: duration, exception: ex, data: null); } Log.HealthCheckError(logger, registration, ex, duration); } // Allow cancellation to propagate if it's not a timeout. catch (Exception ex) when(ex as OperationCanceledException == null) { var duration = stopwatch.GetElapsedTime(); if (cacheable && cacheOptionsValue.CacheExceptions) { entry = BuildAndCacheExceptionReport(cacheKey, registration, cacheOptionsValue, optionsSnapshot, ex, duration); } else { entry = new HealthReportEntry( status: HealthStatus.Unhealthy, description: ex.Message, duration: duration, exception: ex, data: null); } Log.HealthCheckError(logger, registration, ex, duration); } finally { timeoutCancellationTokenSource?.Dispose(); } } return(entry); } }