Updating Health Checks for 2.2

A bunch of small changes and updates for 2.2 focused at making our main
scenarios more streamlined and focused. Also adds samples for
extensibility we support so far.

A list of changes:

Clearing baselines for these projects. We didn't ship anything in 2.1 so
there should be nothing in the baselines.

--

The middleware now uses Map for path matching. This makes the actual
`HealthCheckMiddleware` more standalone. This will make it easy to use
with Dispatcher/Endpoint Routing in the future.

This also manifests by removing Path from HealthCheckOptions - the path
is an explicit argument to the UseHealthChecks middelware - this
streamlines the design for 3.0.

--

Added extensibility for customizing the status codes (aspnet/Home#2584)

--

Added extensibility for writing the textual output (aspnet/Home#2583)

--

Changed the default output to be `text/plain`. The most common use cases
for health checks don't include a detailed status.

The existing output format is still available as an option.
This commit is contained in:
Ryan Nowak 2018-07-31 16:34:55 -07:00 коммит произвёл Ryan Nowak
Родитель bf839dadf1
Коммит 47f427d5ac
21 изменённых файлов: 763 добавлений и 1026 удалений

Просмотреть файл

@ -16,6 +16,7 @@
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>2.2.0-preview1-34823</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
<MicrosoftEntityFrameworkCoreRelationalPackageVersion>2.2.0-preview1-34823</MicrosoftEntityFrameworkCoreRelationalPackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>2.2.0-preview1-34823</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsDiagnosticAdapterPackageVersion>

Просмотреть файл

@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace HealthChecksSample
{
// Pass in `--scenario basic` at the command line to run this sample.
public class BasicStartup
{
public void ConfigureServices(IServiceCollection services)
{
// Registers required services for health checks
services.AddHealthChecks();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// This will register the health checks middleware at the URL /health.
//
// By default health checks will return a 200 with 'Healthy'.
// - No health checks are registered by default, the app is healthy if it is reachable
// - The default response writer writes the HealthCheckStatus as text/plain content
//
// This is the simplest way to use health checks, it is suitable for systems
// that want to check for 'liveness' of an application.
app.UseHealthChecks("/health");
app.Run(async (context) =>
{
await context.Response.WriteAsync("Go to /health to see the health status");
});
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace HealthChecksSample
{
// Pass in `--scenario writer` at the command line to run this sample.
public class CustomWriterStartup
{
public void ConfigureServices(IServiceCollection services)
{
// Registers required services for health checks
services.AddHealthChecks();
// This is an example of registering a custom health check as a service.
// All IHealthCheck services will be available to the health check service and
// middleware.
//
// We recommend registering all health checks as Singleton services.
services.AddSingleton<IHealthCheck, GCInfoHealthCheck>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// This will register the health checks middleware at the URL /health
//
// This example overrides the HealthCheckResponseWriter to write the health
// check result in a totally custom way.
app.UseHealthChecks("/health", new HealthCheckOptions()
{
// This custom writer formats the detailed status as an HTML table.
ResponseWriter = WriteResponse,
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Go to /health to see the health status");
});
}
private static Task WriteResponse(HttpContext httpContext, CompositeHealthCheckResult result)
{
httpContext.Response.ContentType = "text/html";
return httpContext.Response.WriteAsync($@"
<html>
<body>
<h1>
Everything is {result.Status}
</h1>
<table>
<thead>
<tr><td>Name</td><td>Status</td></tr>
</thead>
<tbody>
{string.Join("", result.Results.Select(kvp => $"<tr><td>{kvp.Key}</td><td>{kvp.Value.Status}</td></tr>"))}
</tbody>
</table>
</body>
</html>");
}
}
}

Просмотреть файл

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace HealthChecksSample
{
// Pass in `--scenario detailed` at the command line to run this sample.
public class DetailedStatusStartup
{
public void ConfigureServices(IServiceCollection services)
{
// Registers required services for health checks
services
.AddHealthChecks()
// Registers a custom health check, in this case it will execute an
// inline delegate.
.AddCheck("GC Info", () =>
{
// This example will report degraded status if the application is using
// more than 1gb of memory.
//
// Additionally we include some GC info in the reported diagnostics.
var allocated = GC.GetTotalMemory(forceFullCollection: false);
var data = new Dictionary<string, object>()
{
{ "Allocated", allocated },
{ "Gen0Collections", GC.CollectionCount(0) },
{ "Gen1Collections", GC.CollectionCount(1) },
{ "Gen2Collections", GC.CollectionCount(2) },
};
// Report degraded status if the allocated memory is >= 1gb (in bytes)
var status = allocated >= 1024 * 1024 * 1024 ? HealthCheckStatus.Degraded : HealthCheckStatus.Healthy;
return Task.FromResult(new HealthCheckResult(
status,
exception: null,
description: "reports degraded status if allocated bytes >= 1gb",
data: data));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// This will register the health checks middleware at the URL /health
//
// This example overrides the ResponseWriter to include a detailed
// status as JSON. Use this response writer (or create your own) to include
// detailed diagnostic information for use by a monitoring system.
app.UseHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = HealthCheckResponseWriters.WriteDetailedJson,
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Go to /health to see the health status");
});
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace HealthChecksSample
{
// This is an example of a custom health check that implements IHealthCheck.
// This is the same core logic as the DetailedStatusStartup example.
// See CustomWriterStartup to see how this is registered.
public class GCInfoHealthCheck : IHealthCheck
{
public string Name { get; } = "GCInfo";
public Task<HealthCheckResult> CheckHealthAsync(CancellationToken cancellationToken = default(CancellationToken))
{
// This example will report degraded status if the application is using
// more than 1gb of memory.
//
// Additionally we include some GC info in the reported diagnostics.
var allocated = GC.GetTotalMemory(forceFullCollection: false);
var data = new Dictionary<string, object>()
{
{ "Allocated", allocated },
{ "Gen0Collections", GC.CollectionCount(0) },
{ "Gen1Collections", GC.CollectionCount(1) },
{ "Gen2Collections", GC.CollectionCount(2) },
};
// Report degraded status if the allocated memory is >= 1gb (in bytes)
var status = allocated >= 1024 * 1024 * 1024 ? HealthCheckStatus.Degraded : HealthCheckStatus.Healthy;
return Task.FromResult(new HealthCheckResult(
status,
exception: null,
description: "reports degraded status if allocated bytes >= 1gb",
data: data));
}
}
}

Просмотреть файл

@ -1,10 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<!-- Used in our tests -->
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">netcoreapp2.0;net461</TargetFrameworks>
<TargetFrameworks Condition="'$(TargetFrameworks)'==''">netcoreapp2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(MicrosoftAspNetCoreStaticFilesPackageVersion)" />

Просмотреть файл

@ -1,23 +1,54 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace HealthChecksSample
{
public class Program
{
private static readonly Dictionary<string, Type> _scenarios;
static Program()
{
_scenarios = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
{
{ "", typeof(BasicStartup) },
{ "basic", typeof(BasicStartup) },
{ "detailed", typeof(DetailedStatusStartup) },
{ "writer", typeof(CustomWriterStartup) },
};
}
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
new WebHostBuilder()
public static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
.AddCommandLine(args)
.Build();
var scenario = config["scenario"] ?? string.Empty;
if (!_scenarios.TryGetValue(scenario, out var startupType))
{
startupType = typeof(BasicStartup);
}
return new WebHostBuilder()
.UseConfiguration(config)
.ConfigureLogging(builder =>
{
builder.AddConsole();
})
.UseKestrel()
.UseStartup<Startup>()
.UseStartup(startupType)
.Build();
}
}
}

Просмотреть файл

@ -1,28 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace HealthChecksSample
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHealthChecks("/health");
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
// 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;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// <see cref="IApplicationBuilder"/> extension methods for the <see cref="HealthCheckMiddleware"/>.
/// </summary>
public static class HealthCheckApplicationBuilderExtensions
{
/// <summary>
/// Adds a middleware that provides health check status.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
/// <param name="path">The path on which to provide health check status.</param>
/// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
/// <remarks>
/// The health check middleware will use default settings other than the provided <paramref name="path"/>.
/// </remarks>
public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (!path.HasValue)
{
throw new ArgumentException("A URL path must be provided", nameof(path));
}
return app.Map(path, b => b.UseMiddleware<HealthCheckMiddleware>());
}
/// <summary>
/// Adds a middleware that provides health check status.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
/// <param name="path">The path on which to provide health check status.</param>
/// <param name="options">A <see cref="HealthCheckOptions"/> used to configure the middleware.</param>
/// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path, HealthCheckOptions options)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (!path.HasValue)
{
throw new ArgumentException("A URL path must be provided", nameof(path));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
return app.Map(path, b => b.UseMiddleware<HealthCheckMiddleware>(Options.Create(options)));
}
}
}

Просмотреть файл

@ -1,32 +0,0 @@
// 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;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// <see cref="IApplicationBuilder"/> extension methods for the <see cref="HealthCheckMiddleware"/>.
/// </summary>
public static class HealthCheckAppBuilderExtensions
{
/// <summary>
/// Adds a middleware that provides a REST API for requesting health check status.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
/// <param name="path">The path on which to provide the API.</param>
/// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path)
{
app = app ?? throw new ArgumentNullException(nameof(app));
return app.UseMiddleware<HealthCheckMiddleware>(Options.Create(new HealthCheckOptions()
{
Path = path
}));
}
}
}

Просмотреть файл

@ -2,14 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
{
@ -19,60 +15,62 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
private readonly HealthCheckOptions _healthCheckOptions;
private readonly IHealthCheckService _healthCheckService;
public HealthCheckMiddleware(RequestDelegate next, IOptions<HealthCheckOptions> healthCheckOptions, IHealthCheckService healthCheckService)
public HealthCheckMiddleware(
RequestDelegate next,
IOptions<HealthCheckOptions> healthCheckOptions,
IHealthCheckService healthCheckService)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (healthCheckOptions == null)
{
throw new ArgumentNullException(nameof(healthCheckOptions));
}
if (healthCheckService == null)
{
throw new ArgumentNullException(nameof(healthCheckService));
}
_next = next;
_healthCheckOptions = healthCheckOptions.Value;
_healthCheckService = healthCheckService;
}
/// <summary>
/// Process an individual request.
/// Processes a request.
/// </summary>
/// <param name="context"></param>
/// <param name="httpContext"></param>
/// <returns></returns>
public async Task InvokeAsync(HttpContext context)
public async Task InvokeAsync(HttpContext httpContext)
{
if (context.Request.Path == _healthCheckOptions.Path)
if (httpContext == null)
{
// Get results
var result = await _healthCheckService.CheckHealthAsync(context.RequestAborted);
// Map status to response code
switch (result.Status)
{
case HealthCheckStatus.Failed:
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
break;
case HealthCheckStatus.Unhealthy:
context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
break;
case HealthCheckStatus.Degraded:
// Degraded doesn't mean unhealthy so we return 200, but the content will contain more details
context.Response.StatusCode = StatusCodes.Status200OK;
break;
case HealthCheckStatus.Healthy:
context.Response.StatusCode = StatusCodes.Status200OK;
break;
default:
// This will only happen when we change HealthCheckStatus and we don't update this.
Debug.Fail($"Unrecognized HealthCheckStatus value: {result.Status}");
throw new InvalidOperationException($"Unrecognized HealthCheckStatus value: {result.Status}");
}
// Render results to JSON
var json = new JObject(
new JProperty("status", result.Status.ToString()),
new JProperty("results", new JObject(result.Results.Select(pair =>
new JProperty(pair.Key, new JObject(
new JProperty("status", pair.Value.Status.ToString()),
new JProperty("description", pair.Value.Description),
new JProperty("data", new JObject(pair.Value.Data.Select(p => new JProperty(p.Key, p.Value))))))))));
await context.Response.WriteAsync(json.ToString(Formatting.None));
throw new ArgumentNullException(nameof(httpContext));
}
else
// Get results
var result = await _healthCheckService.CheckHealthAsync(httpContext.RequestAborted);
// Map status to response code - this is customizable via options.
if (!_healthCheckOptions.ResultStatusCodes.TryGetValue(result.Status, out var statusCode))
{
await _next(context);
var message =
$"No status code mapping found for {nameof(HealthCheckStatus)} value: {result.Status}." +
$"{nameof(HealthCheckOptions)}.{nameof(HealthCheckOptions.ResultStatusCodes)} must contain" +
$"an entry for {result.Status}.";
throw new InvalidOperationException(message);
}
httpContext.Response.StatusCode = statusCode;
if (_healthCheckOptions.ResponseWriter != null)
{
await _healthCheckOptions.ResponseWriter(httpContext, result);
}
}
}

Просмотреть файл

@ -1,7 +1,11 @@
// 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;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
{
@ -10,9 +14,23 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
/// </summary>
public class HealthCheckOptions
{
public IDictionary<HealthCheckStatus, int> ResultStatusCodes { get; } = new Dictionary<HealthCheckStatus, int>()
{
{ HealthCheckStatus.Healthy, StatusCodes.Status200OK },
{ HealthCheckStatus.Degraded, StatusCodes.Status200OK },
{ HealthCheckStatus.Unhealthy, StatusCodes.Status503ServiceUnavailable },
// This means that a health check failed, so 500 is appropriate. This is an error.
{ HealthCheckStatus.Failed, StatusCodes.Status500InternalServerError },
};
/// <summary>
/// Gets or sets the path at which the Health Check results will be available.
/// Gets or sets a delegate used to write the response.
/// </summary>
public PathString Path { get; set; }
/// <remarks>
/// The default value is a delegate that will write a minimal <c>text/plain</c> response with the value
/// of <see cref="CompositeHealthCheckResult.Status"/> as a string.
/// </remarks>
public Func<HttpContext, CompositeHealthCheckResult, Task> ResponseWriter { get; set; } = HealthCheckResponseWriters.WriteMinimalPlaintext;
}
}

Просмотреть файл

@ -0,0 +1,56 @@
// 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;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
{
public static class HealthCheckResponseWriters
{
public static Task WriteMinimalPlaintext(HttpContext httpContext, CompositeHealthCheckResult result)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
httpContext.Response.ContentType = "text/plain";
return httpContext.Response.WriteAsync(result.Status.ToString());
}
public static Task WriteDetailedJson(HttpContext httpContext, CompositeHealthCheckResult result)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
httpContext.Response.ContentType = "application/json";
var json = new JObject(
new JProperty("status", result.Status.ToString()),
new JProperty("results", new JObject(result.Results.Select(pair =>
new JProperty(pair.Key, new JObject(
new JProperty("status", pair.Value.Status.ToString()),
new JProperty("description", pair.Value.Description),
new JProperty("data", new JObject(pair.Value.Data.Select(p => new JProperty(p.Key, p.Value))))))))));
return httpContext.Response.WriteAsync(json.ToString(Formatting.Indented));
}
}
}

Просмотреть файл

@ -1,115 +1,5 @@
{
"AssemblyIdentity": "Microsoft.AspNetCore.Diagnostics.HealthChecks, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"Types": [
{
"Name": "Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckMiddleware",
"Visibility": "Public",
"Kind": "Class",
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "InvokeAsync",
"Parameters": [
{
"Name": "context",
"Type": "Microsoft.AspNetCore.Http.HttpContext"
}
],
"ReturnType": "System.Threading.Tasks.Task",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [
{
"Name": "next",
"Type": "Microsoft.AspNetCore.Http.RequestDelegate"
},
{
"Name": "healthCheckOptions",
"Type": "Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions>"
},
{
"Name": "healthCheckService",
"Type": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheckService"
}
],
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions",
"Visibility": "Public",
"Kind": "Class",
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "get_Path",
"Parameters": [],
"ReturnType": "Microsoft.AspNetCore.Http.PathString",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "set_Path",
"Parameters": [
{
"Name": "value",
"Type": "Microsoft.AspNetCore.Http.PathString"
}
],
"ReturnType": "System.Void",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [],
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.AspNetCore.Builder.HealthCheckAppBuilderExtensions",
"Visibility": "Public",
"Kind": "Class",
"Abstract": true,
"Static": true,
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "UseHealthChecks",
"Parameters": [
{
"Name": "app",
"Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder"
},
{
"Name": "path",
"Type": "Microsoft.AspNetCore.Http.PathString"
}
],
"ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
}
]
}

Просмотреть файл

@ -1,377 +1,5 @@
{
"AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"Types": [
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Visibility": "Public",
"Kind": "Struct",
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "get_Status",
"Parameters": [],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckStatus",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "get_Exception",
"Parameters": [],
"ReturnType": "System.Exception",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "get_Description",
"Parameters": [],
"ReturnType": "System.String",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "get_Data",
"Parameters": [],
"ReturnType": "System.Collections.Generic.IReadOnlyDictionary<System.String, System.Object>",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unhealthy",
"Parameters": [],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unhealthy",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unhealthy",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "data",
"Type": "System.Collections.Generic.IReadOnlyDictionary<System.String, System.Object>"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unhealthy",
"Parameters": [
{
"Name": "exception",
"Type": "System.Exception"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unhealthy",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "exception",
"Type": "System.Exception"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Unhealthy",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "exception",
"Type": "System.Exception"
},
{
"Name": "data",
"Type": "System.Collections.Generic.IReadOnlyDictionary<System.String, System.Object>"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Healthy",
"Parameters": [],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Healthy",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Healthy",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "data",
"Type": "System.Collections.Generic.IReadOnlyDictionary<System.String, System.Object>"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Degraded",
"Parameters": [],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Degraded",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Degraded",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "data",
"Type": "System.Collections.Generic.IReadOnlyDictionary<System.String, System.Object>"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Degraded",
"Parameters": [
{
"Name": "exception",
"Type": "System.Exception"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Degraded",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "exception",
"Type": "System.Exception"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "Degraded",
"Parameters": [
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "exception",
"Type": "System.Exception"
},
{
"Name": "data",
"Type": "System.Collections.Generic.IReadOnlyDictionary<System.String, System.Object>"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult",
"Static": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [
{
"Name": "status",
"Type": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckStatus"
},
{
"Name": "exception",
"Type": "System.Exception"
},
{
"Name": "description",
"Type": "System.String"
},
{
"Name": "data",
"Type": "System.Collections.Generic.IReadOnlyDictionary<System.String, System.Object>"
}
],
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckStatus",
"Visibility": "Public",
"Kind": "Enumeration",
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Field",
"Name": "Unknown",
"Parameters": [],
"GenericParameter": [],
"Literal": "0"
},
{
"Kind": "Field",
"Name": "Failed",
"Parameters": [],
"GenericParameter": [],
"Literal": "1"
},
{
"Kind": "Field",
"Name": "Unhealthy",
"Parameters": [],
"GenericParameter": [],
"Literal": "2"
},
{
"Kind": "Field",
"Name": "Degraded",
"Parameters": [],
"GenericParameter": [],
"Literal": "3"
},
{
"Kind": "Field",
"Name": "Healthy",
"Parameters": [],
"GenericParameter": [],
"Literal": "4"
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck",
"Visibility": "Public",
"Kind": "Interface",
"Abstract": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "get_Name",
"Parameters": [],
"ReturnType": "System.String",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "CheckHealthAsync",
"Parameters": [
{
"Name": "cancellationToken",
"Type": "System.Threading.CancellationToken",
"DefaultValue": "default(System.Threading.CancellationToken)"
}
],
"ReturnType": "System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>",
"GenericParameter": []
}
],
"GenericParameters": []
}
]
}

Просмотреть файл

@ -11,15 +11,10 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
/// A simple implementation of <see cref="IHealthCheck"/> which uses a provided delegate to
/// implement the check.
/// </summary>
public class HealthCheck : IHealthCheck
public sealed class HealthCheck : IHealthCheck
{
private readonly Func<CancellationToken, Task<HealthCheckResult>> _check;
/// <summary>
/// Gets the name of the health check, which should indicate the component being checked.
/// </summary>
public string Name { get; }
/// <summary>
/// Create an instance of <see cref="HealthCheck"/> from the specified <paramref name="name"/> and <paramref name="check"/>.
/// </summary>
@ -27,10 +22,15 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
/// <param name="check">A delegate which provides the code to execute when the health check is run.</param>
public HealthCheck(string name, Func<CancellationToken, Task<HealthCheckResult>> check)
{
Name = name;
_check = check;
Name = name ?? throw new ArgumentNullException(nameof(name));
_check = check ?? throw new ArgumentNullException(nameof(check));
}
/// <summary>
/// Gets the name of the health check, which should indicate the component being checked.
/// </summary>
public string Name { get; }
/// <summary>
/// Runs the health check, returning the status of the component being checked.
/// </summary>

Просмотреть файл

@ -22,8 +22,22 @@ namespace Microsoft.Extensions.DependencyInjection
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(this IHealthChecksBuilder builder, string name, Func<CancellationToken, Task<HealthCheckResult>> check)
{
builder.Services.AddSingleton<IHealthCheck>(services => new HealthCheck(name, check));
return builder;
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (check == null)
{
throw new ArgumentNullException(nameof(check));
}
return builder.AddCheck(new HealthCheck(name, check));
}
/// <summary>
@ -33,7 +47,46 @@ namespace Microsoft.Extensions.DependencyInjection
/// <param name="name">The name of the health check, which should indicate the component being checked.</param>
/// <param name="check">A delegate which provides the code to execute when the health check is run.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(this IHealthChecksBuilder builder, string name, Func<Task<HealthCheckResult>> check) =>
builder.AddCheck(name, _ => check());
public static IHealthChecksBuilder AddCheck(this IHealthChecksBuilder builder, string name, Func<Task<HealthCheckResult>> check)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (check == null)
{
throw new ArgumentNullException(nameof(check));
}
return builder.AddCheck(name, _ => check());
}
/// <summary>
/// Adds a new health check with the implementation.
/// </summary>
/// <param name="builder">The <see cref="IHealthChecksBuilder"/> to add the check to.</param>
/// <param name="check">An <see cref="IHealthCheck"/> implementation.</param>
/// <returns>The <see cref="IHealthChecksBuilder"/>.</returns>
public static IHealthChecksBuilder AddCheck(this IHealthChecksBuilder builder, IHealthCheck check)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (check == null)
{
throw new ArgumentNullException(nameof(check));
}
builder.Services.AddSingleton<IHealthCheck>(check);
return builder;
}
}
}

Просмотреть файл

@ -1,334 +1,5 @@
{
"AssemblyIdentity": "Microsoft.Extensions.Diagnostics.HealthChecks, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60",
"Types": [
{
"Name": "Microsoft.Extensions.DependencyInjection.HealthChecksBuilderAddCheckExtensions",
"Visibility": "Public",
"Kind": "Class",
"Abstract": true,
"Static": true,
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "AddCheck",
"Parameters": [
{
"Name": "builder",
"Type": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder"
},
{
"Name": "name",
"Type": "System.String"
},
{
"Name": "check",
"Type": "System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>>"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "AddCheck",
"Parameters": [
{
"Name": "builder",
"Type": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder"
},
{
"Name": "name",
"Type": "System.String"
},
{
"Name": "check",
"Type": "System.Func<System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>>"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.DependencyInjection.HealthCheckServiceCollectionExtensions",
"Visibility": "Public",
"Kind": "Class",
"Abstract": true,
"Static": true,
"Sealed": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "AddHealthChecks",
"Parameters": [
{
"Name": "services",
"Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection"
}
],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder",
"Static": true,
"Extension": true,
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.CompositeHealthCheckResult",
"Visibility": "Public",
"Kind": "Class",
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "get_Results",
"Parameters": [],
"ReturnType": "System.Collections.Generic.IReadOnlyDictionary<System.String, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "get_Status",
"Parameters": [],
"ReturnType": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckStatus",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [
{
"Name": "results",
"Type": "System.Collections.Generic.IReadOnlyDictionary<System.String, Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>"
}
],
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheck",
"Visibility": "Public",
"Kind": "Class",
"ImplementedInterfaces": [
"Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck"
],
"Members": [
{
"Kind": "Method",
"Name": "get_Name",
"Parameters": [],
"ReturnType": "System.String",
"Sealed": true,
"Virtual": true,
"ImplementedInterface": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "CheckHealthAsync",
"Parameters": [
{
"Name": "cancellationToken",
"Type": "System.Threading.CancellationToken",
"DefaultValue": "default(System.Threading.CancellationToken)"
}
],
"ReturnType": "System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>",
"Sealed": true,
"Virtual": true,
"ImplementedInterface": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [
{
"Name": "name",
"Type": "System.String"
},
{
"Name": "check",
"Type": "System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult>>"
}
],
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckService",
"Visibility": "Public",
"Kind": "Class",
"ImplementedInterfaces": [
"Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheckService"
],
"Members": [
{
"Kind": "Method",
"Name": "get_Checks",
"Parameters": [],
"ReturnType": "System.Collections.Generic.IReadOnlyDictionary<System.String, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck>",
"Sealed": true,
"Virtual": true,
"ImplementedInterface": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheckService",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "CheckHealthAsync",
"Parameters": [
{
"Name": "cancellationToken",
"Type": "System.Threading.CancellationToken",
"DefaultValue": "default(System.Threading.CancellationToken)"
}
],
"ReturnType": "System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.CompositeHealthCheckResult>",
"Sealed": true,
"Virtual": true,
"ImplementedInterface": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheckService",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "CheckHealthAsync",
"Parameters": [
{
"Name": "checks",
"Type": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck>"
},
{
"Name": "cancellationToken",
"Type": "System.Threading.CancellationToken",
"DefaultValue": "default(System.Threading.CancellationToken)"
}
],
"ReturnType": "System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.CompositeHealthCheckResult>",
"Sealed": true,
"Virtual": true,
"ImplementedInterface": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheckService",
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [
{
"Name": "healthChecks",
"Type": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck>"
}
],
"Visibility": "Public",
"GenericParameter": []
},
{
"Kind": "Constructor",
"Name": ".ctor",
"Parameters": [
{
"Name": "healthChecks",
"Type": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck>"
},
{
"Name": "logger",
"Type": "Microsoft.Extensions.Logging.ILogger<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckService>"
}
],
"Visibility": "Public",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder",
"Visibility": "Public",
"Kind": "Interface",
"Abstract": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "get_Services",
"Parameters": [],
"ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection",
"GenericParameter": []
}
],
"GenericParameters": []
},
{
"Name": "Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheckService",
"Visibility": "Public",
"Kind": "Interface",
"Abstract": true,
"ImplementedInterfaces": [],
"Members": [
{
"Kind": "Method",
"Name": "get_Checks",
"Parameters": [],
"ReturnType": "System.Collections.Generic.IReadOnlyDictionary<System.String, Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck>",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "CheckHealthAsync",
"Parameters": [
{
"Name": "cancellationToken",
"Type": "System.Threading.CancellationToken",
"DefaultValue": "default(System.Threading.CancellationToken)"
}
],
"ReturnType": "System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.CompositeHealthCheckResult>",
"GenericParameter": []
},
{
"Kind": "Method",
"Name": "CheckHealthAsync",
"Parameters": [
{
"Name": "checks",
"Type": "System.Collections.Generic.IEnumerable<Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck>"
},
{
"Name": "cancellationToken",
"Type": "System.Threading.CancellationToken",
"DefaultValue": "default(System.Threading.CancellationToken)"
}
],
"ReturnType": "System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.CompositeHealthCheckResult>",
"GenericParameter": []
}
],
"GenericParameters": []
}
]
}

Просмотреть файл

@ -0,0 +1,62 @@
// 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.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Xunit;
namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
{
public class HealthCheckMiddlewareSampleTest
{
[Fact]
public async Task BasicStartup()
{
var builder = new WebHostBuilder()
.UseStartup<HealthChecksSample.BasicStartup>();
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task CustomWriterStartup()
{
var builder = new WebHostBuilder()
.UseStartup<HealthChecksSample.CustomWriterStartup>();
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/html", response.Content.Headers.ContentType.ToString());
// Ignoring the body since it contains a bunch of statistics
}
[Fact]
public async Task DetailedStatusStartup()
{
var builder = new WebHostBuilder()
.UseStartup<HealthChecksSample.DetailedStatusStartup>();
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/json", response.Content.Headers.ContentType.ToString());
// Ignoring the body since it contains a bunch of statistics
}
}
}

Просмотреть файл

@ -17,10 +17,8 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
{
public class HealthCheckMiddlewareTests
{
[Theory]
[InlineData("/frob")]
[InlineData("/health/")] // Match is exact, for now at least
public async Task IgnoresRequestThatDoesNotMatchPath(string requestPath)
[Fact] // Matches based on '.Map'
public async Task IgnoresRequestThatDoesNotMatchPath()
{
var builder = new WebHostBuilder()
.Configure(app =>
@ -34,26 +32,190 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync(requestPath);
var response = await client.GetAsync("/frob");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData("/health")]
[InlineData("/Health")]
[InlineData("/HEALTH")]
public async Task ReturnsEmptyHealthyRequestIfNoHealthChecksRegistered(string requestPath)
[Fact] // Matches based on '.Map'
public async Task MatchIsCaseInsensitive()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks();
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/HEALTH");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task ReturnsPlainTextStatus()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks();
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task StatusCodeIs200IfNoChecks()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks();
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task StatusCodeIs200IfAllChecksHealthy()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Bar", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task StatusCodeIs200IfCheckIsDegraded()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Bar", () => Task.FromResult(HealthCheckResult.Degraded("Not so great.")))
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.Equal("Degraded", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task StatusCodeIs503IfCheckIsUnhealthy()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Bar", () => Task.FromResult(HealthCheckResult.Unhealthy("Pretty bad.")))
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.Equal("Unhealthy", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task StatusCodeIs500IfCheckIsFailed()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Bar", () => Task.FromResult(new HealthCheckResult(HealthCheckStatus.Failed, null, null, null)))
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
Assert.Equal("Failed", await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task DetailedJsonReturnsEmptyHealthyResponseIfNoHealthChecksRegistered()
{
var expectedJson = JsonConvert.SerializeObject(new
{
status = "Healthy",
results = new { }
}, Formatting.None);
}, Formatting.Indented);
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
app.UseHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = HealthCheckResponseWriters.WriteDetailedJson,
});
})
.ConfigureServices(services =>
{
@ -62,14 +224,14 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync(requestPath);
var response = await client.GetAsync("/health");
var result = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedJson, result);
}
[Fact]
public async Task ReturnsResultsFromHealthChecks()
public async Task DetailedJsonReturnsResultsFromHealthChecks()
{
var expectedJson = JsonConvert.SerializeObject(new
{
@ -101,12 +263,15 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
data = new { }
},
},
}, Formatting.None);
}, Formatting.Indented);
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
app.UseHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = HealthCheckResponseWriters.WriteDetailedJson,
});
})
.ConfigureServices(services =>
{
@ -129,78 +294,15 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
}
[Fact]
public async Task StatusCodeIs200IfNoChecks()
public async Task NoResponseWriterReturnsEmptyBody()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks();
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task StatusCodeIs200IfAllChecksHealthy()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Bar", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task StatusCodeIs200IfCheckIsDegraded()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
})
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Bar", () => Task.FromResult(HealthCheckResult.Degraded("Not so great.")))
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
[Fact]
public async Task StatusCodeIs503IfCheckIsUnhealthy()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
app.UseHealthChecks("/health", new HealthCheckOptions()
{
ResponseWriter = null,
});
})
.ConfigureServices(services =>
{
@ -215,29 +317,34 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
Assert.Equal(string.Empty, await response.Content.ReadAsStringAsync());
}
[Fact]
public async Task StatusCodeIs500IfCheckIsFailed()
public async Task CanSetCustomStatusCodes()
{
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseHealthChecks("/health");
app.UseHealthChecks("/health", new HealthCheckOptions()
{
ResultStatusCodes =
{
[HealthCheckStatus.Healthy] = 201,
}
});
})
.ConfigureServices(services =>
{
services.AddHealthChecks()
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")))
.AddCheck("Bar", () => Task.FromResult(new HealthCheckResult(HealthCheckStatus.Failed, null, null, null)))
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Healthy("A-ok!")));
services.AddHealthChecks();
});
var server = new TestServer(builder);
var client = server.CreateClient();
var response = await client.GetAsync("/health");
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
}
}
}

Просмотреть файл

@ -14,6 +14,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\samples\HealthChecksSample\HealthChecksSample.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Diagnostics.HealthChecks\Microsoft.AspNetCore.Diagnostics.HealthChecks.csproj" />
</ItemGroup>