In today’s global world, there’s a growing demand to ensure your applications work correctly around the clock.
Many businesses, including UKFast, rely on open source frameworks like Laravel and Lumen to provide effective solutions for their customers. By getting involved with open source development, business owners and developers can take a framework and shape it into something with the potential to help in innovative new ways.
That’s how UKFast’s Laravel Health Check package was created.
As a development team we love using the package internally. It allows us to monitor UKFast’s expansive suite of APIs , identify and solve issues during the development process. Once we discovered how helpful it was, we knew we had to open source it so our customers and the open source community could benefit too.
UKFast’s Laravel Health Check package supports lots of core health checks out of the box, with more being added periodically. In this blog, we’re looking at how the package makes monitoring new functionality easier.
We’re going to be adding a CacheHealthCheck
for our apps, which is now a part of the Laravel Health Check package.
We’ll need to create our new check inside the App\HealthChecks
directory calledCacheHealthCheck.php
.
Below is the basic structure of our check file. You’ll see we’ve given the check a name and set the status to return OK
for now.
<?php
namespace App\HealthChecks;
use UKFast\HealthCheck\HealthCheck;
class CacheHealthCheck extends HealthCheck
{
/**
* The friendly name for this check
*/
protected $name = 'cache';
/**
* In the status method we need to define
* the logic needed to decide whether the
* check should pass or fail
*/
public function status()
{
/**
* For now, let's mark the check as 'OK'
*/
return $this->okay();
}
}
The package contains a config/healthcheck.php
file where our configuration goes. This can be published and altered by users, but for simplicity, we’ll stick to the Laravel defaults.
We want our new check to work out of the box, but we also want users to specify which cache stores to run checks against.
So, in our healthcheck.php
config file, let’s add our config.
/**
* Can define a list of cache stores to test. Names can be
* found in your config/cache.php file. By default, we'll
* just check the 'file' store
*/
'cache' => [
'stores' => ['file'],
],
Now if the user wants to use the CacheHealthCheck
out of the box, it’ll work.
Inside our status()
method we’re going to build out our cache check.
We want to ensure that we can store something in our application’s cache and get the value back out (removing it as we go).
We also want to support checking multiple cache stores, which is as easy as using a foreach loop on the stores array we added to the config in the last step.
<?php
namespace App\HealthChecks;
use Illuminate\Support\Facades\Cache;
use UKFast\HealthCheck\HealthCheck;
class CacheHealthCheck extends HealthCheck
{
/**
* The friendly name for this check
*/
protected $name = 'cache';
/**
* Contains cache stores which have
* been tested successfully
*/
private $workingStores = [];
/**
* Contains cache stores which have
* returned incorrect values
*/
private $incorrectValues = [];
/**
* Contains cache stores which
* threw exceptions
*/
private $exceptions = [];
/**
* In the status method we need to define
* the logic needed to decide whether the
* check should pass or fail
*/
public function status()
{
/**
* Let's loop through the cache stores
* we configured in our config file
*/
foreach (config('healthcheck.cache.stores') as $store) {
try {
/**
* Let's get the store for this check
*/
$cache = Cache::store($store);
/**
* Let's store 'healthy' in the cache with
* a key of 'laravel-health-check' and keep
* it cached for 60 minutes
*/
$cache->put('laravel-health-check', 'healthy', 60);
/**
* Next, we'll check that the value was
* cached correctly and return 'broken'
* if we can't find anything
*/
$value = $cache->pull('laravel-health-check', 'broken');
/**
* If the value isn't as expected but it
* didn't throw an exception, we'll add
* it to our incorrectValues array
*/
if ($value != 'healthy') {
$this->incorrectValues[] = [
'store' => $store,
'value' => $value
];
continue;
}
/**
* At this point we're sure the value
* returned is correct, so we can add it
* to our workingStores array
*/
$this->workingStores[] = $store;
} catch (\Exception $e) {
/**
* If the check threw any exceptions
* we'll add it to our exceptions array
*/
$this->exceptions[] = [
'store' => $store,
'error' => $e->getMessage()
];
}
}
/**
* If after looping through our stores, all
* tests have been successful, we'll pass
* the check, returning an 'OK' response
*/
if (empty($this->incorrectValues) && empty($this->exceptions)) {
return $this->okay();
}
/**
* However, if there has been a problem with
* one or more stores, we'll fail the check
* with the results of each cache test
*/
return $this->problem(
'Some cache connections are not working',
[
'working' => $this->workingStores,
'incorrect_values' => $this->incorrectValues,
'exceptions' => $this->exceptions
]
);
}
}
Now we’ve created our check, we can jump into a fresh Laravel 7 application and see the magic happen. If we add the database
store to our config/healthcheck.php
file (without having migrated the database table), the check will fail.
"cache": {
"status": "PROBLEM",
"message": "Some cache connections are not working",
"context": {
"working": [],
"incorrect_values": [],
"exceptions": [
{
"store": "database",
"error": "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'homestead.cache' doesn't exist (SQL: update `cache` set `value` = s:7:\"healthy\";, `expiration` = 1595197676 where `key` = laravel_cachelaravel-health-check)"
}
]
}
}
If we add the array
store (which is configured out of the box), it will show up in the working
array.
"cache": {
"status": "PROBLEM",
"message": "Some cache connections are not working",
"context": {
"working": [
"array"
],
"incorrect_values": [],
"exceptions": [
{
"store": "database",
"error": "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'homestead.cache' doesn't exist (SQL: update `cache` set `value` = s:7:\"healthy\";, `expiration` = 1595197802 where `key` = laravel_cachelaravel-health-check)"
}
]
}
}
Finally, if we remove the database store (or migrate the cache
database table), the check will return an OK
status.
"cache": {
"status":"OK"
}
As with all good code, we need to write a test, which we’ll include when committing this check into the package. The latest version of the CacheHealthCheck
can be found in the UKFast GitHub repository.
Hopefully, this post demonstrates how useful developing custom functionality using open source packages can be.
If you find our Laravel package useful, show your support by starring it on GitHub and let us know on Twitter @UKFast.
We hope you enjoy using the package as much as we do!