Batteries included UI to monitor your Messenger workers, transports, schedules, and messages.
If the packaged UI is not to your liking, you can easily build your own with the provided tools.
composer require zenstruck/messenger-monitor-bundle
With zero configuration, you can run the messenger:monitor
command to see information
about your running workers and transports. If storage is configured, it displays a
historical snapshot whose period can be customized with the --period
option.
Note
This step is required to use the User Interface and History.
Note
Only Doctrine ORM is currently available as a storage engine.
-
Create a
ProcessedMessage
entity that extendsZenstruck\Messenger\Monitor\History\Model\ProcessedMessage
in your app:// src/Entity/ProcessedMessage.php namespace App\Entity; use Zenstruck\Messenger\Monitor\History\Model\ProcessedMessage as BaseProcessedMessage; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(readOnly: true)] #[ORM\Table('processed_messages')] class ProcessedMessage extends BaseProcessedMessage { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; public function id(): ?int { return $this->id; } }
-
Add the entity class to the bundle config:
# config/packages/zenstruck_messenger_monitor.yaml zenstruck_messenger_monitor: storage: orm: entity_class: App\Entity\ProcessedMessage
-
Clear Cache:
bin/console cache:clear
-
Create and execute the migration:
bin/console doctrine:migrations:diff bin/console doctrine:migrations:migrate
Once configured, consumed messages are tracked and saved. These processed messages contain a lot of useful information and can be viewed in the user interface or the provided tools.
You may want to disable monitoring for certain messages. There are several ways to do this:
- When dispatching the message, add the
DisableMonitoringStamp
:use Zenstruck\Messenger\Monitor\Stamp\DisableMonitoringStamp; /** @var \Symfony\Component\Messenger\MessageBusInterface $bus */ $bus->dispatch(new MyMessage(), [new DisableMonitoringStamp()])
- Add the
DisableMonitoringStamp
as a class attribute to your message (or parent class/interface):You may want to disable monitoring for messages that are dispatched without any handler. You can do this by using theuse Zenstruck\Messenger\Monitor\Stamp\DisableMonitoringStamp; #[DisableMonitoringStamp] class MyMessage { }
DisableMonitoringStamp
with optional constructor argumenttrue
:use Zenstruck\Messenger\Monitor\Stamp\DisableMonitoringStamp; #[DisableMonitoringStamp(onlyWhenNoHandler: true)] class MyMessage { }
- Add the message class to the
exclude
config option (can be abstract/interface):# config/packages/zenstruck_messenger_monitor.yaml zenstruck_messenger_monitor: storage: exclude: - App\Message\MyMessage
The stored ProcessedMessage
has a description property. This is helpful to differentiate between
messages in the user interface. By default, this is the stringified version of the message object
(if it implements \Stringable
). You can add the DescriptionStamp
to customize:
use Zenstruck\Messenger\Monitor\Stamp\DescriptionStamp;
/** @var \Symfony\Component\Messenger\MessageBusInterface $bus */
$bus->dispatch(new MyMessage(), [new DescriptionStamp('some custom description')])
To help with filtering processed messages, they can have one or more tags. Some tags
are added automatically (like schedule
if it's a scheduled message) but you can also
add your own in one of two ways:
- When dispatching the message, add one or more
TagStamp
's:use Zenstruck\Messenger\Monitor\Stamp\TagStamp; /** @var \Symfony\Component\Messenger\MessageBusInterface $bus */ $bus->dispatch(new MyMessage(), [new TagStamp('tag-1'), new TagStamp('tag-2')])
- Add the
TagStamp
as a class attribute to your message (and parent class/interface):use Zenstruck\Messenger\Monitor\Stamp\TagStamp; #[TagStamp('tag-1')] #[TagStamp('tag-2')] class MyMessage { }
[!TIP] You can also add the
TagStamp
attribute to parent classes/interfaces.
If your app handles a lot of messages, the processed message database table will get very large.
The messenger:monitor:purge
clears messages older than a specific date:
See Period
for allowed values.
bin/console messenger:monitor:purge # by default, purges all messages older than 1 month
bin/console messenger:monitor:purge --older-than all
bin/console messenger:monitor:purge --older-than 1-day
bin/console messenger:monitor:purge --older-than 1-week
bin/console messenger:monitor:purge --exclude-schedules # ignore messages tagged with "schedule"
Note
Schedule this command to run daily with symfony/scheduler
and
RunCommandMessage
.
If using symfony/scheduler
, you might want to keep a specific # of these messages as they might run
very infrequently. When running messenger:monitor:purge
, add the --exclude-schedules
option to
avoid deleting schedule history. Then run messenger:monitor:schedule:purge
to keep a specific number
(10 by default) of task run histories.
bin/console messenger:monitor:schedule:purge # by default, keeps 10 runs for each task
bin/console messenger:monitor:schedule:purge --keep 5 # keep only 5
Use the --remove-orphans
option to delete schedule task runs that are no longer associated with a schedule.
bin/console messenger:monitor:schedule:purge --remove-orphans
Note
Schedule this command to run daily with symfony/schedule
and
RunCommandMessage
.
Note
Storage must be configured for this feature.
Create a controller that extends Zenstruck\Messenger\Monitor\Controller\MessengerMonitorController
in your app:
// src/Controller/MessengerMonitorController.php
namespace App\Controller;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Zenstruck\Messenger\Monitor\Controller\MessengerMonitorController as BaseMessengerMonitorController;
#[Route('/admin/messenger')] // path prefix for the controllers
#[IsGranted('ROLE_ADMIN')] // alternatively, use a firewall
final class MessengerMonitorController extends BaseMessengerMonitorController
{
}
You can now access the dashboard at: /admin/messenger
or with the route zenstruck_messenger_monitor_dashboard
.
Warning
It is important that your MessengerMonitorController
is only accessible by
site administrators as it contains sensitive application information. Use either the
IsGranted
attribute on your controller as shown above and/or ensure the controller is
behind an access-controlled firewall
that only allows site administrators.
Note
Install knplabs/knp-time-bundle
(composer require knplabs/knp-time-bundle
) to display
friendlier times and durations in the UI.
Note
Install lorisleiva/cron-translator
(composer require lorisleiva/cron-translator
) to display
friendlier CRON values for your scheduled tasks.
Note
Storage must be configured for this feature.
While you run php bin/console messenger:consume async [-vv]
you see a lot of messages like this one.
[cache] Lock acquired, now computing item "zenstruck_messenger_monitor.worker.xxx" ["key" => "zenstruck_messenger_monitor.worker.xxx"]
If you want to not display them, you can disable them with adding "!cache"
to the console-channels in config/packages/monolog.yaml
when@dev:
monolog:
handlers:
#...
console:
#...
channels: ["!event", "!doctrine", "!console", "!cache"]
zenstruck_messenger_monitor:
storage:
# Message classes to disable monitoring for (can be abstract/interface)
exclude: []
orm:
# Your Doctrine entity class that extends "Zenstruck\Messenger\Monitor\History\Model\ProcessedMessage"
entity_class: ~
cache:
pool: app.cache # If using workers in docker. You can use shared cache pool for all workers
expired_worker_ttl: 3600