Ratpacked: Add Health Checks
In the Ratpack core we can find the ratpack.health.HealthCheck
interface. We can implement this interface to check for example if a mail server, that we need in our application, is available. Any objects that implement this interface and are registered in the Guice registry are handled by Ratpack. Ratpack also offers a HealthCheckHandler
to output the results of all health checks or a single health check identified by a name. Instead of creating a new class that implements the HealthCheck
interface we can also use the HealtCheck.of
method. This method accepts an argument with the name of our check and a Closure
or lambda expression with the code that does the checking.
Let's write a sample Ratpack application using the Groovy DSL. We first use the HealthCheck.of
to implement a simple health check. We also using the HealthCheckHandler
so we can request information about the health check.
// File: src/ratpack/Ratpack.groovy
import ratpack.exec.Promise
import ratpack.health.HealthCheck
import ratpack.health.HealthCheckHandler
import ratpack.registry.Registry
import static ratpack.groovy.Groovy.ratpack
ratpack {
bindings {
// Add a simple HealtCheck implementation with
// the of method. The name of our health check is
// "application". We simply return a Promise with
// a HealthCheck.Result value. If we get here we assume
// the application is UP and running.
add HealthCheck.of('application') { Registry registry ->
Promise.value(HealthCheck.Result.healthy("UP"))
}
}
handlers {
// Assign HealthCheckHandler to health/ endpoint.
// Optionally we can provide the name of the health check
// to get the results specific for that health check.
get('health/:name?', new HealthCheckHandler())
}
}
When we run the application and request the URL http://localhost:5050/health
and http://localhost:5050/health/application
we get the following output:
$ http localhost:5050/health
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked
application : HEALTHY
$ http localhost:5050/health/application
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked
application : HEALTHY
Now we create a new class that implements the HealthCheck
interface. We write a health check implementation that checks if the free space on a drive is less than a given threshold. If there is less space the health check should return an unhealthy status:
// File: src/main/groovy/com/mrhaki/ratpack/healthcheck/DiskSpaceHealthCheck.groovy
package com.mrhaki.ratpack.healthcheck
import groovy.transform.CompileStatic
import ratpack.exec.Blocking
import ratpack.exec.Promise
import ratpack.health.HealthCheck
import ratpack.registry.Registry
import static org.apache.commons.io.FileUtils.byteCountToDisplaySize
/**
* Ratpack {@link HealthCheck} implementation to check for free space
* on a disk. If the available free space is less than a given
* threshold value than the check return unhealthy.
*/
@CompileStatic
class DiskSpaceHealthCheck implements HealthCheck {
/**
* Default disk to check for free space is the local
* disk our Ratpack app is running.
*/
File path = new File(".")
/**
* Default threshold is 50 MB.
*/
long threshold = 50L * 1024 * 1024
/**
* Name for health check. Name must be unique
* in Ratpack application. To add more instances
* of this class use different names.
*/
String name = 'diskSpace'
/**
* Check available free space for the given {@link #path} value. Compare
* this with the configured {@link #threshold} value. If available free
* space is less than threshold return unhealthy result.
* Otherwise return a healthy status.
*
* @param registry Ratpack registry.
* @return Unhealthy result when available free space less than threshold, otherwise healthy.
* @throws Exception Something goes wrong.
*/
@Override
Promise check(final Registry registry) throws Exception {
Blocking.get {
// Get available free space. Operation is potentially blocking
// so inside a Blocking.get{} block.
path.freeSpace
}.map { Long diskFreeSpace ->
// Format bytes to readable format with KB, MB, GB, etc.
final String diskFreeSpaceFormatted = byteCountToDisplaySize(diskFreeSpace)
final String thresholdFormatted = byteCountToDisplaySize(threshold)
// Check if available free space is above given threshold.
if (diskFreeSpace >= threshold) {
// Everything is ok.
HealthCheck.Result.healthy(
'Available: %s (threshold: %s)',
diskFreeSpaceFormatted,
thresholdFormatted)
} else {
// Available free space is below given threshold.
// Create message with information
// and signal as unhealthy.
HealthCheck.Result.unhealthy(
'Free disk space below threshold. Available: %s (threshold: %s)',
diskFreeSpaceFormatted,
thresholdFormatted)
}
}
}
}
We only have to add this class to our registry and Ratpack will pick it up:
// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.healthcheck.DiskSpaceHealthCheck
import ratpack.exec.Promise
import ratpack.health.HealthCheck
import ratpack.health.HealthCheckHandler
import ratpack.registry.Registry
import static ratpack.groovy.Groovy.ratpack
ratpack {
bindings {
add new DiskSpaceHealthCheck(threshold: 50L * 1024 * 1024 * 1024 /* 50GB */)
add HealthCheck.of('application') { Registry registry ->
Promise.value(HealthCheck.Result.healthy("UP"))
}
}
handlers {
get('health/:name?', new HealthCheckHandler())
}
}
We run the application and check the results of the health
endpoint:
$ http localhost:5050/health
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked
application : HEALTHY
diskSpace : UNHEALTHY [Free disk space below threshold. Available: 41 GB (threshold: 50 GB)]
$ http localhost:5050/health/diskSpace
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked
diskSpace : UNHEALTHY [Free disk space below threshold. Available: 41 GB (threshold: 50 GB)]
Written with Ratpack 1.1.1.