Alexa metrics
Live Chat

Welcome to UKFast, do you have a question? Our hosting experts have the answers.

Chat Now
Sarah UKFast | Account Manager

Dev Blog: Step by step open source in practice

16 October 2020 by Guest

This guest post was authored by UKFast R&D team member Tyler Woonton.

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.

Let’s write a custom health check

We’re going to be adding a CacheHealthCheck for our apps, which is now a part of the Laravel Health Check package.

Step 1: Create the file

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();
    }
}

Step 2: Add the configuration

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.

Step 3: Add your functionality

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
            ]
        );
    }
}

Step 4: Testing our check

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.

Conclusion

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!

At UKFast we care about empowering our clients while giving back to the digital community. Find out more about our open source contributions.LEARN MORE