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

1.png

Add App.Metrics packages

Add the packages marked below to start using App Metrics for it’s health checking capablity in a web context.

3.png

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.

4.png

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.

Advertisements

Posted by Allan Hardy

7 Comments

  1. Nice write up. Is there any integration for health reporting to Consul by HashiCorp?

    Like

    Reply

    1. 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.

      Like

      Reply

  2. 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

    Like

    Reply

    1. 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

      Like

      Reply

      1. 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.

        Like

      2. 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 🙂

        Like

  3. 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 😉

    Like

    Reply

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s