Exemple #1
0
        public CacheableHealthChecksBuilder(IHealthChecksBuilder builder, CacheableHealthChecksServiceOptions options)
        {
            this.builder = builder;
            this.options = options;

            Services = builder.Services;
        }
Exemple #2
0
        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));
        }
Exemple #3
0
        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));
        }
Exemple #5
0
        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);
            }
        }