Skip to content

Commit

Permalink
feature #49795 add debug:scheduler command (kbond)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the 6.3 branch.

Discussion
----------

add `debug:scheduler` command

| Q             | A
| ------------- | ---
| Branch?       | 6.3
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | n/a
| License       | MIT
| Doc PR        | todo

Example output:

```
Scheduler
=========

default
-------

 ---------------------- ------------------- ---------------------------
  Message                Trigger             Next Run
 ---------------------- ------------------- ---------------------------
  stdClass               cron: 10 10 5 2 0   2024-02-04T10:10:00-05:00
  generate user report   cron: 0 0 * * *     2023-03-25T00:00:00-04:00
 ---------------------- ------------------- ---------------------------
```

The output is helped if the trigger and message implement `\Stringable`. I've made `CronExpressionTrigger` stringable but I think we should have all the packaged triggers implement.

Commits
-------

398e2b2 add `debug:scheduler` command
  • Loading branch information
fabpot committed Mar 25, 2023
2 parents b01130d + 398e2b2 commit 50fbe01
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2016,6 +2016,10 @@ private function registerSchedulerConfiguration(array $config, ContainerBuilder
}

$loader->load('scheduler.php');

if (!$this->hasConsole()) {
$container->removeDefinition('console.command.scheduler_debug');
}
}

private function registerMessengerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $validationEnabled): void
Expand Down
11 changes: 9 additions & 2 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@
use Symfony\Component\Console\EventListener\ErrorListener;
use Symfony\Component\Dotenv\Command\DebugCommand as DotenvDebugCommand;
use Symfony\Component\Messenger\Command\ConsumeMessagesCommand;
use Symfony\Component\Messenger\Command\DebugCommand;
use Symfony\Component\Messenger\Command\DebugCommand as MessengerDebugCommand;
use Symfony\Component\Messenger\Command\FailedMessagesRemoveCommand;
use Symfony\Component\Messenger\Command\FailedMessagesRetryCommand;
use Symfony\Component\Messenger\Command\FailedMessagesShowCommand;
use Symfony\Component\Messenger\Command\SetupTransportsCommand;
use Symfony\Component\Messenger\Command\StatsCommand;
use Symfony\Component\Messenger\Command\StopWorkersCommand;
use Symfony\Component\Scheduler\Command\DebugCommand as SchedulerDebugCommand;
use Symfony\Component\Translation\Command\TranslationPullCommand;
use Symfony\Component\Translation\Command\TranslationPushCommand;
use Symfony\Component\Translation\Command\XliffLintCommand;
Expand Down Expand Up @@ -172,7 +173,7 @@
])
->tag('console.command')

->set('console.command.messenger_debug', DebugCommand::class)
->set('console.command.messenger_debug', MessengerDebugCommand::class)
->args([
[], // Message to handlers mapping
])
Expand Down Expand Up @@ -218,6 +219,12 @@
])
->tag('console.command')

->set('console.command.scheduler_debug', SchedulerDebugCommand::class)
->args([
tagged_locator('scheduler.schedule_provider', 'name'),
])
->tag('console.command')

->set('console.command.router_debug', RouterDebugCommand::class)
->args([
service('router'),
Expand Down
96 changes: 96 additions & 0 deletions src/Symfony/Component/Scheduler/Command/DebugCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Scheduler\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Symfony\Contracts\Service\ServiceProviderInterface;

use function Symfony\Component\Clock\now;

/**
* Command to list/debug schedules.
*
* @author Kevin Bond <kevinbond@gmail.com>
*/
#[AsCommand(name: 'debug:scheduler', description: 'List schedules and their recurring messages')]
final class DebugCommand extends Command
{
private array $scheduleNames;

public function __construct(private ServiceProviderInterface $schedules)
{
$this->scheduleNames = array_keys($this->schedules->getProvidedServices());

parent::__construct();
}

protected function configure(): void
{
$this
->addArgument('schedule', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, sprintf('The schedule name (one of "%s")', implode('", "', $this->scheduleNames)), null, $this->scheduleNames)
->setHelp(<<<'EOF'
The <info>%command.name%</info> lists schedules and their recurring messages:
<info>php %command.full_name%</info>
Or for a specific schedule only:
<info>php %command.full_name% default</info>

EOF
)
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('Scheduler');

$names = $input->getArgument('schedule') ?: $this->scheduleNames;

foreach ($names as $name) {
/** @var ScheduleProviderInterface $schedule */
$schedule = $this->schedules->get($name);

$io->section($name);
$io->table(
['Message', 'Trigger', 'Next Run'],
array_map(self::renderRecurringMessage(...), $schedule->getSchedule()->getRecurringMessages())
);
}

return self::SUCCESS;
}

/**
* @return array{0:string,1:string,2:string}
*/
private static function renderRecurringMessage(RecurringMessage $recurringMessage): array
{
$message = $recurringMessage->getMessage();
$trigger = $recurringMessage->getTrigger();

return [
$message instanceof \Stringable ? (string) $message : (new \ReflectionClass($message))->getShortName(),
$trigger instanceof \Stringable ? (string) $trigger : (new \ReflectionClass($trigger))->getShortName(),
$recurringMessage->getTrigger()->getNextRunDate(now())->format(\DateTimeInterface::ATOM),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@
*
* @experimental
*/
final class CronExpressionTrigger implements TriggerInterface
final class CronExpressionTrigger implements TriggerInterface, \Stringable
{
public function __construct(
private readonly CronExpression $expression = new CronExpression('* * * * *'),
) {
}

public function __toString(): string
{
return "cron: {$this->expression->getExpression()}";
}

public static function fromSpec(string $expression = '* * * * *'): self
{
if (!class_exists(CronExpression::class)) {
Expand Down

0 comments on commit 50fbe01

Please sign in to comment.