A Symfony bundle to create and run diagnostic probes.
Add the bundle to your project via Composer:
composer require arty/probe-bundleIf you are not using Symfony Flex, you will need to register the bundle manually in config/bundles.php:
return [
// ...
Arty\ProbeBundle\ArtyProbeBundle::class => ['all' => true],
];Create a configuration file at config/packages/arty_probe.yaml:
arty_probe:
probe_status_history_class: App\Entity\ProbeStatusHistory
alerting:
enabled: true
channel: email # or chat
to: "admin@example.com" # required for email channelThe bundle uses Symfony Notifier to send alerts when a probe fails for the first time. Two channels are available: email and chat.
Install and configure Symfony Mailer:
composer require symfony/mailer# config/packages/arty_probe.yaml
arty_probe:
alerting:
enabled: true
channel: email
to: "admin@example.com"Make sure your mailer is configured (e.g. MAILER_DSN in .env). The sender address is configured in your mailer config:
# config/packages/mailer.yaml
framework:
mailer:
dsn: '%env(MAILER_DSN)%'
envelope:
sender: 'no-reply@example.com'Install the transport for your chat service:
# Pick one:
composer require symfony/slack-notifier
composer require symfony/microsoft-teams-notifierConfigure the transport DSN and the bundle:
# config/packages/notifier.yaml
framework:
notifier:
chatter_transports:
slack: '%env(SLACK_DSN)%'
# config/packages/arty_probe.yaml
arty_probe:
alerting:
enabled: true
channel: chatSet the DSN in your .env:
SLACK_DSN=slack://TOKEN@default?channel=CHANNELBy default, if Symfony Messenger is installed, notifications are sent asynchronously. To send them synchronously, add
message_bus: falseto yourframework.notifierconfig. See Symfony Notifier docs for details.
Make it extend the base Arty\ProbeBundle\Entity\ProbeStatusHistory :
<?php
declare(strict_types=1);
namespace App\Entity;
use Arty\ProbeBundle\Doctrine\Repository\ProbeStatusHistoryRepository;
use Arty\ProbeBundle\Entity\ProbeStatusHistory as BaseProbeStatusHistory;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table('probe_status_history')]
class ProbeStatusHistory extends BaseProbeStatusHistory
{
}Run migrations to create the table, and you are good to go !
To create a probe, you need to implement the ProbeInterface and add the #[Probe] attribute to your class.
A probe must implement Arty\ProbeBundle\Model\ProbeInterface, which requires a check() method returning an int.
The #[Probe] attribute registers your service as a probe and allows you to configure its behavior.
The attribute accepts the following parameters:
name(required): A unique identifier for the probedescription(optional): A human-readable description of what the probe checkssuccessThreshold(optional, default: 0): The threshold for success statuswarningThreshold(optional, default: 1): The threshold for warning statusfailureThreshold(optional, default: 2): The threshold for failure statusnotify(optional, default: true): Whether to send alerts when the probe fails
namespace App\Probe;
use Arty\ProbeBundle\Attribute\Probe;
use Arty\ProbeBundle\Model\ProbeInterface;
#[Probe(name: 'database_connectivity', description: 'Checks if the database is reachable')]
class DatabaseProbe implements ProbeInterface
{
public function check(): int
{
// Your logic here
// Return 0 for success, 1 for warning, 2 for failure (by default you can use Probe constants)
return Probe::SUCCESS;
}
}There are two main strategies for returning values from your probes:
By default, the bundle uses the following status mapping based on the value returned by check():
- 0:
ProbeStatus::SUCCESS - 1:
ProbeStatus::WARNING - 2 or more:
ProbeStatus::FAILED
You can use the constants provided in the Probe attribute for clarity:
public function check(): int
{
if ($this->isHealthy()) {
return Probe::SUCCESS; // 0
}
if ($this->hasMinorIssues()) {
return Probe::WARNING; // 1
}
return Probe::FAILURE; // 2
}You can customize the thresholds in the #[Probe] attribute. This is particularly useful when the returned value represents a measurable metric, such as the number of corrupted records in a database.
#[Probe(
name: 'data_integrity',
successThreshold: 0,
warningThreshold: 10,
failureThreshold: 50,
description: 'Monitors the number of corrupted records',
notify: true,
)]
class DataIntegrityProbe implements ProbeInterface
{
public function check(): int
{
$corruptedCount = $this->repository->countCorruptedData();
// The bundle will evaluate the status based on the thresholds:
// < 10 => SUCCESS
// >= 10 and < 50 => WARNING
// >= 50 => FAILED
return $corruptedCount;
}
}You can run all registered probes using the provided console command:
php bin/console arty:probe:runThis will execute each probe, store the result in the database, and send an alert if a probe fails (and it wasn't already failing).
The ProbeManagerInterface provides a set of methods to interact with the probe status history. You can inject this interface into your services to retrieve or manage probe results.
findLastByProbeName(string $name): Returns the most recent status history for a specific probe.findAllLastStatuses(): Returns the latest status for every registered probe.findLast5ByProbeName(string $name): Returns the 5 most recent status history records for a specific probe.create(...): Creates a newProbeStatusHistoryinstance.save(ProbeStatusHistory $history): Persists a status history record to the database.delete(ProbeStatusHistory $history): Removes a status history record.
namespace App\Controller;
use Arty\ProbeBundle\Model\ProbeManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DashboardController extends AbstractController
{
#[Route('/admin/probes', name: 'admin_probes')]
public function index(ProbeManagerInterface $probeManager): Response
{
$lastStatuses = $probeManager->findAllLastStatuses();
return $this->render('admin/probes.html.twig', [
'statuses' => $lastStatuses,
]);
}
}