/// <inheritdoc/> public async Task <HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { return(await _memoryCache.GetOrCreateAsync(_cacheKey, async result => { result.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(_cacheExpirationMs); return await _healthCheck.CheckHealthAsync(context, cancellationToken); })); }
private async Task <HealthCheckResult> CheckHealthAsync(HealthCheckPolicy policy, IHealthCheckSettings settings) { var checkCache = _resultCache[settings.Name]; var utcNow = _clock.UtcNow; HealthCheckResult result = null; if (!checkCache.ShouldCheck(utcNow)) { result = checkCache.Result; } else { using (CancellationTokenSource cts = new CancellationTokenSource(settings.Timeout)) { IHealthCheck check = checkCache.Check; var healthContext = new HealthCheckContext(settings); healthContext.CancellationToken = cts.Token; try { await check.CheckHealthAsync(healthContext); result = new HealthCheckResult { Name = settings.Name, Elapsed = healthContext.ElapsedMilliseconds, Message = healthContext.Message, Status = healthContext.HasSucceeded ? HealthStatus.Healthy : healthContext.HasWarned ? HealthStatus.Warning : HealthStatus.Unhealthy, Issued = utcNow.ToUnixTimeSeconds(), NextTry = utcNow.AddSeconds(settings.Frequency), Critical = settings.Critical, Properties = healthContext.Properties?.ToDictionary(kvp => kvp.Key, kvp => JToken.FromObject(kvp.Value)) }; checkCache.Result = result; } catch (Exception e) { result = new HealthCheckResult { Name = settings.Name, Elapsed = healthContext.ElapsedMilliseconds, Message = "An error occured. See logs for more details.", Status = HealthStatus.Unhealthy, Issued = utcNow.ToUnixTimeSeconds(), NextTry = utcNow.AddSeconds(settings.Frequency), Critical = settings.Critical, Exception = e }; checkCache.Result = result; } } } return(result); }
private async Task <(string Name, HealthCheckResult Result)> MakeHealthCheckAsync(string name, IHealthCheck check, CancellationToken ct) { try { var result = await check.CheckHealthAsync(ct); return(name, result); } catch { return(name, new HealthCheckResult(false)); } }
public async Task <HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { if (_lastChecked >= ExpirationWindow) { return(_lastResult); } try { await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException oce) when(cancellationToken.IsCancellationRequested) { _logger.LogInformation(oce, $"Cancellation was requested for {nameof(CheckHealthAsync)}"); return(_lastResult); } try { if (_lastChecked >= ExpirationWindow) { return(_lastResult); } using (IServiceScope scope = _provider.CreateScope()) { try { IHealthCheck check = _healthCheck.Invoke(scope.ServiceProvider); _lastResult = await check.CheckHealthAsync(context, cancellationToken).ConfigureAwait(false); _lastChecked = Clock.UtcNow; } catch (Exception ex) { _logger.LogError(ex, Resources.FailedHealthCheckMessage); _lastResult = HealthCheckResult.Unhealthy(Resources.FailedHealthCheckMessage); _lastChecked = Clock.UtcNow; } } return(_lastResult); } finally { _semaphore.Release(); } }
/// <summary> /// Health End Point /// </summary> /// <returns></returns> public async Task <IActionResult> HealthCheckPoint() { try { HealthCheckContext context = new HealthCheckContext(); var result = await _healthCheck.CheckHealthAsync(context); return(Ok(Enum.GetName(typeof(HealthStatus), result.Status))); } catch (Exception error) { _logger.LogError(error, nameof(HealthCheckPoint)); return(StatusCode(StatusCodes.Status500InternalServerError)); } }
public async Task <HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { if (healthCheck == null) { var addresses = server.Features.Get <IServerAddressesFeature>().Addresses; if (!addresses.Any()) { return(HealthCheckResult.Unhealthy()); } healthCheck = new UriHealthCheck( new UriHealthCheckOptions().UseGet().AddUri(new Uri(addresses.First().Replace("[::]", "localhost"))), () => httpClientFactory.CreateClient()); } return(await healthCheck.CheckHealthAsync(context, cancellationToken)); }
public HealthCheckCachingTests() { var serviceProvider = Substitute.For <IServiceProvider>(); var scopeFactory = Substitute.For <IServiceScopeFactory>(); serviceProvider.GetService(typeof(IServiceScopeFactory)).Returns(scopeFactory); _serviceScope = Substitute.For <IServiceScope>(); scopeFactory.CreateScope().Returns(_serviceScope); _healthCheckFunc = Substitute.For <Func <IServiceProvider, IHealthCheck> >(); _healthCheck = Substitute.For <IHealthCheck>(); _context = new HealthCheckContext(); _cahcedHealthCheck = new CachedHealthCheck(serviceProvider, _healthCheckFunc, NullLogger <CachedHealthCheck> .Instance); _healthCheckFunc.Invoke(Arg.Any <IServiceProvider>()).ReturnsForAnyArgs(_healthCheck); _healthCheck.CheckHealthAsync(Arg.Any <HealthCheckContext>()).Returns(HealthCheckResult.Healthy()); }
public async Task <HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken) { if (_lastChecked >= ExpirationWindow) { return(_lastResult); } await _semaphore.WaitAsync(cancellationToken); try { if (_lastChecked >= ExpirationWindow) { return(_lastResult); } using (IServiceScope scope = _provider.CreateScope()) { try { IHealthCheck check = _healthCheck.Invoke(scope.ServiceProvider); _lastResult = await check.CheckHealthAsync(context, cancellationToken); _lastChecked = Clock.UtcNow; } catch (Exception ex) { _logger.LogError(ex, Resources.FailedHealthCheckMessage); _lastResult = HealthCheckResult.Unhealthy(Resources.FailedHealthCheckMessage); _lastChecked = Clock.UtcNow; } } return(_lastResult); } finally { _semaphore.Release(); } }
public async Task <HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { // you could improve thread concurrency by separating the read/write logic but this would require additional thread safety checks. // will throw OperationCanceledException if the token is canceled while we're waiting. await _mutex.WaitAsync(cancellationToken); try { // previous check is cached & not yet expired; just return it if (_lastCheck > DateTime.MinValue && DateTime.Now - _lastCheck < _timeToLive) { return(_result); } // check has not been performed or is expired; run it now & cache the result _result = await _healthCheck.CheckHealthAsync(context, cancellationToken); _lastCheck = DateTime.Now; return(_result); } finally { _mutex.Release(); } }