Update: Based on App Metrics 1.0, latest documentation can be found here
Health checks are useful to not only test the internal health of your application but also it’s external dependencies such as confirming that a third party api is available or confirming the connectivity to a database for example. App Metrics provides such health checks and exposes a health endpoint whereby results can be viewed and monitored. Health checks in App Metrics are small tests which return either a healthy, degraded or unhealthy result. Health checks are executed when the health endpoint is requested via ASP.NET Core Middleware.
Here we’ll look at configuring App Metrics to provide health checks in an ASP.NET Core MVC application.
Create a new ASP.NET Web Application
Add App.Metrics packages
Add the packages marked below to start using App Metrics for it’s health checking capablity in a web context.
This demo is assuming a dependency on ASP.NET Core MVC although not required i.e. App.Metrics.Extensions.Middleware
exposes the health endpoint through ASP.NET Core Middleware.
The App.Metrics.Extensions.Mvc
package provides requested MVC route templates to App Metrics to measure things like reponse times and error rates per endpoint for example, this package has a dependency on the middleware package.
Configure Services
To configure the required application services we need to wire-up App Metrics on the IServiceCollection
. At the moment JSON serialization is the only supported format.
public void ConfigureServices(IServiceCollection services) { services. AddMetrics(Configuration.GetSection("AppMetrics"), options => options.GlobalTags.Add("app", "sample app")). AddJsonSerialization(). AddHealthChecks(). AddMetricsMiddleware(Configuration.GetSection("AspNetMetrics")); services.AddMvc(options => options.AddMetricsResourceFilter()); }
Configure Middleware
Next we need to register the required ASP.NET Core middleware via the IApplicationBuilder
.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseMetrics(); app.UseMvc(); }
Configuration Options
App Metrics provides configuration options via code, config or a mix of both. For more details on what is available see the docs on AppMetrics Configuration and AppMetrics Middleware Configuration. For now add the following to appsettings.json
.
{ "AppMetrics": { "DefaultContextLabel": "SampleApi", "MetricsEnabled": true, "ReportingEnabled": false, "GlobalTags": { "env": "stage" }, "ApdexTSeconds": 0.1 } }
Viewing the results
Now that we have everything wired-up, run the web application and request /health
, the response will look as follows
{ "degraded": {}, "healthy": { "apdex Score": "Satisfied. Score 1" }, "status": "Healthy", "timestamp": "2017-04-17T00:00:00.0000Z", "unhealthy": {} }
You will notice an Apdex Score check already registered. This is a health check registered by default which can be disabled through configuration.
Implementing Health Checks
Health checks can be written either by inheriting HealthCheck
or using the IHealthCheckFactory
provided via the IMetricHostBuilder
allowing checks to be fluently registered.
App Metrics will automatically register all classes inheritenting HealthCheck
on startup by scanning the host project as well as any of it’s references which have a dependency on App Metrics, so we basically just need to provide an implementation.
Health Check class implementation
To demostrate how to have a health check return a degraded result, add a new HealthCheck
class.
Then modify the class with the following snippet which simply returns a degraded result.
public class SampleDegradedHealthCheck : HealthCheck { public SampleDegradedHealthCheck() : base("Sample Degraded") { } protected override Task<HealthCheckResult> CheckAsync(CancellationToken token = default(CancellationToken)) { return Task.FromResult(HealthCheckResult.Degraded("Degraded")); } }
To demonstrate returning an unhealthy result, add another HealthCheck
implementation
Add sample unhealthy
public class SampleHealthCheckUnhealthy : HealthCheck { public SampleHealthCheckUnhealthy() : base("Sample Unhealthy") { } protected override Task<HealthCheckResult> CheckAsync(CancellationToken token = default(CancellationToken)) { return Task.FromResult(HealthCheckResult.Unhealthy("OOPS")); } }
Viewing the results
Now by running the web application and requesting /health
, the response will look as follows
{ "degraded": { "sample Degraded": "Degraded" }, "healthy": { "apdex Score": "Satisfied. Score 1" }, "status": "Unhealthy", "timestamp": "2017-04-17T00:00:00.0000Z", "unhealthy": { "sample UnHealthy": "OOPS" } }
We can see our sample degraded and unhealthy checks along with their failure reasons.
Health Check fluent implementation
Alternatively, App Metrics allows Health Checks is to be registered fluently and also comes with some pre-defined checks such as performing a ping on a domain, confirming a GET request on a specified URI returns a 200 status and checking the physical, private and virtual memory sizes against a configured threshold for example.
Update the ConfigureServices
method with the following to demonstrate.
public void ConfigureServices(IServiceCollection services) { services.AddMetrics(Configuration.GetSection("AppMetrics"), options => options.GlobalTags.Add("app", "sample app")). AddJsonSerialization(). AddHealthChecks( factory => { var physicalMemoryThresholdBytes = 100 * 1024 * 1024; factory.RegisterProcessPhysicalMemoryHealthCheck("physical memory", physicalMemoryThresholdBytes); factory.RegisterPingHealthCheck("google ping", "google.com", TimeSpan.FromSeconds(10)); factory.RegisterHttpGetHealthCheck("github", new Uri("https://github.com/"), TimeSpan.FromSeconds(10)); }). AddMetricsMiddleware(Configuration.GetSection("AspNetMetrics")); services.AddMvc(options => options.AddMetricsResourceFilter()); }
Additional health checks can be implemented this way through an extension method on the IHealthCheckFactory
, see here for example. When using App Metrics for performance monitoring, health checks can be implemented comparing values measured by your application against a specified threshold, see the ApdexHealthCheck as an example.
Viewing the results
Once again by running the web application and requesting /health
, the response will look as follows displaying the newly registered checks via the health check factory.
{ "degraded": { "sample Degraded": "Degraded" }, "healthy": { "apdex Score": "Satisfied. Score 1", "github": "OK. https://github.com/", "google ping": "OK. google.com", "physical memory": "OK. 104857600 bytes" }, "status": "Unhealthy", "timestamp": "2017-04-17T17:00:00.0000Z", "unhealthy": { "sample UnHealthy": "OOPS" } }
Response Headers
Regardless of the health check result, the health endpoint will respond with the following response headers
Cache-Control:no-cache, no-store, must-revalidate
Content-Type:application/json
Expires:0
Pragma:no-cache
In addition:
When all checks pass, a 200 status code will be returned.
When one or more checks fail or throw an uncaught exception, a 500 status code will be returned.
When no checks fail but one or more checks were considered to be degrading the application, a 200 status code as well as a warning response header of Warning:100 'Degraded'
will be returned to indicate the application is in a degrading state.
What’s next
Having our application’s health exposed over an HTTP endpoint isn’t really that useful yet. This becomes more useful when we have automated monitoring and alerting on this endpoint e.g. Pingdom, or by persisting the results to build for example a health status page. App Metrics will automatically record health check results as gauges, which can be configured to report metrics to InfluxDB or Elasticsearch for example.
For other features provided by App Metrics see the documentation.
Nice write up. Is there any integration for health reporting to Consul by HashiCorp?
LikeLike
Thanks. Interesting you mentioning Consul, in my day job we’ve recently setup a Consul/Nomad/Docker POC. App Metrics doesnt have any Consul integration but I may look at this at some stage.
LikeLike
Great post Allan!
I’m having trouble replicating your custom methods (i.e. SampleHealthCheckUnhealthy). How do you register them with the factory?
Was wondering if you have the source code for this article available, so I could have a look?
Thanks,
Anders
LikeLike
Thanks Anders.
App Metrics will automatically scan and register any class referencing `HealthCheck`, so there is no need to register health checks with the factory when taking this approach.
The fluent health check registration is an alternative approach to registering health checks which provides easier discoverability on the available pre-defined checks. This allows simple registration of more common types of health checks such as checking the connectivity to an API your application is dependent on. The downside of the fluent registration approach is that DI is not supported, depending on the health check logic that may or may not be an issue.
I don’t have the sample code used in the post, but there are a bunch of more comprehensive samples you can take a look at: e.g. https://github.com/alhardy/AppMetrics/tree/master/sandbox/App.Metrics.Sandbox and https://github.com/alhardy/AppMetrics.Samples.
You can also refer to the documentation http://app-metrics.io/getting-started/health-checks/index.html
LikeLike
One other thing worth mentioning that this post doesn’t cover, there is now support to register SLA type health checks by using the metrics recorded by your application, more details on that in the documentation http://app-metrics.io/getting-started/health-checks/index.html#metric-health-checks.
LikeLike
Thanks for the reply!
I found out what I did wrong, so I thought I would come back with a solution. Apparantly the custom health check (i.e. SampleHealthCheckUnhealthy) has to be in its own class file. I had just made the class in the same file as some of my other code.
Also, if you want to update this guide, you are missing a on the CheckAsync overrrides on all of the custom health checks.
I really enjoy the framework – keep up the good work 🙂
LikeLike
Thanks for pointing out the missing return type on the `CheckAsync` methods Anders, I’ve updated the post.
Re your initial issue having multiple health checks in the same file, that’s very interesting I don’t know how that would make a difference in terms of compilation and wasn’t able to replicate.
Glad you’re enjoying it 😉
LikeLike
Hello Allan, is there any way to get an automatic health check every 30 seconds for example?
LikeLike
At the moment no, other than using something like pingdom to monitor. There is a GitHub issue for this though.
LikeLike
[…] This is where, we looked into the third option: Host our Background Worker on IIS. Hosting the Background Worker on IIS meant that we were able to deploy our application same as we would deploy our Background Worker as a web app. Instead of using a “Generic Host” we were able to use default “Web Host”. There was less custom code. It was easier to debug and monitor. In addition to this, we were also able to leverage .NET Core Health Checks. […]
LikeLike