Remove JSON output (#457)
* Remove JSON output and convert to sample GlennC and I made the decisison to turn the JSON output into a sample rather than something we support out of the box. We wouldn't tell customers to definitely use it and we don't want to introduce more coupling to JSON.NET.
This commit is contained in:
parent
64124e9c85
commit
4549b84cb5
|
|
@ -6,6 +6,8 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace HealthChecksSample
|
||||
{
|
||||
|
|
@ -45,23 +47,16 @@ namespace HealthChecksSample
|
|||
|
||||
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>");
|
||||
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,68 +0,0 @@
|
|||
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");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ 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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(MicrosoftAspNetCoreStaticFilesPackageVersion)" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -48,12 +48,7 @@ namespace HealthChecksSample
|
|||
|
||||
|
||||
// The readiness check uses all of the registered health checks (default)
|
||||
app.UseHealthChecks("/health/ready", new HealthCheckOptions()
|
||||
{
|
||||
// This sample is using detailed status to make more apparent which checks are being run - any
|
||||
// output format will work with liveness and readiness checks.
|
||||
ResponseWriter = HealthCheckResponseWriters.WriteDetailedJson,
|
||||
});
|
||||
app.UseHealthChecks("/health/ready");
|
||||
|
||||
// The liveness check uses an 'identity' health check that always returns healty
|
||||
app.UseHealthChecks("/health/live", new HealthCheckOptions()
|
||||
|
|
@ -63,10 +58,6 @@ namespace HealthChecksSample
|
|||
{
|
||||
"identity",
|
||||
},
|
||||
|
||||
// This sample is using detailed status to make more apparent which checks are being run - any
|
||||
// output format will work with liveness and readiness checks.
|
||||
ResponseWriter = HealthCheckResponseWriters.WriteDetailedJson,
|
||||
});
|
||||
|
||||
app.Run(async (context) =>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ namespace HealthChecksSample
|
|||
{
|
||||
{ "", typeof(BasicStartup) },
|
||||
{ "basic", typeof(BasicStartup) },
|
||||
{ "detailed", typeof(DetailedStatusStartup) },
|
||||
{ "writer", typeof(CustomWriterStartup) },
|
||||
{ "liveness", typeof(LivenessProbeStartup) },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,13 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// <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"/>.
|
||||
/// <para>
|
||||
/// This method will use <see cref="MapExtensions.Map(IApplicationBuilder, PathString, Action{IApplicationBuilder})"/> to
|
||||
/// listen to health checks requests on the specified URL path.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The health check middleware will use default settings from <see cref="IOptions{HealthCheckOptions}"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path)
|
||||
{
|
||||
|
|
@ -44,6 +50,12 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// <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>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method will use <see cref="MapExtensions.Map(IApplicationBuilder, PathString, Action{IApplicationBuilder})"/> to
|
||||
/// listen to health checks requests on the specified URL path.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static IApplicationBuilder UseHealthChecks(this IApplicationBuilder app, PathString path, HealthCheckOptions options)
|
||||
{
|
||||
if (app == null)
|
||||
|
|
|
|||
|
|
@ -1,56 +1,18 @@
|
|||
// 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
|
||||
internal 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,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>ASP.NET Core middleware for returning the results of Health Checks in the application</Description>
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
// 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 Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
|
||||
|
|
@ -37,22 +35,6 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
|
|||
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());
|
||||
|
|
@ -63,20 +45,6 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
|
|||
[Fact]
|
||||
public async Task LivenessProbeStartup_Liveness()
|
||||
{
|
||||
var expectedJson = JsonConvert.SerializeObject(new
|
||||
{
|
||||
status = "Healthy",
|
||||
results = new
|
||||
{
|
||||
identity = new
|
||||
{
|
||||
status = "Healthy",
|
||||
description = "",
|
||||
data = new { }
|
||||
},
|
||||
},
|
||||
}, Formatting.Indented);
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.UseStartup<HealthChecksSample.LivenessProbeStartup>();
|
||||
|
||||
|
|
@ -85,33 +53,13 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
|
|||
|
||||
var response = await client.GetAsync("/health/live");
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("application/json", response.Content.Headers.ContentType.ToString());
|
||||
Assert.Equal(expectedJson, await response.Content.ReadAsStringAsync());
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||
Assert.Equal("Healthy", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LivenessProbeStartup_Readiness()
|
||||
{
|
||||
var expectedJson = JsonConvert.SerializeObject(new
|
||||
{
|
||||
status = "Unhealthy",
|
||||
results = new
|
||||
{
|
||||
identity = new
|
||||
{
|
||||
status = "Healthy",
|
||||
description = "",
|
||||
data = new { }
|
||||
},
|
||||
slow_dependency = new
|
||||
{
|
||||
status = "Unhealthy",
|
||||
description = "Dependency is still initializing",
|
||||
data = new { }
|
||||
},
|
||||
},
|
||||
}, Formatting.Indented);
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.UseStartup<HealthChecksSample.LivenessProbeStartup>();
|
||||
|
||||
|
|
@ -120,8 +68,8 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
|
|||
|
||||
var response = await client.GetAsync("/health/ready");
|
||||
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
|
||||
Assert.Equal("application/json", response.Content.Headers.ContentType.ToString());
|
||||
Assert.Equal(expectedJson, await response.Content.ReadAsStringAsync());
|
||||
Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
|
||||
Assert.Equal("Unhealthy", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Net;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
|
|
@ -201,94 +202,40 @@ namespace Microsoft.AspNetCore.Diagnostics.HealthChecks
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DetailedJsonReturnsEmptyHealthyResponseIfNoHealthChecksRegistered()
|
||||
{
|
||||
var expectedJson = JsonConvert.SerializeObject(new
|
||||
{
|
||||
status = "Healthy",
|
||||
results = new { }
|
||||
}, Formatting.Indented);
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
ResponseWriter = HealthCheckResponseWriters.WriteDetailedJson,
|
||||
});
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHealthChecks();
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("/health");
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(expectedJson, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DetailedJsonReturnsResultsFromHealthChecks()
|
||||
public async Task CanUseCustomWriter()
|
||||
{
|
||||
var expectedJson = JsonConvert.SerializeObject(new
|
||||
{
|
||||
status = "Unhealthy",
|
||||
results = new
|
||||
{
|
||||
Foo = new
|
||||
{
|
||||
status = "Healthy",
|
||||
description = "Good to go!",
|
||||
data = new { }
|
||||
},
|
||||
Bar = new
|
||||
{
|
||||
status = "Degraded",
|
||||
description = "Feeling a bit off.",
|
||||
data = new { someUsefulAttribute = 42 }
|
||||
},
|
||||
Baz = new
|
||||
{
|
||||
status = "Unhealthy",
|
||||
description = "Not feeling good at all",
|
||||
data = new { }
|
||||
},
|
||||
Boz = new
|
||||
{
|
||||
status = "Unhealthy",
|
||||
description = string.Empty,
|
||||
data = new { }
|
||||
},
|
||||
},
|
||||
}, Formatting.Indented);
|
||||
});
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHealthChecks("/health", new HealthCheckOptions()
|
||||
{
|
||||
ResponseWriter = HealthCheckResponseWriters.WriteDetailedJson,
|
||||
ResponseWriter = (c, r) =>
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(new { status = r.Status.ToString(), });
|
||||
c.Response.ContentType = "application/json";
|
||||
return c.Response.WriteAsync(json);
|
||||
},
|
||||
});
|
||||
})
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHealthChecks()
|
||||
.AddCheck("Foo", () => Task.FromResult(HealthCheckResult.Healthy("Good to go!")))
|
||||
.AddCheck("Bar", () => Task.FromResult(HealthCheckResult.Degraded("Feeling a bit off.", new Dictionary<string, object>()
|
||||
{
|
||||
{ "someUsefulAttribute", 42 }
|
||||
})))
|
||||
.AddCheck("Baz", () => Task.FromResult(HealthCheckResult.Unhealthy("Not feeling good at all", new Exception("Bad times"))))
|
||||
.AddCheck("Boz", () => Task.FromResult(HealthCheckResult.Unhealthy(new Exception("Very bad times"))));
|
||||
.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("application/json", response.Content.Headers.ContentType.ToString());
|
||||
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
Assert.Equal(expectedJson, result);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue