ohdearapp/health-check-results
Install the package:
composer require ohdearapp/health-check-results
Create a health check endpoint (e.g., routes/web.php):
use OhDearApp\HealthCheckResults\CheckResults;
use OhDearApp\HealthCheckResults\CheckResult;
Route::get('/health-check', function () {
$checkResults = new CheckResults(now());
$checkResults->addCheckResult(new CheckResult(
name: 'DatabaseConnection',
label: 'Database Connection',
notificationMessage: 'Database is unreachable',
shortSummary: 'Failed',
status: CheckResult::STATUS_FAILED,
meta: ['error' => 'Connection timeout']
));
return response()->json($checkResults->toArray());
});
Configure Oh Dear to poll this endpoint (e.g., https://your-app.test/health-check).
Start with critical checks like:
Example:
$checkResults = new CheckResults(now());
// Database check
$checkResults->addCheckResult(new CheckResult(
name: 'Database',
label: 'Database Status',
notificationMessage: 'Database connection failed',
shortSummary: 'Failed',
status: CheckResult::STATUS_FAILED,
meta: ['query' => 'SELECT 1']
));
// Disk space check
$checkResults->addCheckResult(new CheckResult(
name: 'DiskSpace',
label: 'Disk Space',
notificationMessage: 'Disk usage exceeds 90%',
shortSummary: '92%',
status: CheckResult::STATUS_WARNING,
meta: ['usage_percentage' => 92]
));
Organize checks into logical groups (e.g., Infrastructure, Application, ThirdParty):
$checkResults = new CheckResults(now());
// Infrastructure checks
$checkResults->addCheckResult(new CheckResult(
name: 'Storage',
label: 'Storage Health',
status: CheckResult::STATUS_OK,
shortSummary: 'OK',
meta: ['free_space_gb' => 50]
));
// Application checks
$checkResults->addCheckResult(new CheckResult(
name: 'Cache',
label: 'Redis Cache',
status: CheckResult::STATUS_FAILED,
notificationMessage: 'Redis connection lost',
meta: ['last_ping' => '2023-01-01T00:00:00Z']
));
Use closures for checks that require runtime logic (e.g., API calls):
$checkResults = new CheckResults(now());
$checkResults->addCheckResult(new CheckResult(
name: 'StripeAPI',
label: 'Stripe API',
status: fn() => (new StripeService())->ping() ? CheckResult::STATUS_OK : CheckResult::STATUS_FAILED,
notificationMessage: 'Stripe API is unavailable',
shortSummary: fn() => (new StripeService())->ping() ? 'OK' : 'Failed',
meta: ['last_ping' => now()->toIso8601String()]
));
Trigger health checks on critical events (e.g., job.failed):
use Illuminate\Support\Facades\Event;
Event::listen('job.failed', function ($event) {
$checkResults = new CheckResults(now());
$checkResults->addCheckResult(new CheckResult(
name: 'QueueWorker',
label: 'Queue Worker',
status: CheckResult::STATUS_FAILED,
notificationMessage: 'Job failed: ' . $event->job->payload['command'],
meta: ['job_id' => $event->job->id]
));
// Store or return results (e.g., via API)
});
Use Laravel’s task scheduling (app/Console/Kernel.php) to run periodic checks:
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
$checkResults = new CheckResults(now());
// Add checks here (e.g., database backups, cache warming)
$checkResults->addCheckResult(new CheckResult(
name: 'Backup',
label: 'Database Backup',
status: Storage::disk('backups')->exists('latest.sql') ? CheckResult::STATUS_OK : CheckResult::STATUS_FAILED,
notificationMessage: 'Backup failed',
meta: ['last_backup' => Storage::disk('backups')->lastModified('latest.sql')]
));
// Log or return results
})->hourly();
}
Create factories for common checks (e.g., app/Services/HealthChecks.php):
namespace App\Services;
use OhDearApp\HealthCheckResults\CheckResult;
class HealthChecks
{
public static function databaseCheck(): CheckResult
{
return new CheckResult(
name: 'Database',
label: 'Database Connection',
status: self::isDatabaseReachable() ? CheckResult::STATUS_OK : CheckResult::STATUS_FAILED,
notificationMessage: 'Database is unreachable',
shortSummary: self::isDatabaseReachable() ? 'OK' : 'Failed',
meta: ['last_query' => now()->toIso8601String()]
);
}
private static function isDatabaseReachable(): bool
{
// Logic to check DB connection
return true;
}
}
Usage:
$checkResults->addCheckResult(HealthChecks::databaseCheck());
Status Naming:
CheckResult::STATUS_OK, STATUS_WARNING, or STATUS_FAILED (not strings like "ok").$status = CheckResult::STATUS_FAILED; (not "failed").Meta Data Types:
meta field must be an array (not an object or string).meta: ['key' => 'value'] (not meta: new stdClass()).Timestamp Precision:
finishedAt expects a Unix timestamp (integer). Use now()->timestamp or strtotime($date).DateTime objects directly; convert to timestamp first.Notification Messages:
Performance:
Cache::remember()).Validate JSON Output: Use Oh Dear’s test endpoint to validate your JSON:
curl -X POST https://ohdear.app/api/v1/health-checks/test \
-H "Content-Type: application/json" \
-d @path/to/your/health-check.json
Log Raw Results:
Log the raw CheckResults object before returning JSON to debug:
\Log::debug('Health check results:', $checkResults->toArray());
Check for Deprecations:
CheckResult::meta type hints).Custom Statuses:
Extend the package by adding a CheckResult subclass for custom statuses:
class CustomCheckResult extends CheckResult
{
public const STATUS_MAINTENANCE = 'maintenance';
public static function maintenanceCheck(): self
{
return new self(
name: 'Maintenance',
label: 'System Maintenance',
status: self::STATUS_MAINTENANCE,
shortSummary: 'In Maintenance',
meta: ['scheduled_until' => now()->addHours(2)]
);
}
}
Batch Processing: Process checks in batches to avoid timeouts:
$checkResults = new CheckResults(now());
$batch = collect([...])->chunk(10);
foreach ($batch as $items) {
foreach ($items as $item) {
$checkResults->addCheckResult($this->createCheckFor($item));
}
}
Integration with Laravel Horizon:
Use Horizon’s FailedJob events to auto-generate health checks:
Horizon::failed(function ($job, $exception) {
$checkResults = new CheckResults(now());
$checkResults->addCheckResult(new CheckResult(
name: 'QueueJob',
label: 'Failed Job',
status: CheckResult::STATUS_FAILED,
notificationMessage: "Job {$job->payload['command']} failed",
meta: ['
How can I help you explore Laravel packages today?