feat: expose queue driver, schedule status (#3593)

* feat: expose queue driver, schedule status

* Apply fixes from StyleCI

* docblock

* fix inheritdoc

* Add info link for scheduler setup

* Remove unsed import

* fix: phpstan error

* Only show scheduler status when jobs are registered

* Apply fixes from StyleCI

* add ItemList priorities

* Add translations

* Include schedule status in info console cmd

* Apply fixes from StyleCI

* Move identifyX logic into shared 'ApplicationInfoProvider'

* Apply fixes from StyleCI

* uniform data layout

* inject queue

Co-authored-by: StyleCI Bot <bot@styleci.io>
This commit is contained in:
Ian Morland 2022-11-19 22:23:04 +00:00 committed by GitHub
parent e0b9dcfbcd
commit 47d2053766
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 316 additions and 105 deletions

View File

@ -5,6 +5,7 @@ import ItemList from '../../common/utils/ItemList';
import Dropdown from '../../common/components/Dropdown'; import Dropdown from '../../common/components/Dropdown';
import Button from '../../common/components/Button'; import Button from '../../common/components/Button';
import LoadingModal from './LoadingModal'; import LoadingModal from './LoadingModal';
import LinkButton from '../../common/components/LinkButton';
export default class StatusWidget extends DashboardWidget { export default class StatusWidget extends DashboardWidget {
className() { className() {
@ -30,9 +31,26 @@ export default class StatusWidget extends DashboardWidget {
</Dropdown> </Dropdown>
); );
items.add('version-flarum', [<strong>Flarum</strong>, <br />, app.forum.attribute('version')]); items.add('version-flarum', [<strong>Flarum</strong>, <br />, app.forum.attribute('version')], 100);
items.add('version-php', [<strong>PHP</strong>, <br />, app.data.phpVersion]); items.add('version-php', [<strong>PHP</strong>, <br />, app.data.phpVersion], 90);
items.add('version-mysql', [<strong>MySQL</strong>, <br />, app.data.mysqlVersion]); items.add('version-mysql', [<strong>MySQL</strong>, <br />, app.data.mysqlVersion], 80);
if (app.data.schedulerStatus) {
items.add(
'schedule-status',
[
<span>
<strong>Scheduler</strong>{' '}
<LinkButton href="https://discuss.flarum.org/d/24118" external={true} target="_blank" icon="fas fa-info-circle" />
</span>,
<br />,
app.data.schedulerStatus,
],
70
);
}
items.add('queue-driver', [<strong>Queue Driver</strong>, <br />, app.data.queueDriver], 60);
items.add('session-driver', [<strong>Session Driver</strong>, <br />, app.data.sessionDriver], 50);
return items; return items;
} }

View File

@ -56,6 +56,11 @@ core:
clear_cache_button: Clear Cache clear_cache_button: Clear Cache
description: Your forum at a glance. description: Your forum at a glance.
io_error_message: "Could not write to filesystem. Check your filesystem permissions and try again. Or try running from the command line." io_error_message: "Could not write to filesystem. Check your filesystem permissions and try again. Or try running from the command line."
status:
scheduler:
active: Active
inactive: Inactive
never-run: Never run
title: Dashboard title: Dashboard
tools_button: Tools tools_button: Tools

View File

@ -10,6 +10,7 @@
namespace Flarum\Admin\Content; namespace Flarum\Admin\Content;
use Flarum\Extension\ExtensionManager; use Flarum\Extension\ExtensionManager;
use Flarum\Foundation\ApplicationInfoProvider;
use Flarum\Foundation\Config; use Flarum\Foundation\Config;
use Flarum\Frontend\Document; use Flarum\Frontend\Document;
use Flarum\Group\Permission; use Flarum\Group\Permission;
@ -54,6 +55,11 @@ class AdminPayload
*/ */
protected $config; protected $config;
/**
* @var ApplicationInfoProvider
*/
protected $appInfo;
/** /**
* @param Container $container * @param Container $container
* @param SettingsRepositoryInterface $settings * @param SettingsRepositoryInterface $settings
@ -61,6 +67,7 @@ class AdminPayload
* @param ConnectionInterface $db * @param ConnectionInterface $db
* @param Dispatcher $events * @param Dispatcher $events
* @param Config $config * @param Config $config
* @param ApplicationInfoProvider $appInfo
*/ */
public function __construct( public function __construct(
Container $container, Container $container,
@ -68,7 +75,8 @@ class AdminPayload
ExtensionManager $extensions, ExtensionManager $extensions,
ConnectionInterface $db, ConnectionInterface $db,
Dispatcher $events, Dispatcher $events,
Config $config Config $config,
ApplicationInfoProvider $appInfo
) { ) {
$this->container = $container; $this->container = $container;
$this->settings = $settings; $this->settings = $settings;
@ -76,6 +84,7 @@ class AdminPayload
$this->db = $db; $this->db = $db;
$this->events = $events; $this->events = $events;
$this->config = $config; $this->config = $config;
$this->appInfo = $appInfo;
} }
public function __invoke(Document $document, Request $request) public function __invoke(Document $document, Request $request)
@ -95,10 +104,17 @@ class AdminPayload
return array_keys($resourceDrivers); return array_keys($resourceDrivers);
}, $this->container->make('flarum.http.slugDrivers')); }, $this->container->make('flarum.http.slugDrivers'));
$document->payload['phpVersion'] = PHP_VERSION; $document->payload['phpVersion'] = $this->appInfo->identifyPHPVersion();
$document->payload['mysqlVersion'] = $this->db->selectOne('select version() as version')->version; $document->payload['mysqlVersion'] = $this->appInfo->identifyDatabaseVersion();
$document->payload['debugEnabled'] = Arr::get($this->config, 'debug'); $document->payload['debugEnabled'] = Arr::get($this->config, 'debug');
if ($this->appInfo->scheduledTasksRegistered()) {
$document->payload['schedulerStatus'] = $this->appInfo->getSchedulerStatus();
}
$document->payload['queueDriver'] = $this->appInfo->identifyQueueDriver();
$document->payload['sessionDriver'] = $this->appInfo->identifySessionDriver();
/** /**
* Used in the admin user list. Implemented as this as it matches the API in flarum/statistics. * Used in the admin user list. Implemented as this as it matches the API in flarum/statistics.
* If flarum/statistics ext is enabled, it will override this data with its own stats. * If flarum/statistics ext is enabled, it will override this data with its own stats.

View File

@ -16,12 +16,12 @@ use Flarum\Foundation\AbstractServiceProvider;
use Flarum\Foundation\Console\AssetsPublishCommand; use Flarum\Foundation\Console\AssetsPublishCommand;
use Flarum\Foundation\Console\CacheClearCommand; use Flarum\Foundation\Console\CacheClearCommand;
use Flarum\Foundation\Console\InfoCommand; use Flarum\Foundation\Console\InfoCommand;
use Flarum\Foundation\Console\ScheduleRunCommand;
use Illuminate\Console\Scheduling\CacheEventMutex; use Illuminate\Console\Scheduling\CacheEventMutex;
use Illuminate\Console\Scheduling\CacheSchedulingMutex; use Illuminate\Console\Scheduling\CacheSchedulingMutex;
use Illuminate\Console\Scheduling\EventMutex; use Illuminate\Console\Scheduling\EventMutex;
use Illuminate\Console\Scheduling\Schedule as LaravelSchedule; use Illuminate\Console\Scheduling\Schedule as LaravelSchedule;
use Illuminate\Console\Scheduling\ScheduleListCommand; use Illuminate\Console\Scheduling\ScheduleListCommand;
use Illuminate\Console\Scheduling\ScheduleRunCommand;
use Illuminate\Console\Scheduling\SchedulingMutex; use Illuminate\Console\Scheduling\SchedulingMutex;
use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Container\Container;

View File

@ -0,0 +1,212 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Foundation;
use Carbon\Carbon;
use Flarum\Locale\Translator;
use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\SessionManager;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Contracts\Queue\Queue;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
use SessionHandlerInterface;
class ApplicationInfoProvider
{
/**
* @var SettingsRepositoryInterface
*/
protected $settings;
/**
* @var Translator
*/
protected $translator;
/**
* @var Schedule
*/
protected $schedule;
/**
* @var ConnectionInterface
*/
protected $db;
/**
* @var Config
*/
protected $config;
/**
* @var SessionManager
*/
protected $session;
/**
* @var SessionHandlerInterface
*/
protected $sessionHandler;
/**
* @var Queue
*/
protected $queue;
/**
* @param SettingsRepositoryInterface $settings
* @param Translator $translator
* @param Schedule $schedule
* @param ConnectionInterface $db
* @param Config $config
* @param SessionManager $session
* @param SessionHandlerInterface $sessionHandler
* @param Queue $queue
*/
public function __construct(
SettingsRepositoryInterface $settings,
Translator $translator,
Schedule $schedule,
ConnectionInterface $db,
Config $config,
SessionManager $session,
SessionHandlerInterface $sessionHandler,
Queue $queue
) {
$this->settings = $settings;
$this->translator = $translator;
$this->schedule = $schedule;
$this->db = $db;
$this->config = $config;
$this->session = $session;
$this->sessionHandler = $sessionHandler;
$this->queue = $queue;
}
/**
* Identify if any tasks are registered with the scheduler.
*
* @return bool
*/
public function scheduledTasksRegistered(): bool
{
return count($this->schedule->events()) > 0;
}
/**
* Gets the current status of the scheduler.
*
* @return string
*/
public function getSchedulerStatus(): string
{
$status = $this->settings->get('schedule.last_run');
if (! $status) {
return $this->translator->trans('core.admin.dashboard.status.scheduler.never-run');
}
// If the schedule has not run in the last 5 minutes, mark it as inactive.
return Carbon::parse($status) > Carbon::now()->subMinutes(5)
? $this->translator->trans('core.admin.dashboard.status.scheduler.active')
: $this->translator->trans('core.admin.status.scheduler.inactive');
}
/**
* Identify the queue driver in use.
*
* @return string
*/
public function identifyQueueDriver(): string
{
// Get class name
$queue = get_class($this->queue);
// Drop the namespace
$queue = Str::afterLast($queue, '\\');
// Lowercase the class name
$queue = strtolower($queue);
// Drop everything like queue SyncQueue, RedisQueue
$queue = str_replace('queue', '', $queue);
return $queue;
}
/**
* Identify the version of the database we are connected to.
*
* @return string
*/
public function identifyDatabaseVersion(): string
{
return $this->db->selectOne('select version() as version')->version;
}
/**
* Reports on the session driver in use based on three scenarios:
* 1. If the configured session driver is valid and in use, it will be returned.
* 2. If the configured session driver is invalid, fallback to the default one and mention it.
* 3. If the actual used driver (i.e `session.handler`) is different from the current one (configured or default), mention it.
*/
public function identifySessionDriver(): string
{
/*
* Get the configured driver and fallback to the default one.
*/
$defaultDriver = $this->session->getDefaultDriver();
$configuredDriver = Arr::get($this->config, 'session.driver', $defaultDriver);
$driver = $configuredDriver;
try {
// Try to get the configured driver instance.
// Driver instances are created on demand.
$this->session->driver($configuredDriver);
} catch (InvalidArgumentException $e) {
// An exception is thrown if the configured driver is not a valid driver.
// So we fallback to the default driver.
$driver = $defaultDriver;
}
/*
* Get actual driver name from its class name.
* And compare that to the current configured driver.
*/
// Get class name
$handlerName = get_class($this->sessionHandler);
// Drop the namespace
$handlerName = Str::afterLast($handlerName, '\\');
// Lowercase the class name
$handlerName = strtolower($handlerName);
// Drop everything like sessionhandler FileSessionHandler, DatabaseSessionHandler ..etc
$handlerName = str_replace('sessionhandler', '', $handlerName);
if ($driver !== $handlerName) {
return "$handlerName <comment>(Code override. Configured to <options=bold,underscore>$configuredDriver</>)</comment>";
}
if ($driver !== $configuredDriver) {
return "$driver <comment>(Fallback default driver. Configured to invalid driver <options=bold,underscore>$configuredDriver</>)</comment>";
}
return $driver;
}
/**
* Identifiy the current PHP version.
*
* @return string
*/
public function identifyPHPVersion(): string
{
return PHP_VERSION;
}
}

View File

@ -12,16 +12,10 @@ namespace Flarum\Foundation\Console;
use Flarum\Console\AbstractCommand; use Flarum\Console\AbstractCommand;
use Flarum\Extension\ExtensionManager; use Flarum\Extension\ExtensionManager;
use Flarum\Foundation\Application; use Flarum\Foundation\Application;
use Flarum\Foundation\ApplicationInfoProvider;
use Flarum\Foundation\Config; use Flarum\Foundation\Config;
use Flarum\Settings\SettingsRepositoryInterface; use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\SessionManager;
use Illuminate\Contracts\Queue\Queue;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
use PDO;
use SessionHandlerInterface;
use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Helper\TableStyle; use Symfony\Component\Console\Helper\TableStyle;
@ -48,36 +42,22 @@ class InfoCommand extends AbstractCommand
protected $db; protected $db;
/** /**
* @var Queue * @var ApplicationInfoProvider
*/ */
private $queue; private $appInfo;
/**
* @var SessionManager
*/
private $session;
/**
* @var SessionHandlerInterface
*/
private $sessionHandler;
public function __construct( public function __construct(
ExtensionManager $extensions, ExtensionManager $extensions,
Config $config, Config $config,
SettingsRepositoryInterface $settings, SettingsRepositoryInterface $settings,
ConnectionInterface $db, ConnectionInterface $db,
Queue $queue, ApplicationInfoProvider $appInfo
SessionManager $session,
SessionHandlerInterface $sessionHandler
) { ) {
$this->extensions = $extensions; $this->extensions = $extensions;
$this->config = $config; $this->config = $config;
$this->settings = $settings; $this->settings = $settings;
$this->db = $db; $this->db = $db;
$this->queue = $queue; $this->appInfo = $appInfo;
$this->session = $session;
$this->sessionHandler = $sessionHandler;
parent::__construct(); parent::__construct();
} }
@ -98,10 +78,10 @@ class InfoCommand extends AbstractCommand
protected function fire() protected function fire()
{ {
$coreVersion = $this->findPackageVersion(__DIR__.'/../../../', Application::VERSION); $coreVersion = $this->findPackageVersion(__DIR__.'/../../../', Application::VERSION);
$this->output->writeln("<info>Flarum core $coreVersion</info>"); $this->output->writeln("<info>Flarum core:</info> $coreVersion");
$this->output->writeln('<info>PHP version:</info> '.PHP_VERSION); $this->output->writeln('<info>PHP version:</info> '.$this->appInfo->identifyPHPVersion());
$this->output->writeln('<info>MySQL version:</info> '.$this->identifyDatabaseVersion()); $this->output->writeln('<info>MySQL version:</info> '.$this->appInfo->identifyDatabaseVersion());
$phpExtensions = implode(', ', get_loaded_extensions()); $phpExtensions = implode(', ', get_loaded_extensions());
$this->output->writeln("<info>Loaded extensions:</info> $phpExtensions"); $this->output->writeln("<info>Loaded extensions:</info> $phpExtensions");
@ -110,8 +90,13 @@ class InfoCommand extends AbstractCommand
$this->output->writeln('<info>Base URL:</info> '.$this->config->url()); $this->output->writeln('<info>Base URL:</info> '.$this->config->url());
$this->output->writeln('<info>Installation path:</info> '.getcwd()); $this->output->writeln('<info>Installation path:</info> '.getcwd());
$this->output->writeln('<info>Queue driver:</info> '.$this->identifyQueueDriver()); $this->output->writeln('<info>Queue driver:</info> '.$this->appInfo->identifyQueueDriver());
$this->output->writeln('<info>Session driver:</info> '.$this->identifySessionDriver()); $this->output->writeln('<info>Session driver:</info> '.$this->appInfo->identifySessionDriver());
if ($this->appInfo->scheduledTasksRegistered()) {
$this->output->writeln('<info>Scheduler status:</info> '.$this->appInfo->getSchedulerStatus());
}
$this->output->writeln('<info>Mail driver:</info> '.$this->settings->get('mail_driver', 'unknown')); $this->output->writeln('<info>Mail driver:</info> '.$this->settings->get('mail_driver', 'unknown'));
$this->output->writeln('<info>Debug mode:</info> '.($this->config->inDebugMode() ? '<error>ON</error>' : 'off')); $this->output->writeln('<info>Debug mode:</info> '.($this->config->inDebugMode() ? '<error>ON</error>' : 'off'));
@ -169,72 +154,4 @@ class InfoCommand extends AbstractCommand
return $fallback; return $fallback;
} }
private function identifyQueueDriver(): string
{
// Get class name
$queue = get_class($this->queue);
// Drop the namespace
$queue = Str::afterLast($queue, '\\');
// Lowercase the class name
$queue = strtolower($queue);
// Drop everything like queue SyncQueue, RedisQueue
$queue = str_replace('queue', '', $queue);
return $queue;
}
private function identifyDatabaseVersion(): string
{
return $this->db->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
}
/**
* Reports on the session driver in use based on three scenarios:
* 1. If the configured session driver is valid and in use, it will be returned.
* 2. If the configured session driver is invalid, fallback to the default one and mention it.
* 3. If the actual used driver (i.e `session.handler`) is different from the current one (configured or default), mention it.
*/
private function identifySessionDriver(): string
{
/*
* Get the configured driver and fallback to the default one.
*/
$defaultDriver = $this->session->getDefaultDriver();
$configuredDriver = Arr::get($this->config, 'session.driver', $defaultDriver);
$driver = $configuredDriver;
try {
// Try to get the configured driver instance.
// Driver instances are created on demand.
$this->session->driver($configuredDriver);
} catch (InvalidArgumentException $e) {
// An exception is thrown if the configured driver is not a valid driver.
// So we fallback to the default driver.
$driver = $defaultDriver;
}
/*
* Get actual driver name from its class name.
* And compare that to the current configured driver.
*/
// Get class name
$handlerName = get_class($this->sessionHandler);
// Drop the namespace
$handlerName = Str::afterLast($handlerName, '\\');
// Lowercase the class name
$handlerName = strtolower($handlerName);
// Drop everything like sessionhandler FileSessionHandler, DatabaseSessionHandler ..etc
$handlerName = str_replace('sessionhandler', '', $handlerName);
if ($driver !== $handlerName) {
return "$handlerName <comment>(Code override. Configured to <options=bold,underscore>$configuredDriver</>)</comment>";
}
if ($driver !== $configuredDriver) {
return "$driver <comment>(Fallback default driver. Configured to invalid driver <options=bold,underscore>$configuredDriver</>)</comment>";
}
return $driver;
}
} }

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Foundation\Console;
use Flarum\Settings\SettingsRepositoryInterface;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Events\Dispatcher;
class ScheduleRunCommand extends \Illuminate\Console\Scheduling\ScheduleRunCommand
{
/**
* @var SettingsRepositoryInterface
*/
protected $settings;
/**
* {@inheritdoc}
*/
public function __construct(SettingsRepositoryInterface $settings)
{
parent::__construct();
$this->settings = $settings;
}
/**
* {@inheritdoc}
*/
public function handle(Schedule $schedule, Dispatcher $dispatcher, ExceptionHandler $handler)
{
parent::handle($schedule, $dispatcher, $handler);
$this->settings->set('schedule.last_run', $this->startedAt);
}
}