Robust application monitoring for Laravel web apps
Quality focus areas for a modern web application
Building a stable web app is key to delighting customers and delivering value. There are a few core pieces to ship a quality web app, outside of the core business logic:
- Write unit tests for your application
- Enable application error reporting
- Enable application monitoring to be notified when things go wrong
- Enable application tracing and logs to deep dive into where things are going slow
- ... and a whole lot more :)
For this post, we'll focus on application monitoring. By the end we will configure our Laravel app to use OhDear, an application monitoring service by the awesome folks at Spatie to monitor various aspects of our app and server:
-
Web server (external POV: no code changes)
- Website uptime
- SSL certs expiration
- Average latency
- ...
-
Laravel application (internal POV: minimal code changes required)
- CPU load
- Disk space
- Schedule is running
- Database & redis are connecting properly
- App is optimized
- ...
laravel-health
and OhDear.app
Setting up Install dependencies
Following the install instructions, install laravel-health
package via composer
:
1composer install laravel-health
OhDear configuration
Sign up on OhDear.app to get a free 14-day trial for application monitoring (I am not sponsored, just a happy customer!). It's crucial to getting insights into your application and your web server.
Once you have an account, follow the laravel-health
instructions to configure the package to use OhDear.
You'll have to publish the config config/health.php
and modify the oh_dear_endpoint
in the config to be enabled:
1'oh_dear_endpoint' => [ 2 'enabled' => true, 3 4 /* 5 * When this option is enabled, the checks will run before sending a response. 6 * Otherwise, we'll send the results from the last time the checks have run. 7 */ 8 'always_send_fresh_results' => true, 9 10 /*11 * The secret that is displayed at the Application Health settings at Oh Dear.12 */13 'secret' => env('OH_DEAR_HEALTH_CHECK_SECRET'), 14 15 /*16 * The URL that should be configured in the Application health settings at Oh Dear.17 */18 'url' => '/oh-dear-health-check-results', 19 ],
Generate a random health check secret key and put in your .env.
:
1OH_DEAR_HEALTH_CHECK_SECRET={YOUR_SECRET_HERE}
Enable OhDear Application health check for your domain
To enable Application health on your OhDear account: add your domain (if you haven't already) and then turn on Application health
. Open up the Settings
section and set:
-
Health Report URL
to:https://{my-domain}.com/oh-dear-health-check-results
-
Health Report Secret
to your.env
secret forOH_DEAR_HEALTH_CHECK_SECRET
- Save changes
Including the most important health checks
Following instructions for registering health checks, create a new Service Provider: HealthCheckServiceProvider
via php artisan make:provider HealthCheckServiceProvider
and add health checks to the boot method:
1namespace App\Providers;
2 3use Illuminate\Support\ServiceProvider; 4use Spatie\CpuLoadHealthCheck\CpuLoadCheck; 5use Spatie\Health\Checks\Checks\CacheCheck; 6use Spatie\Health\Checks\Checks\DatabaseCheck; 7use Spatie\Health\Checks\Checks\DebugModeCheck; 8use Spatie\Health\Checks\Checks\EnvironmentCheck; 9use Spatie\Health\Checks\Checks\OptimizedAppCheck;10use Spatie\Health\Checks\Checks\RedisCheck;11use Spatie\Health\Checks\Checks\ScheduleCheck;12use Spatie\Health\Checks\Checks\UsedDiskSpaceCheck;13use Spatie\Health\Facades\Health; 14 15class HealthCheckServiceProvider extends ServiceProvider16{17 public function register()
18 {19 //20 } 21 22 public function boot()23 {24 Health::checks([25 UsedDiskSpaceCheck::new(),26 DatabaseCheck::new(),27 CpuLoadCheck::new()28 ->failWhenLoadIsHigherInTheLast5Minutes(2.0)29 ->failWhenLoadIsHigherInTheLast15Minutes(1.5),30 DebugModeCheck::new(),31 EnvironmentCheck::new(),32 ScheduleCheck::new()->heartbeatMaxAgeInMinutes(5),33 CacheCheck::new(),34 OptimizedAppCheck::new(),35 RedisCheck::new(),36 ]);37 }38}
Each health check is configurable, for example you can modify the CPU load alerting properties via:
-
failWhenLoadIsHigherInTheLast5Minutes
-
failWhenLoadIsHigherInTheLast15Minutes
The Schedule check has an important modification: heartbeatMaxAgeInMinutes
which sets how long the schedule can not report to be running, before firing an alert. I've found this to produce a lot of noise with the default 1 minute heartbeat, so I'd recommend at least 2+ minutes, but I would find tune based on your deployment / application.
Eliminate false monitoring alarms during deployments
These health checks will be run every minute, when OhDear pings your health check URL. Sometimes it will ping your app when you are deploying, so these checks will fail, and that is to be expected, but we will fix it! So to fix these false alarms, OhDear provides a CLI tool that can allow you to put your site in a "maintenance window".
You will need to install the ohdear-cli
tool on your server, then modify your deployment script to start a maintenance window on deploy begin, and end it on deploy finish:
1# OhDear ENV token 2export OHDEAR_API_TOKEN={YOUR_TOKEN_HERE} 3 4OH_DEAR_CLI=/home/forge/.config/composer/vendor/bin/ohdear-cli 5OH_DEAR_SITE_ID={MY_SITE_ID} 6OH_DEAR_MAINTENANCE_WINDOW_SECONDS=60 7 8# Start maintenance for ohdear checks (site-id, seconds) 9$OH_DEAR_CLI maintenance-period:start $OH_DEAR_SITE_ID $OH_DEAR_MAINTENANCE_WINDOW_SECONDS10 11all12my13deployment14steps15here16 17# Stop maintenance period18$OH_DEAR_CLI maintenance-period:stop $OH_DEAR_SITE_ID
What monitoring we've achieved so far
OhDear brings a lot of website monitoring out of the box, when you onboard your domain:
- Website uptime
- SSL certs expiration
- Domain expiration
When we add laravel-health
into the mix and turn on Application health
as a health check for our website in OhDear, we unlock a whole lot more monitoring:
- Disk space
- CPU load
- Schedule
- DB & Redis connections
- App optimized
- Environment + debug check
This list of monitoring capabilities alone is worth gold! We can enable Slack or other notifications via OhDear's settings, depending on what works for your team. This means you will get alerted when a health check fails, when the website is down, or when the CPU load is too high.
What's next?
With our schedule check, we are only checking if the schedule is running on an interval. We are not checking the individual entries we have in our Laravel Scheduler
. There is yet another package for this: spatie/laravel-schedule-monitor
that will enable this level of granularity, and I'd highly recommend it!