Installation:
composer require cushon/health-bundle
Add to config/bundles.php:
return [
// ...
Cushon\HealthBundle\CushonHealthBundle::class => ['all' => true],
];
First Health Check:
Create a custom health check class (e.g., src/Health/DatabaseHealthCheck.php):
namespace App\Health;
use Cushon\HealthBundle\Health\HealthCheckInterface;
use Cushon\HealthBundle\Health\Result\HealthResult;
use Doctrine\DBAL\Connection;
class DatabaseHealthCheck implements HealthCheckInterface
{
public function __construct(private Connection $connection) {}
public function check(): HealthResult
{
return $this->connection->getSchemaManager()->listTableNames()
? HealthResult::healthy()
: HealthResult::unhealthy('Database connection failed');
}
}
Register the Check:
Configure in config/packages/cushon_health.yaml:
cushon_health:
checks:
- App\Health\DatabaseHealthCheck
Trigger Health Endpoint:
Access /health (default route) or /health/{check_name} (e.g., /health/database).
/health endpoint for monitoring tools (e.g., Prometheus, Kubernetes liveness probes).HttpClient to poll dependent services (e.g., databases, APIs) and aggregate results.# config/services.yaml
tags:
cushon_health.check:
- { service: App\Health\DatabaseHealthCheck }
check_priority in config:
cushon_health:
checks:
- { id: database, service: App\Health\DatabaseHealthCheck, priority: 100 }
Cushon\HealthBundle\Health\Result\HealthResultAggregator to implement custom logic (e.g., weighted voting).
class CustomAggregator implements HealthResultAggregator
{
public function aggregate(array $results): HealthResult
{
$healthy = array_filter($results, fn($r) => $r->isHealthy());
return count($healthy) >= 2 ? HealthResult::healthy() : HealthResult::unhealthy();
}
}
Register in config:
cushon_health:
aggregator: App\Health\CustomAggregator
symfony/http-client to call the health endpoint from Laravel:
$client = new Client();
$response = $client->request('GET', 'http://symfony-app/health');
$status = json_decode($response->getContent(), true)['status'];
App\Shared\Health\CheckInterface).$check = new DatabaseHealthCheck($mockConnection);
$result = $check->check();
$this->assertTrue($result->isHealthy());
Http::fake() or Symfony’s WebTestCase to test /health routes.CacheHealthCheck depends on DatabaseHealthCheck):
class CacheHealthCheck implements HealthCheckInterface
{
public function __construct(private HealthCheckInterface $databaseCheck) {}
public function check(): HealthResult
{
if (!$this->databaseCheck->check()->isHealthy()) {
return HealthResult::unhealthy('Database dependency failed');
}
// ... cache-specific logic
}
}
Circular Dependencies:
CheckA depends on CheckB, which depends on CheckA). Use priority ordering or refactor.Performance Overhead:
/health responses. Optimize with lightweight probes (e.g., ping queries).Configuration Overrides:
config/packages/cushon_health.yaml can be overridden by environment-specific configs (e.g., config/packages/dev/cushon_health.yaml). Ensure critical checks are not disabled accidentally.Symfony-Specific Quirks:
kernel.request), ensure health checks don’t trigger side effects (e.g., logging, notifications) during probes.Laravel-Symfony Integration:
Check Execution Order:
check_priority to debug why a check is skipped or runs out of order. Higher priority = earlier execution.Result Inspection:
public function check(): HealthResult
{
$result = HealthResult::unhealthy('Debug: ' . $this->connection->getWarnings());
return $result;
}
Endpoint Debugging:
APP_DEBUG=1) to inspect the /_profiler endpoint for check execution details.Idempotent Checks:
Custom HTTP Status Codes:
HealthResult to include custom HTTP status codes (e.g., 503 for maintenance mode):
return HealthResult::unhealthy('Maintenance', 503);
Environment-Specific Checks:
cushon_health:
checks:
- { id: logs, service: App\Health\LogHealthCheck, enabled: '%kernel.environment% != "prod"' }
Prometheus Metrics:
sensio_framework_extra or symfony/monolog-bundle:
$start = microtime(true);
$result = $this->databaseCheck->check();
$duration = microtime(true) - $start;
// Log $duration for Prometheus scraping
Documentation:
README.md in your project’s docs/health folder to document:
/health).Security:
/health in production:
# config/routes.yaml
health:
path: /health
controller: Cushon\HealthBundle\Controller\HealthController::checkAction
methods: [GET]
requirements:
_ip: 192.168.1.0/24 # Allow only internal IPs
Laravel-Specific Workarounds:
$this->app->bind(
Cushon\HealthBundle\Health\HealthCheckerInterface::class,
fn($app) => new HealthChecker($app->make('cushon_health.checker'))
);
# config/routes.yaml
_symfony_health:
resource: "@CushonHealthBundle/Resources/config/routing.yaml"
prefix: /_symfony
How can I help you explore Laravel packages today?