Skip to content

Commit

Permalink
Work on probes and health checks. (#4042)
Browse files Browse the repository at this point in the history
- Introduce probe functionality. Basic abstractions and k8s
implementation.

- Streamline the APIs for health checks by removing needless overloads.

- Rename the HealthChecks.Core package to HealthChecks.Common

Co-authored-by: Martin Taillefer <mataille@microsoft.com>
  • Loading branch information
geeknoid and Martin Taillefer authored Jun 9, 2023
1 parent 77ac48c commit f4952b6
Show file tree
Hide file tree
Showing 45 changed files with 1,227 additions and 568 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks;
internal sealed class ApplicationLifecycleHealthCheck : IHealthCheck
{
private static readonly Task<HealthCheckResult> _healthy = Task.FromResult(HealthCheckResult.Healthy());
private static readonly Task<HealthCheckResult> _unhealthy = Task.FromResult(HealthCheckResult.Unhealthy());
private static readonly Task<HealthCheckResult> _unhealthyNotStarted = Task.FromResult(HealthCheckResult.Unhealthy("Not Started"));
private static readonly Task<HealthCheckResult> _unhealthyStopping = Task.FromResult(HealthCheckResult.Unhealthy("Stopping"));
private static readonly Task<HealthCheckResult> _unhealthyStopped = Task.FromResult(HealthCheckResult.Unhealthy("Stopped"));
private readonly IHostApplicationLifetime _appLifetime;

/// <summary>
Expand Down Expand Up @@ -42,9 +44,23 @@ public ApplicationLifecycleHealthCheck(IHostApplicationLifetime appLifetime)
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
bool isStarted = _appLifetime.ApplicationStarted.IsCancellationRequested;
if (!isStarted)
{
return _unhealthyNotStarted;
}

bool isStopping = _appLifetime.ApplicationStopping.IsCancellationRequested;
if (isStopping)
{
return _unhealthyStopping;
}

bool isStopped = _appLifetime.ApplicationStopped.IsCancellationRequested;
bool isHealthy = isStarted && !isStopping && !isStopped;
return isHealthy ? _healthy : _unhealthy;
if (isStopped)
{
return _unhealthyStopped;
}

return _healthy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Shared.Diagnostics;

Expand All @@ -12,28 +11,17 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks;
/// <summary>
/// Controls various health check features.
/// </summary>
public static partial class CoreHealthChecksExtensions
public static partial class CommonHealthChecksExtensions
{
/// <summary>
/// Registers a health check provider that's tied to the application's lifecycle.
/// </summary>
/// <param name="builder">The builder to add the provider to.</param>
/// <returns>The value of <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="builder" /> is <see langword="null" />.</exception>
public static IHealthChecksBuilder AddApplicationLifecycleHealthCheck(this IHealthChecksBuilder builder)
=> Throw.IfNull(builder)
.AddCheck<ApplicationLifecycleHealthCheck>("ApplicationLifecycleHealthCheck");

/// <summary>
/// Registers a health check provider that's tied to the application's lifecycle.
/// </summary>
/// <param name="builder">The builder to add the provider to.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <returns>The value of <paramref name="builder"/>.</returns>
[Experimental]
public static IHealthChecksBuilder AddApplicationLifecycleHealthCheck(this IHealthChecksBuilder builder, params string[] tags)
=> Throw.IfNull(builder)
.AddCheck<ApplicationLifecycleHealthCheck>("ApplicationLifecycleHealthCheck", tags: Throw.IfNull(tags));
.AddCheck<ApplicationLifecycleHealthCheck>("ApplicationLifecycle", tags: Throw.IfNull(tags));

/// <summary>
/// Registers a health check provider that's tied to the application's lifecycle.
Expand All @@ -44,5 +32,5 @@ public static IHealthChecksBuilder AddApplicationLifecycleHealthCheck(this IHeal
/// <exception cref="ArgumentNullException">If <paramref name="builder" /> or <paramref name="tags"/> are <see langword="null" />.</exception>
public static IHealthChecksBuilder AddApplicationLifecycleHealthCheck(this IHealthChecksBuilder builder, IEnumerable<string> tags)
=> Throw.IfNull(builder)
.AddCheck<ApplicationLifecycleHealthCheck>("ApplicationLifecycleHealthCheck", tags: Throw.IfNull(tags));
.AddCheck<ApplicationLifecycleHealthCheck>("ApplicationLifecycle", tags: Throw.IfNull(tags));
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,8 @@

namespace Microsoft.Extensions.Diagnostics.HealthChecks;

public static partial class CoreHealthChecksExtensions
public static partial class CommonHealthChecksExtensions
{
/// <summary>
/// Registers a health check provider that enables manual control of the application's health.
/// </summary>
/// <param name="builder">The builder to add the provider to.</param>
/// <returns>The value of <paramref name="builder"/>.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="builder" /> is <see langword="null" />.</exception>
public static IHealthChecksBuilder AddManualHealthCheck(this IHealthChecksBuilder builder)
=> Throw.IfNull(builder)
.AddManualHealthCheckDependencies()
.AddCheck<ManualHealthCheckService>("ManualHealthCheck");

/// <summary>
/// Registers a health check provider that enables manual control of the application's health.
/// </summary>
Expand All @@ -31,7 +20,7 @@ public static IHealthChecksBuilder AddManualHealthCheck(this IHealthChecksBuilde
public static IHealthChecksBuilder AddManualHealthCheck(this IHealthChecksBuilder builder, params string[] tags)
=> Throw.IfNull(builder)
.AddManualHealthCheckDependencies()
.AddCheck<ManualHealthCheckService>("ManualHealthCheck", tags: Throw.IfNull(tags));
.AddCheck<ManualHealthCheckService>("Manual", tags: Throw.IfNull(tags));

/// <summary>
/// Registers a health check provider that enables manual control of the application's health.
Expand All @@ -43,7 +32,7 @@ public static IHealthChecksBuilder AddManualHealthCheck(this IHealthChecksBuilde
public static IHealthChecksBuilder AddManualHealthCheck(this IHealthChecksBuilder builder, IEnumerable<string> tags)
=> Throw.IfNull(builder)
.AddManualHealthCheckDependencies()
.AddCheck<ManualHealthCheckService>("ManualHealthCheck", tags: Throw.IfNull(tags));
.AddCheck<ManualHealthCheckService>("Manual", tags: Throw.IfNull(tags));

/// <summary>
/// Sets the manual health check to the healthy state.
Expand All @@ -63,8 +52,8 @@ public static void ReportUnhealthy(this IManualHealthCheck manualHealthCheck, st
=> Throw.IfNull(manualHealthCheck).Result = HealthCheckResult.Unhealthy(Throw.IfNullOrWhitespace(reason));

private static IHealthChecksBuilder AddManualHealthCheckDependencies(this IHealthChecksBuilder builder)
=> builder
.Services.AddSingleton<IManualHealthCheckTracker, ManualHealthCheckTracker>()
=> builder.Services
.AddSingleton<ManualHealthCheckTracker>()
.AddTransient(typeof(IManualHealthCheck<>), typeof(ManualHealthCheck<>))
.AddHealthChecks();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Microsoft.Extensions.Diagnostics.HealthChecks;

public static partial class CoreHealthChecksExtensions
public static partial class CommonHealthChecksExtensions
{
/// <summary>
/// Registers a health check publisher which emits telemetry representing the application's health.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ public HealthCheckResult Result
}
}

private readonly IManualHealthCheckTracker _tracker;
private readonly ManualHealthCheckTracker _tracker;

[SuppressMessage("Major Code Smell", "S3366:\"this\" should not be exposed from constructors", Justification = "It's OK, just registering into a list")]
public ManualHealthCheck(IManualHealthCheckTracker tracker)
public ManualHealthCheck(ManualHealthCheckTracker tracker)
{
Result = HealthCheckResult.Unhealthy("Initial state");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks;
/// </remarks>
internal sealed class ManualHealthCheckService : IHealthCheck
{
private readonly IManualHealthCheckTracker _tracker;
private readonly ManualHealthCheckTracker _tracker;

public ManualHealthCheckService(IManualHealthCheckTracker tracker)
public ManualHealthCheckService(ManualHealthCheckTracker tracker)
{
_tracker = tracker;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@

namespace Microsoft.Extensions.Diagnostics.HealthChecks;

internal sealed class ManualHealthCheckTracker : IManualHealthCheckTracker
internal sealed class ManualHealthCheckTracker
{
private static readonly HealthCheckResult _healthy = HealthCheckResult.Healthy();

private readonly ConcurrentDictionary<IManualHealthCheck, bool> _checks = new();

/// <inheritdoc />
public void Register(IManualHealthCheck check)
{
_ = _checks.AddOrUpdate(check, true, (_, _) => true);
Expand All @@ -25,7 +24,6 @@ public void Unregister(IManualHealthCheck checkToRemove)
_ = _checks.TryRemove(checkToRemove, out _);
}

/// <inheritdoc />
public HealthCheckResult GetHealthCheckResult()
{
// Construct string showing all reasons for unhealthy manual health checks
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.Common, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"Types": [
{
"Type": "static class Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions",
"Stage": "Stable",
"Methods": [
{
"Member": "static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.AddApplicationLifecycleHealthCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, params string[] tags);",
"Stage": "Stable"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.AddApplicationLifecycleHealthCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Collections.Generic.IEnumerable<string> tags);",
"Stage": "Stable"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.AddManualHealthCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, params string[] tags);",
"Stage": "Stable"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.AddManualHealthCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, System.Collections.Generic.IEnumerable<string> tags);",
"Stage": "Stable"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.AddTelemetryHealthCheckPublisher(this Microsoft.Extensions.DependencyInjection.IServiceCollection services);",
"Stage": "Stable"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.AddTelemetryHealthCheckPublisher(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.Configuration.IConfigurationSection section);",
"Stage": "Experimental"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.AddTelemetryHealthCheckPublisher(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.Extensions.Diagnostics.HealthChecks.TelemetryHealthCheckPublisherOptions> configure);",
"Stage": "Experimental"
},
{
"Member": "static void Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.ReportHealthy(this Microsoft.Extensions.Diagnostics.HealthChecks.IManualHealthCheck manualHealthCheck);",
"Stage": "Stable"
},
{
"Member": "static void Microsoft.Extensions.Diagnostics.HealthChecks.CommonHealthChecksExtensions.ReportUnhealthy(this Microsoft.Extensions.Diagnostics.HealthChecks.IManualHealthCheck manualHealthCheck, string reason);",
"Stage": "Stable"
}
]
},
{
"Type": "interface Microsoft.Extensions.Diagnostics.HealthChecks.IManualHealthCheck : System.IDisposable",
"Stage": "Stable",
"Properties": [
{
"Member": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult Microsoft.Extensions.Diagnostics.HealthChecks.IManualHealthCheck.Result { get; set; }",
"Stage": "Stable"
}
]
},
{
"Type": "interface Microsoft.Extensions.Diagnostics.HealthChecks.IManualHealthCheck<T> : Microsoft.Extensions.Diagnostics.HealthChecks.IManualHealthCheck, System.IDisposable",
"Stage": "Stable"
},
{
"Type": "class Microsoft.Extensions.Diagnostics.HealthChecks.TelemetryHealthCheckPublisherOptions",
"Stage": "Experimental",
"Methods": [
{
"Member": "Microsoft.Extensions.Diagnostics.HealthChecks.TelemetryHealthCheckPublisherOptions.TelemetryHealthCheckPublisherOptions();",
"Stage": "Experimental"
}
],
"Properties": [
{
"Member": "bool Microsoft.Extensions.Diagnostics.HealthChecks.TelemetryHealthCheckPublisherOptions.LogOnlyUnhealthy { get; set; }",
"Stage": "Experimental"
}
]
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ public class TelemetryHealthCheckPublisherOptions
/// <remarks>
/// Default set to false.
/// </remarks>
[Experimental]
public bool LogOnlyUnhealthy { get; set; }
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit f4952b6

Please sign in to comment.