public static HealthReportEntry TestaControllersInjection(string nomeAPI, IContainer container) { var mensagens = new List <string>(); System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); var controllers = Assembly.Load(nomeAPI) .GetTypes() .Where(x => !string.IsNullOrEmpty(x.Namespace)) .Where(x => x.IsClass) .Where(x => x.Name.Contains("Controller") && !x.Name.Contains("HealthCheck")) .ToList(); foreach (var controllerType in controllers) { try { container.GetInstance(controllerType); } catch (Exception ex) { mensagens.Add($"Erro ao instanciar controller: {controllerType.Name}; {ex.Message}"); } } stopwatch.Stop(); var entry = new HealthReportEntry( mensagens.Any() ? HealthStatus.Unhealthy : HealthStatus.Healthy, "", TimeSpan.FromMilliseconds(stopwatch.ElapsedMilliseconds), null, null); return(entry); }
protected override Task DoSendAsync(string checkName, HealthReportEntry entry, CancellationToken cancellationToken) { var serviceName = $"{HostingEnvironment.ApplicationName} ({Dns.GetHostName()})"; string title = entry.Status switch { HealthStatus.Unhealthy => $"Error in {serviceName}. Check: {checkName}", HealthStatus.Degraded => $"Check {checkName} in {serviceName} is degraded", HealthStatus.Healthy => $"Check {checkName} in {serviceName} is restored", _ => $"Unknown check {checkName} status {entry.Status}" }; string summary = string.IsNullOrEmpty(entry.Description) ? "No description" : entry.Description; string text = $"{TelegramExtensions.TelegramRaw(title)}\n{TelegramExtensions.TelegramRaw(summary)}"; if (entry.Status == HealthStatus.Unhealthy || entry.Status == HealthStatus.Degraded) { if (entry.Exception != null) { text += $"\n`{entry.Exception}`"; } } return(_telegramBotClient.SendTextMessageAsync(_chatId, text, ParseMode.Markdown, cancellationToken: cancellationToken)); } }
public void BuildPrtgResponseObject_Healthy() { var healthReportEntry = new HealthReportEntry(HealthStatus.Healthy, null, TimeSpan.FromMilliseconds(10), null, null); var healthReportEntries = new Dictionary <string, HealthReportEntry> { { "test", healthReportEntry } }; var healthReport = new HealthReport(healthReportEntries, TimeSpan.FromMilliseconds(15)); var expected = new List <PrtgResponseChannelValueBase> { new PrtgResponseChannelValueTimeSpan { Channel = "TotalDuration", Value = 15 }, new PrtgResponseChannelValueTimeSpan { Channel = "test.Duration", Value = 10 }, }; var actual = PrtgResponseWriter.BuildPrtgResponseObject(healthReport); actual.Error.Should().Be(0); actual.Text.Should().Be(PrtgResponse.DefaultText); actual.Result.Should().BeEquivalentTo(expected, config => config.RespectingRuntimeTypes()); }
public Task PublishAsync(HealthReport report, CancellationToken cancellationToken) { IServicePartition?partition = m_partitionAccessor.Value; if (partition == null) { m_logger.LogWarning(Tag.Create(), "Publisher run before partition provided"); return(Task.CompletedTask); } partition.ReportPartitionHealth(new SfHealthInformation(SourceId, "Summary", ConvertStatus(report.Status))); foreach (KeyValuePair <string, HealthReportEntry> entryPair in report.Entries) { HealthReportEntry entry = entryPair.Value; SfHealthInformation sfHealthInformation = new SfHealthInformation(SourceId, entryPair.Key, ConvertStatus(entry.Status)) { Description = CreateDescription(entry) }; try { partition.ReportPartitionHealth(sfHealthInformation); } catch (Exception exception) { m_logger.LogError(Tag.Create(), exception, "Failed to report partition health"); } } return(Task.CompletedTask); }
public HealthCheckResponse(HealthReportEntry report) { Status = report.Status.ToString(); UserManagement = GetStatus("UserManagement", report.Data); DatabaseConnectivity = GetStatus("Database", report.Data); }
public static string CreateHealthReportPlainText(string key, HealthReportEntry entry) { var entryOutput = new StringBuilder($"{key}: {entry.Status} | {entry.Duration}\n"); if (entry.Tags?.Any() == true) { entryOutput.Append("- Tags:"); entryOutput.Append(string.Join(", ", entry.Tags)); entryOutput.Append('\n'); } if (!string.IsNullOrWhiteSpace(entry.Description)) { entryOutput.Append($"- Description: {entry.Description}\n\n"); } if (entry.Exception != null) { entryOutput.Append($"- Exception: {entry.Exception}\n\n"); } if (entry.Data?.Count > 0) { entryOutput.Append("- Data:\n"); foreach (var keyValuePair in entry.Data) { entryOutput.Append($"\t{keyValuePair.Key}: {keyValuePair.Value}"); } entryOutput.Append('\n'); } return(entryOutput.ToString()); }
private void AssertHealtItem(MockServicePartition partition, string name, HealthReport report) { HealthReportEntry entry = report.Entries[name]; SfHealthInformation?info = partition.HealthInformation.SingleOrDefault(h => string.Equals(h.Property, name, StringComparison.OrdinalIgnoreCase)); Assert.IsNotNull(info, name); Assert.AreEqual(GetMachingState(entry.Status), info.HealthState, nameof(HealthReportEntry.Status)); if (entry.Status == HealthStatus.Healthy) { Assert.AreEqual(info.Description, entry.Description, nameof(HealthReportEntry.Description)); } else { StringAssert.Contains(info.Description, entry.Description, nameof(HealthReportEntry.Description)); StringAssert.Contains(info.Description, entry.Exception.ToString(), nameof(HealthReportEntry.Exception)); StringAssert.Contains(info.Description, entry.Duration.ToString(), nameof(HealthReportEntry.Duration)); int index = 0; foreach (string tag in entry.Tags) { StringAssert.Contains(info.Description, tag, nameof(HealthReportEntry.Tags) + index); index++; } } }
public SerializableHealthCheckResultEntry(HealthReportEntry entry, string name) { Description = entry.Description; Duration = entry.Duration; Exception = entry.Exception?.ToString(); Name = name; Status = entry.Status; Tags = entry.Tags?.ToList(); }
public HealthReportEntryResult(HealthReportEntry entry) { Data = entry.Data; Description = entry.Description; Duration = entry.Duration; Exception = entry.Exception; Tags = entry.Tags; Status = HealthReportResult <T> .GetHealthStatusString(entry.Status); }
public override Task <HealthReport> CheckHealthAsync(Func <HealthCheckRegistration, bool> predicate, CancellationToken cancellationToken = new CancellationToken()) { var entry = new HealthReportEntry(_isHealthy ? HealthStatus.Healthy : HealthStatus.Unhealthy, "", TimeSpan.Zero, null, null); var dictionary = new Dictionary <string, HealthReportEntry> { { "test", entry } }; return(Task.FromResult(new HealthReport(dictionary, TimeSpan.Zero))); }
public void BuildPrtgResponseObject_Unhealthy() { var healthReportEntry = new HealthReportEntry(HealthStatus.Unhealthy, "description", TimeSpan.Zero, new Exception("testException"), null); var healthReportEntries = new Dictionary <string, HealthReportEntry> { { "test", healthReportEntry } }; var healthReport = new HealthReport(healthReportEntries, TimeSpan.Zero); var actual = PrtgResponseWriter.BuildPrtgResponseObject(healthReport); actual.Error.Should().Be(1); actual.Text.Should().BeEquivalentTo("test:\ndescription\nSystem.Exception: testException"); }
private static string EntryDescription(HealthReportEntry entry) { if (!string.IsNullOrEmpty(entry.Description)) { return(entry.Description); } return(entry.Status switch { HealthStatus.Healthy => $"OK - {entry.Duration.TotalSeconds:0.000}s", HealthStatus.Degraded => entry.Exception != null ? $"Warning - {entry.Exception.Message}" : "Warning", HealthStatus.Unhealthy => entry.Exception != null ? $"Failure - {entry.Exception.Message}" : "Failure", _ => entry.ToString(), });
public void BuildPrtgResponseObject_Unhealthy_ErrorText() { var healthReportEntry = new HealthReportEntry(HealthStatus.Unhealthy, null, TimeSpan.Zero, null, null); var healthReportEntries = new Dictionary <string, HealthReportEntry> { { "test", healthReportEntry } }; var healthReport = new HealthReport(healthReportEntries, TimeSpan.Zero); var actual = PrtgResponseWriter.BuildPrtgResponseObject(healthReport); actual.Error.Should().Be(1); actual.Text.Should().BeEquivalentTo("test:\nUnhealthy"); }
private string CreateDescription(HealthReportEntry entry) { // Healthy reports won't be displayed and most of the checks will be healthy, // so we are creating a detailed description only for failed checks and avoiding allocations for healthy if (entry.Status == HealthStatus.Healthy) { return(entry.Description); } return(new StringBuilder() .Append("Exception:").AppendLine(entry.Exception?.ToString()) .Append("Description:").AppendLine(entry.Description) .Append("Duration:").AppendLine(entry.Duration.ToString()) .Append("Tags:").AppendLine(string.Join(",", entry.Tags)) .ToString()); }
public ServiceHealthInfo Create(string key, HealthReportEntry healthReportEntry) { if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentNullException(nameof(key)); } return(new ServiceHealthInfo { Key = key, Description = healthReportEntry.Description ?? string.Empty, Duration = healthReportEntry.Duration, Status = healthReportEntry.Status.ToString(), Error = healthReportEntry.Exception?.Message }); }
private AvailabilityTelemetry GetAvailabilityTelemetry(string name, HealthReportEntry entry) { var availabilityTelemetry = new AvailabilityTelemetry { Timestamp = DateTimeOffset.UtcNow, Duration = entry.Duration, Success = GetSuccessFromStatus(entry.Status), Name = $"{_options.Value.TestNamePrefix}{name}", RunLocation = _options.Value.TestRunLocation, Message = entry.Description }; AddHealthReportEntryData(availabilityTelemetry, entry); return(availabilityTelemetry); }
private AvailabilityTelemetry GetAvailabilityTelemetry(string name, HealthReportEntry entry) { var availabilityTelemetry = new AvailabilityTelemetry { Name = name, Duration = entry.Duration, Message = entry.Description, Timestamp = DateTimeOffset.UtcNow, Success = entry.Status == HealthStatus.Healthy }; availabilityTelemetry.Properties.Add(nameof(entry.Status), entry.Status.ToString()); foreach (var(key, value) in entry.Data) { availabilityTelemetry.Properties.Add(key, value?.ToString()); } return(availabilityTelemetry); }
protected override Task DoSendAsync(string checkName, HealthReportEntry entry, CancellationToken cancellationToken) { var serviceName = $"{HostingEnvironment.ApplicationName} ({Dns.GetHostName()})"; string title = entry.Status switch { HealthStatus.Unhealthy => $"Error in {serviceName}. Check: {checkName}", HealthStatus.Degraded => $"Check {checkName} in {serviceName} is degraded", HealthStatus.Healthy => $"Check {checkName} in {serviceName} is restored", _ => $"Unknown check {checkName} status {entry.Status}" }; string color = entry.Status switch { HealthStatus.Unhealthy => Options.UnHealthyColor, HealthStatus.Degraded => Options.DegradedColor, HealthStatus.Healthy => Options.HealthyColor, _ => "" }; string summary = string.IsNullOrEmpty(entry.Description) ? "No description" : entry.Description; var sections = new List <Section>(); if (entry.Status == HealthStatus.Unhealthy || entry.Status == HealthStatus.Degraded) { if (entry.Exception != null) { sections.Add(new Section { ActivityTitle = "Exception", ActivityText = $"```{entry.Exception}```" }); } } var client = new TeamsHookClient(_httpClientFactory.CreateClient()); return(client.PostAsync(Options.WebHookUrl, new MessageCard { Title = title, Text = summary, Sections = sections, ThemeColor = color })); } } }
private SfHealthInformation BuildSfHealthInformation(string healthCheckName, HealthReportEntry reportEntry) { StringBuilder descriptionBuilder = m_stringBuilderPool.Get(); if (!string.IsNullOrWhiteSpace(reportEntry.Description)) { descriptionBuilder.Append("Description: ").Append(reportEntry.Description).AppendLine(); } descriptionBuilder.Append("Duration: ").Append(reportEntry.Duration).Append('.').AppendLine(); if (reportEntry.Exception != null) { descriptionBuilder.Append("Exception: ").Append(reportEntry.Exception).AppendLine(); } string description = descriptionBuilder.ToString(); m_stringBuilderPool.Return(descriptionBuilder); return(new SfHealthInformation(HealthReportSourceId, healthCheckName, ToSfHealthState(reportEntry.Status)) { Description = description, }); }
public async Task CheckHealthAsyncWithTimeout_IgnoreCheckIfCachedInputIsHealthy() { var entry = new HealthReportEntry( HealthStatus.Healthy, "timeout", TimeSpan.FromMilliseconds(1), null, null); var cachedItem = new Dictionary <string, object> { { "health", new HealthReport( new Dictionary <string, HealthReportEntry> { { "timeout", entry } }, TimeSpan.FromMilliseconds(0)) } }; var result = await new HealthController( new FakeHealthCheckService(false), null, null, new FakeMemoryCache(cachedItem)) .CheckHealthAsyncWithTimeout(); Assert.AreEqual(HealthStatus.Healthy, result.Status); }
/// <summary> /// With timeout after 15 seconds /// </summary> /// <param name="timeoutTime">in milliseconds, defaults to 15 seconds</param> /// <returns>report</returns> internal async Task <HealthReport> CheckHealthAsyncWithTimeout(int timeoutTime = 15000) { const string healthControllerCacheKey = "health"; try { if (_cache != null && _cache.TryGetValue(healthControllerCacheKey, out var objectHealthStatus) && objectHealthStatus is HealthReport healthStatus && healthStatus.Status == HealthStatus.Healthy) { return(healthStatus); } var result = await _service.CheckHealthAsync().TimeoutAfter(timeoutTime); if (_cache != null && result.Status == HealthStatus.Healthy) { _cache.Set(healthControllerCacheKey, result, new TimeSpan(0, 1, 30)); } return(result); } catch (TimeoutException exception) { var entry = new HealthReportEntry( HealthStatus.Unhealthy, "timeout", TimeSpan.FromMilliseconds(timeoutTime), exception, null); return(new HealthReport( new Dictionary <string, HealthReportEntry> { { "timeout", entry } }, TimeSpan.FromMilliseconds(timeoutTime))); } }
private HealthReportEntry BuildAndCacheReportEntry(string cacheKey, TimeSpan expiresIn, HealthStatus healthStatus, string description, TimeSpan duration, Exception exception, IReadOnlyDictionary <string, object> healthData) { var expiresAt = DateTime.UtcNow.Add(expiresIn); var data = new Dictionary <string, object>(healthData) { { "cached", $"This response was resolved in {DateTime.UtcNow:R} and will remain in cache until {expiresAt:R}" } }; var entry = new HealthReportEntry( status: healthStatus, description: description, duration: duration, exception: exception, data: data); memoryCache.Set(cacheKey, entry, expiresAt); return(entry); }
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); } }
public async Task <HealthReport> CheckHealthAsync( // Func<HealthCheckRegistration, bool> predicate, IEnumerable <HealthCheckConfig> healthChecks, CancellationToken cancellationToken = default(CancellationToken)) { var entries = new Dictionary <string, HealthReportEntry>(StringComparer.OrdinalIgnoreCase); var totalTime = Stopwatch.StartNew(); Log.HealthCheckProcessingBegin(_logger); foreach (var healthCheckConfig in healthChecks) { var healthCheck = healthCheckConfig.HealthCheck; var healthCheckName = healthCheckConfig.Name ?? healthCheck.GetType().Name; /* * if (predicate != null && !predicate(registration)) * { * continue; * } */ 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(healthCheckName))) { var stopwatch = Stopwatch.StartNew(); Log.HealthCheckBegin(_logger, healthCheckName); HealthReportEntry entry; try { var context = new HealthCheckContext { Registration = new HealthCheckRegistration( healthCheckName, healthCheck, null, Enumerable.Empty <string>()) }; var result = await healthCheck.CheckHealthAsync(context, cancellationToken); var duration = stopwatch.Elapsed; entry = new HealthReportEntry( status: result.Status, description: result.Description, duration: duration, exception: result.Exception, data: result.Data); Log.HealthCheckEnd(_logger, healthCheckName, entry, duration); Log.HealthCheckData(_logger, healthCheckName, entry); } // Allow cancellation to propagate. catch (Exception ex) when(ex as OperationCanceledException == null) { var duration = stopwatch.Elapsed; entry = new HealthReportEntry( status: HealthStatus.Unhealthy, description: ex.Message, duration: duration, exception: ex, data: null); Log.HealthCheckError(_logger, healthCheckName, ex, duration); } entries[healthCheckName] = entry; } } var totalElapsedTime = totalTime.Elapsed; var report = new HealthReport(entries, totalElapsedTime); Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime); return(report); }
public static void HealthCheckData(ILogger logger, string healthCheckName, HealthReportEntry entry) { if (entry.Data.Count > 0 && logger.IsEnabled(LogLevel.Debug)) { logger.Log( LogLevel.Debug, EventIds.HealthCheckData, new HealthCheckDataLogValue(healthCheckName, entry.Data), null, (state, ex) => state.ToString()); } }
public static void HealthCheckEnd(ILogger logger, string healthCheckName, HealthReportEntry entry, TimeSpan duration) { switch (entry.Status) { case HealthStatus.Healthy: _healthCheckEndHealthy(logger, healthCheckName, duration.TotalMilliseconds, entry.Status, entry.Description, null); break; case HealthStatus.Degraded: _healthCheckEndDegraded(logger, healthCheckName, duration.TotalMilliseconds, entry.Status, entry.Description, null); break; case HealthStatus.Unhealthy: _healthCheckEndUnhealthy(logger, healthCheckName, duration.TotalMilliseconds, entry.Status, entry.Description, null); break; } }
private async Task <HealthReportEntry> RunCheckAsync(HealthCheckRegistration registration, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var scope = _scopeFactory.CreateAsyncScope(); await using (scope.ConfigureAwait(false)) { var healthCheck = registration.Factory(scope.ServiceProvider); // 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 stopwatch = ValueStopwatch.StartNew(); var context = new HealthCheckContext { Registration = registration }; Log.HealthCheckBegin(_logger, registration.Name); HealthReportEntry entry; 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(); 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(); entry = new HealthReportEntry( status: registration.FailureStatus, description: "A timeout occurred while running check.", duration: duration, exception: ex, data: null, tags: registration.Tags); 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(); entry = new HealthReportEntry( status: registration.FailureStatus, description: ex.Message, duration: duration, exception: ex, data: null, tags: registration.Tags); Log.HealthCheckError(_logger, registration, ex, duration); } finally { timeoutCancellationTokenSource?.Dispose(); } return(entry); } } }
#pragma warning restore SYSLIB1006 public static void HealthCheckEnd(ILogger logger, HealthCheckRegistration registration, HealthReportEntry entry, TimeSpan duration) { switch (entry.Status) { case HealthStatus.Healthy: HealthCheckEndHealthy(logger, registration.Name, entry.Status, duration.TotalMilliseconds, entry.Description); break; case HealthStatus.Degraded: HealthCheckEndDegraded(logger, registration.Name, entry.Status, duration.TotalMilliseconds, entry.Description, entry.Exception); break; case HealthStatus.Unhealthy: HealthCheckEndUnhealthy(logger, registration.Name, entry.Status, duration.TotalMilliseconds, entry.Description, entry.Exception); break; } }
public override async Task <HealthReport> CheckHealthAsync(Func <HealthCheckRegistration, bool> predicate, CancellationToken cancellationToken = default) { var registrations = baseOptions.Value.Registrations; var cacheOptionsValue = cacheOptions.Value; using (var scope = scopeFactory.CreateScope()) { var context = new HealthCheckContext(); var entries = new Dictionary <string, HealthReportEntry>(StringComparer.OrdinalIgnoreCase); var optionsSnapshot = scope.ServiceProvider.GetService <IOptionsSnapshot <RegistrationOptions> >(); var totalTime = ValueStopwatch.StartNew(); Log.HealthCheckProcessingBegin(logger); foreach (var registration in registrations) { if (predicate != null && !predicate(registration)) { continue; } 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 cacheable = registration.Tags.Any(a => a == cacheOptionsValue.Tag); var cacheKey = $"{cacheOptionsValue.CachePrefix}{registration.Name}"; if (memoryCache.TryGetValue <HealthReportEntry>(cacheKey, out var cachedEntry)) { Log.HealthCheckFromCache(logger, registration, cachedEntry); Log.HealthCheckData(logger, registration, cachedEntry); entries[registration.Name] = cachedEntry; continue; } var healthCheck = registration.Factory(scope.ServiceProvider); var stopwatch = ValueStopwatch.StartNew(); context.Registration = registration; Log.HealthCheckBegin(logger, registration); HealthReportEntry entry; try { var result = await healthCheck.CheckHealthAsync(context, cancellationToken); 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); } Log.HealthCheckEnd(logger, registration, entry, duration); Log.HealthCheckData(logger, registration, entry); } // Allow cancellation to propagate. 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); } entries[registration.Name] = entry; } } var totalElapsedTime = totalTime.GetElapsedTime(); var healthReport = new HealthReport(entries, totalElapsedTime); Log.HealthCheckProcessingEnd(logger, healthReport.Status, totalElapsedTime); return(healthReport); } }
public static void HealthCheckFromCache(ILogger logger, HealthCheckRegistration registration, HealthReportEntry entry) { healthCheckFromCache(logger, registration.Name, null); }