diff --git a/build/dependencies.props b/build/dependencies.props
index 7ef83bd4d7..c201e62915 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -16,6 +16,7 @@
2.2.0-preview1-34823
2.2.0-preview1-34823
2.2.0-preview1-34823
+ 2.2.0-preview1-34823
2.2.0-preview1-34823
2.2.0-preview1-34823
2.2.0-preview1-34823
diff --git a/samples/HealthChecksSample/BasicStartup.cs b/samples/HealthChecksSample/BasicStartup.cs
new file mode 100644
index 0000000000..c89d78c8e5
--- /dev/null
+++ b/samples/HealthChecksSample/BasicStartup.cs
@@ -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");
+ });
+ }
+ }
+}
diff --git a/samples/HealthChecksSample/CustomWriterStartup.cs b/samples/HealthChecksSample/CustomWriterStartup.cs
new file mode 100644
index 0000000000..87e58b7b7d
--- /dev/null
+++ b/samples/HealthChecksSample/CustomWriterStartup.cs
@@ -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();
+ }
+
+ 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($@"
+
+
+
+ Everything is {result.Status}
+
+
+
+ | Name | Status |
+
+
+ {string.Join("", result.Results.Select(kvp => $"| {kvp.Key} | {kvp.Value.Status} |
"))}
+
+
+
+");
+ }
+ }
+}
diff --git a/samples/HealthChecksSample/DetailedStatusStartup.cs b/samples/HealthChecksSample/DetailedStatusStartup.cs
new file mode 100644
index 0000000000..76475c5db5
--- /dev/null
+++ b/samples/HealthChecksSample/DetailedStatusStartup.cs
@@ -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()
+ {
+ { "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");
+ });
+ }
+ }
+}
diff --git a/samples/HealthChecksSample/GCInfoHealthCheck.cs b/samples/HealthChecksSample/GCInfoHealthCheck.cs
new file mode 100644
index 0000000000..644fdb0a32
--- /dev/null
+++ b/samples/HealthChecksSample/GCInfoHealthCheck.cs
@@ -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 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()
+ {
+ { "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));
+ }
+ }
+}
diff --git a/samples/HealthChecksSample/HealthChecksSample.csproj b/samples/HealthChecksSample/HealthChecksSample.csproj
index 19ef79337e..2f3c0e6aaa 100644
--- a/samples/HealthChecksSample/HealthChecksSample.csproj
+++ b/samples/HealthChecksSample/HealthChecksSample.csproj
@@ -1,10 +1,13 @@
-
+
- netcoreapp2.0
+
+ netcoreapp2.0;net461
+ netcoreapp2.0
+
diff --git a/samples/HealthChecksSample/Program.cs b/samples/HealthChecksSample/Program.cs
index be3f8bcdf1..bd72511db6 100644
--- a/samples/HealthChecksSample/Program.cs
+++ b/samples/HealthChecksSample/Program.cs
@@ -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 _scenarios;
+
+ static Program()
+ {
+ _scenarios = new Dictionary(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()
+ .UseStartup(startupType)
.Build();
+ }
+
}
}
diff --git a/samples/HealthChecksSample/Startup.cs b/samples/HealthChecksSample/Startup.cs
deleted file mode 100644
index e3bb04150c..0000000000
--- a/samples/HealthChecksSample/Startup.cs
+++ /dev/null
@@ -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!");
- });
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/Builder/HealthCheckApplicationBuilderExtensions.cs b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/Builder/HealthCheckApplicationBuilderExtensions.cs
new file mode 100644
index 0000000000..f1f47b3ba5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/Builder/HealthCheckApplicationBuilderExtensions.cs
@@ -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
+{
+ ///
+ /// extension methods for the .
+ ///
+ public static class HealthCheckApplicationBuilderExtensions
+ {
+ ///
+ /// Adds a middleware that provides health check status.
+ ///
+ /// The .
+ /// The path on which to provide health check status.
+ /// A reference to the after the operation has completed.
+ ///
+ /// The health check middleware will use default settings other than the provided .
+ ///
+ 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());
+ }
+
+ ///
+ /// Adds a middleware that provides health check status.
+ ///
+ /// The .
+ /// The path on which to provide health check status.
+ /// A used to configure the middleware.
+ /// A reference to the after the operation has completed.
+ 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(Options.Create(options)));
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckAppBuilderExtensions.cs b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckAppBuilderExtensions.cs
deleted file mode 100644
index 916ca46df4..0000000000
--- a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckAppBuilderExtensions.cs
+++ /dev/null
@@ -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
-{
- ///
- /// extension methods for the .
- ///
- public static class HealthCheckAppBuilderExtensions
- {
- ///
- /// Adds a middleware that provides a REST API for requesting health check status.
- ///
- /// The .
- /// The path on which to provide the API.
- /// A reference to the after the operation has completed.
- public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path)
- {
- app = app ?? throw new ArgumentNullException(nameof(app));
-
- return app.UseMiddleware(Options.Create(new HealthCheckOptions()
- {
- Path = path
- }));
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckMiddleware.cs b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckMiddleware.cs
index 18c10da287..5f3ca6df47 100644
--- a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckMiddleware.cs
+++ b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckMiddleware.cs
@@ -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, IHealthCheckService healthCheckService)
+ public HealthCheckMiddleware(
+ RequestDelegate next,
+ IOptions 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;
}
///
- /// Process an individual request.
+ /// Processes a request.
///
- ///
+ ///
///
- 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);
}
}
}
diff --git a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs
index 8416e6843d..6426fcbc3b 100644
--- a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs
+++ b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckOptions.cs
@@ -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
///
public class HealthCheckOptions
{
+ public IDictionary ResultStatusCodes { get; } = new Dictionary()
+ {
+ { 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 },
+ };
+
///
- /// Gets or sets the path at which the Health Check results will be available.
+ /// Gets or sets a delegate used to write the response.
///
- public PathString Path { get; set; }
+ ///
+ /// The default value is a delegate that will write a minimal text/plain response with the value
+ /// of as a string.
+ ///
+ public Func ResponseWriter { get; set; } = HealthCheckResponseWriters.WriteMinimalPlaintext;
}
}
diff --git a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckResponseWriters.cs b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckResponseWriters.cs
new file mode 100644
index 0000000000..95ddf00ac8
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/HealthCheckResponseWriters.cs
@@ -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));
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/baseline.netcore.json b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/baseline.netcore.json
index c0e8deddd1..d089d2470d 100644
--- a/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/baseline.netcore.json
+++ b/src/Microsoft.AspNetCore.Diagnostics.HealthChecks/baseline.netcore.json
@@ -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"
- },
- {
- "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": []
- }
]
}
\ No newline at end of file
diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/baseline.netcore.json b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/baseline.netcore.json
index 7792748d55..871db4c089 100644
--- a/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/baseline.netcore.json
+++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/baseline.netcore.json
@@ -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",
- "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"
- }
- ],
- "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"
- }
- ],
- "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"
- }
- ],
- "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"
- }
- ],
- "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"
- }
- ],
- "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"
- }
- ],
- "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",
- "GenericParameter": []
- }
- ],
- "GenericParameters": []
- }
]
}
\ No newline at end of file
diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthCheck.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthCheck.cs
index e72910df33..da6a92d062 100644
--- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthCheck.cs
+++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthCheck.cs
@@ -11,15 +11,10 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
/// A simple implementation of which uses a provided delegate to
/// implement the check.
///
- public class HealthCheck : IHealthCheck
+ public sealed class HealthCheck : IHealthCheck
{
private readonly Func> _check;
- ///
- /// Gets the name of the health check, which should indicate the component being checked.
- ///
- public string Name { get; }
-
///
/// Create an instance of from the specified and .
///
@@ -27,10 +22,15 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks
/// A delegate which provides the code to execute when the health check is run.
public HealthCheck(string name, Func> check)
{
- Name = name;
- _check = check;
+ Name = name ?? throw new ArgumentNullException(nameof(name));
+ _check = check ?? throw new ArgumentNullException(nameof(check));
}
+ ///
+ /// Gets the name of the health check, which should indicate the component being checked.
+ ///
+ public string Name { get; }
+
///
/// Runs the health check, returning the status of the component being checked.
///
diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthChecksBuilderAddCheckExtensions.cs b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthChecksBuilderAddCheckExtensions.cs
index 3b3ee1eb54..b2f931fcd3 100644
--- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthChecksBuilderAddCheckExtensions.cs
+++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/HealthChecksBuilderAddCheckExtensions.cs
@@ -22,8 +22,22 @@ namespace Microsoft.Extensions.DependencyInjection
/// The .
public static IHealthChecksBuilder AddCheck(this IHealthChecksBuilder builder, string name, Func> check)
{
- builder.Services.AddSingleton(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));
}
///
@@ -33,7 +47,46 @@ namespace Microsoft.Extensions.DependencyInjection
/// The name of the health check, which should indicate the component being checked.
/// A delegate which provides the code to execute when the health check is run.
/// The .
- public static IHealthChecksBuilder AddCheck(this IHealthChecksBuilder builder, string name, Func> check) =>
- builder.AddCheck(name, _ => check());
+ public static IHealthChecksBuilder AddCheck(this IHealthChecksBuilder builder, string name, Func> 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());
+ }
+
+ ///
+ /// Adds a new health check with the implementation.
+ ///
+ /// The to add the check to.
+ /// An implementation.
+ /// The .
+ 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(check);
+ return builder;
+ }
}
}
diff --git a/src/Microsoft.Extensions.Diagnostics.HealthChecks/baseline.netcore.json b/src/Microsoft.Extensions.Diagnostics.HealthChecks/baseline.netcore.json
index 4938a12605..cb2fe053f1 100644
--- a/src/Microsoft.Extensions.Diagnostics.HealthChecks/baseline.netcore.json
+++ b/src/Microsoft.Extensions.Diagnostics.HealthChecks/baseline.netcore.json
@@ -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>"
- }
- ],
- "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>"
- }
- ],
- "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",
- "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"
- }
- ],
- "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",
- "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>"
- }
- ],
- "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",
- "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",
- "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"
- },
- {
- "Name": "cancellationToken",
- "Type": "System.Threading.CancellationToken",
- "DefaultValue": "default(System.Threading.CancellationToken)"
- }
- ],
- "ReturnType": "System.Threading.Tasks.Task",
- "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"
- }
- ],
- "Visibility": "Public",
- "GenericParameter": []
- },
- {
- "Kind": "Constructor",
- "Name": ".ctor",
- "Parameters": [
- {
- "Name": "healthChecks",
- "Type": "System.Collections.Generic.IEnumerable"
- },
- {
- "Name": "logger",
- "Type": "Microsoft.Extensions.Logging.ILogger"
- }
- ],
- "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",
- "GenericParameter": []
- },
- {
- "Kind": "Method",
- "Name": "CheckHealthAsync",
- "Parameters": [
- {
- "Name": "cancellationToken",
- "Type": "System.Threading.CancellationToken",
- "DefaultValue": "default(System.Threading.CancellationToken)"
- }
- ],
- "ReturnType": "System.Threading.Tasks.Task",
- "GenericParameter": []
- },
- {
- "Kind": "Method",
- "Name": "CheckHealthAsync",
- "Parameters": [
- {
- "Name": "checks",
- "Type": "System.Collections.Generic.IEnumerable"
- },
- {
- "Name": "cancellationToken",
- "Type": "System.Threading.CancellationToken",
- "DefaultValue": "default(System.Threading.CancellationToken)"
- }
- ],
- "ReturnType": "System.Threading.Tasks.Task",
- "GenericParameter": []
- }
- ],
- "GenericParameters": []
- }
]
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareSampleTest.cs b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareSampleTest.cs
new file mode 100644
index 0000000000..1007017e11
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareSampleTest.cs
@@ -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();
+
+ 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();
+
+ 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();
+
+ 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
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs
index c6aaee52ff..b81747581c 100644
--- a/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs
+++ b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/HealthCheckMiddlewareTests.cs
@@ -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());
}
}
}
diff --git a/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj
index 001b0c0990..8ac9320ae8 100644
--- a/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj
+++ b/test/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests/Microsoft.AspNetCore.Diagnostics.HealthChecks.Tests.csproj
@@ -14,6 +14,7 @@
+