Implement Timeout

\n\nCommit migrated from 50593c9336
This commit is contained in:
Nathanael Marchand 2019-02-19 10:23:49 +01:00 committed by Ryan Nowak
parent 34b8fc2158
commit 7960bde7da
7 changed files with 332 additions and 25 deletions

View File

@ -11,11 +11,14 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
public sealed partial class HealthCheckRegistration
{
public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { }
public HealthCheckRegistration(string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan? timeout) { }
public HealthCheckRegistration(string name, System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { }
public HealthCheckRegistration(string name, System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> factory, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan? timeout) { }
public System.Func<System.IServiceProvider, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck> Factory { get { throw null; } set { } }
public Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus FailureStatus { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public string Name { get { throw null; } set { } }
public System.Collections.Generic.ISet<string> Tags { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.TimeSpan Timeout { get { throw null; } set { } }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public partial struct HealthCheckResult

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@ -24,6 +24,22 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
{
private Func<IServiceProvider, IHealthCheck> _factory;
private string _name;
private TimeSpan _timeout;
/// <summary>
/// Creates a new <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckRegistration" /> for an existing <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck" /> instance.
/// </summary>
/// <param name="name">The health check name.</param>
/// <param name="instance">The <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck" /> instance.</param>
/// <param name="failureStatus">
/// The <see cref="T:Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus" /> that should be reported upon failure of the health check. If the provided value
/// is <c>null</c>, then <see cref="F:Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Unhealthy" /> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used for filtering health checks.</param>
public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable<string> tags)
: this(name, instance, failureStatus, tags, default)
{
}
/// <summary>
/// Creates a new <see cref="HealthCheckRegistration"/> for an existing <see cref="IHealthCheck"/> instance.
@ -35,7 +51,8 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used for filtering health checks.</param>
public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable<string> tags)
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
public HealthCheckRegistration(string name, IHealthCheck instance, HealthStatus? failureStatus, IEnumerable<string> tags, TimeSpan? timeout)
{
if (name == null)
{
@ -47,10 +64,16 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
throw new ArgumentNullException(nameof(instance));
}
if (timeout <= TimeSpan.Zero && timeout != System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentOutOfRangeException(nameof(timeout));
}
Name = name;
FailureStatus = failureStatus ?? HealthStatus.Unhealthy;
Tags = new HashSet<string>(tags ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
Factory = (_) => instance;
Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan;
}
/// <summary>
@ -68,6 +91,27 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
Func<IServiceProvider, IHealthCheck> factory,
HealthStatus? failureStatus,
IEnumerable<string> tags)
: this(name, factory, failureStatus, tags, default)
{
}
/// <summary>
/// Creates a new <see cref="HealthCheckRegistration"/> for an existing <see cref="IHealthCheck"/> instance.
/// </summary>
/// <param name="name">The health check name.</param>
/// <param name="factory">A delegate used to create the <see cref="IHealthCheck"/> instance.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used for filtering health checks.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
public HealthCheckRegistration(
string name,
Func<IServiceProvider, IHealthCheck> factory,
HealthStatus? failureStatus,
IEnumerable<string> tags,
TimeSpan? timeout)
{
if (name == null)
{
@ -79,10 +123,16 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
throw new ArgumentNullException(nameof(factory));
}
if (timeout <= TimeSpan.Zero && timeout != System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentOutOfRangeException(nameof(timeout));
}
Name = name;
FailureStatus = failureStatus ?? HealthStatus.Unhealthy;
Tags = new HashSet<string>(tags ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
Factory = factory;
Timeout = timeout ?? System.Threading.Timeout.InfiniteTimeSpan;
}
/// <summary>
@ -107,6 +157,23 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
/// </summary>
public HealthStatus FailureStatus { get; set; }
/// <summary>
/// Gets or sets the timeout used for the test.
/// </summary>
public TimeSpan Timeout
{
get => _timeout;
set
{
if (value <= TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
_timeout = value;
}
}
/// <summary>
/// Gets or sets the health check name.
/// </summary>

View File

@ -5,18 +5,25 @@ namespace Microsoft.Extensions.DependencyInjection
{
public static partial class HealthChecksBuilderAddCheckExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck instance, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default(Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus?), System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, System.Collections.Generic.IEnumerable<string> tags, System.TimeSpan timeout, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddTypeActivatedCheck<T>(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, params object[] args) where T : class, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck { throw null; }
}
public static partial class HealthChecksBuilderDelegateExtensions
{
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddAsyncCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCheck(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string name, System.Func<System.Threading.CancellationToken, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> check, System.Collections.Generic.IEnumerable<string> tags = null, System.TimeSpan? timeout = default(System.TimeSpan?)) { throw null; }
}
public static partial class HealthCheckServiceCollectionExtensions
{

View File

@ -91,9 +91,21 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
Log.HealthCheckBegin(_logger, registration);
HealthReportEntry entry;
CancellationTokenSource timeoutCancellationTokenSource = null;
try
{
var result = await healthCheck.CheckHealthAsync(context, cancellationToken);
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);
var duration = stopwatch.GetElapsedTime();
entry = new HealthReportEntry(
@ -107,7 +119,20 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
Log.HealthCheckData(_logger, registration, entry);
}
// Allow cancellation to propagate.
catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested)
{
var duration = stopwatch.GetElapsedTime();
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();
@ -121,6 +146,11 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
Log.HealthCheckError(_logger, registration, ex, duration);
}
finally
{
timeoutCancellationTokenSource?.Dispose();
}
return entry;
}
}

View File

@ -24,12 +24,37 @@ namespace Microsoft.Extensions.DependencyInjection
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
IHealthCheck instance,
HealthStatus? failureStatus,
IEnumerable<string> tags)
{
return AddCheck(builder, name, instance, failureStatus, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="instance">An <see cref="IHealthCheck"/> instance.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
IHealthCheck instance,
HealthStatus? failureStatus = null,
IEnumerable<string> tags = null)
IEnumerable<string> tags = null,
TimeSpan? timeout = null)
{
if (builder == null)
{
@ -46,7 +71,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(instance));
}
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus, tags, timeout));
}
/// <summary>
@ -63,15 +88,45 @@ namespace Microsoft.Extensions.DependencyInjection
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.GetServiceOrCreateInstance{T}(IServiceProvider)"/> to create the health check
/// instance when needed. If a service of type <typeparamref name="T"/> is registred in the dependency injection container
/// with any liftime it will be used. Otherwise an instance of type <typeparamref name="T"/> will be constructed with
/// instance when needed. If a service of type <typeparamref name="T"/> is registered in the dependency injection container
/// with any lifetime it will be used. Otherwise an instance of type <typeparamref name="T"/> will be constructed with
/// access to services from the dependency injection container.
/// </remarks>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus,
IEnumerable<string> tags) where T : class, IHealthCheck
{
return AddCheck<T>(builder, name, failureStatus, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.GetServiceOrCreateInstance{T}(IServiceProvider)"/> to create the health check
/// instance when needed. If a service of type <typeparamref name="T"/> is registered in the dependency injection container
/// with any lifetime it will be used. Otherwise an instance of type <typeparamref name="T"/> will be constructed with
/// access to services from the dependency injection container.
/// </remarks>
public static IHealthChecksBuilder AddCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus = null,
IEnumerable<string> tags = null) where T : class, IHealthCheck
IEnumerable<string> tags = null,
TimeSpan? timeout = null) where T : class, IHealthCheck
{
if (builder == null)
{
@ -83,7 +138,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(name));
}
return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance<T>(s), failureStatus, tags));
return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.GetServiceOrCreateInstance<T>(s), failureStatus, tags, timeout));
}
// NOTE: AddTypeActivatedCheck has overloads rather than default parameters values, because default parameter values don't
@ -187,5 +242,44 @@ namespace Microsoft.Extensions.DependencyInjection
return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance<T>(s, args), failureStatus, tags));
}
/// <summary>
/// Adds a new type activated health check with the specified name and implementation.
/// </summary>
/// <typeparam name="T">The health check implementation type.</typeparam>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="failureStatus">
/// The <see cref="HealthStatus"/> that should be reported when the health check reports a failure. If the provided value
/// is <c>null</c>, then <see cref="HealthStatus.Unhealthy"/> will be reported.
/// </param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="args">Additional arguments to provide to the constructor.</param>
/// <param name="timeout">A <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
/// <remarks>
/// This method will use <see cref="ActivatorUtilities.CreateInstance{T}(IServiceProvider, object[])"/> to create the health check
/// instance when needed. Additional arguments can be provided to the constructor via <paramref name="args"/>.
/// </remarks>
public static IHealthChecksBuilder AddTypeActivatedCheck<T>(
this IHealthChecksBuilder builder,
string name,
HealthStatus? failureStatus,
IEnumerable<string> tags,
TimeSpan timeout,
params object[] args) where T : class, IHealthCheck
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return builder.Add(new HealthCheckRegistration(name, s => ActivatorUtilities.CreateInstance<T>(s, args), failureStatus, tags, timeout));
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@ -22,11 +22,31 @@ namespace Microsoft.Extensions.DependencyInjection
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<HealthCheckResult> check,
IEnumerable<string> tags = null)
IEnumerable<string> tags)
{
return AddCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<HealthCheckResult> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
@ -44,7 +64,7 @@ namespace Microsoft.Extensions.DependencyInjection
}
var instance = new DelegateHealthCheck((ct) => Task.FromResult(check()));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
/// <summary>
@ -55,11 +75,31 @@ namespace Microsoft.Extensions.DependencyInjection
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, HealthCheckResult> check,
IEnumerable<string> tags = null)
IEnumerable<string> tags)
{
return AddCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, HealthCheckResult> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
@ -77,7 +117,7 @@ namespace Microsoft.Extensions.DependencyInjection
}
var instance = new DelegateHealthCheck((ct) => Task.FromResult(check(ct)));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
/// <summary>
@ -88,11 +128,31 @@ namespace Microsoft.Extensions.DependencyInjection
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<Task<HealthCheckResult>> check,
IEnumerable<string> tags = null)
IEnumerable<string> tags)
{
return AddAsyncCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<Task<HealthCheckResult>> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
@ -110,7 +170,7 @@ namespace Microsoft.Extensions.DependencyInjection
}
var instance = new DelegateHealthCheck((ct) => check());
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
/// <summary>
@ -121,11 +181,31 @@ namespace Microsoft.Extensions.DependencyInjection
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
// 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, Task<HealthCheckResult>> check,
IEnumerable<string> tags = null)
IEnumerable<string> tags)
{
return AddAsyncCheck(builder, name, check, tags, default);
}
/// <summary>
/// Adds a new health check with the specified name and implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/>.</param>
/// <param name="name">The name of the health check.</param>
/// <param name="tags">A list of tags that can be used to filter health checks.</param>
/// <param name="check">A delegate that provides the health check implementation.</param>
/// <param name="timeout">An optional <see cref="TimeSpan"/> representing the timeout of the check.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddAsyncCheck(
this IHealthChecksBuilder builder,
string name,
Func<CancellationToken, Task<HealthCheckResult>> check,
IEnumerable<string> tags = null,
TimeSpan? timeout = default)
{
if (builder == null)
{
@ -143,7 +223,7 @@ namespace Microsoft.Extensions.DependencyInjection
}
var instance = new DelegateHealthCheck((ct) => check(ct));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags));
return builder.Add(new HealthCheckRegistration(name, instance, failureStatus: null, tags, timeout));
}
}
}

View File

@ -424,6 +424,32 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
});
}
[Fact]
public async Task CheckHealthAsync_TimeoutReturnsUnhealthy()
{
// Arrange
var service = CreateHealthChecksService(b =>
{
b.AddAsyncCheck("timeout", async (ct) =>
{
await Task.Delay(2000, ct);
return HealthCheckResult.Healthy();
}, timeout: TimeSpan.FromMilliseconds(100));
});
// Act
var results = await service.CheckHealthAsync();
// Assert
Assert.Collection(
results.Entries,
actual =>
{
Assert.Equal("timeout", actual.Key);
Assert.Equal(HealthStatus.Unhealthy, actual.Value.Status);
});
}
private static DefaultHealthCheckService CreateHealthChecksService(Action<IHealthChecksBuilder> configure)
{
var services = new ServiceCollection();