diff --git a/administrator/components/com_scheduler/src/Extension/SchedulerComponent.php b/administrator/components/com_scheduler/src/Extension/SchedulerComponent.php index 2961a7e00371c..25d8c61febef0 100644 --- a/administrator/components/com_scheduler/src/Extension/SchedulerComponent.php +++ b/administrator/components/com_scheduler/src/Extension/SchedulerComponent.php @@ -23,7 +23,6 @@ * Component class for com_scheduler. * * @since 4.1.0 - * @todo Set up logger(s) here. */ class SchedulerComponent extends MVCComponent implements BootableExtensionInterface { diff --git a/administrator/components/com_scheduler/src/Scheduler/Scheduler.php b/administrator/components/com_scheduler/src/Scheduler/Scheduler.php index 58c646cffa93b..201f702ec1284 100644 --- a/administrator/components/com_scheduler/src/Scheduler/Scheduler.php +++ b/administrator/components/com_scheduler/src/Scheduler/Scheduler.php @@ -13,6 +13,7 @@ use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; +use Joomla\CMS\Log\DelegatingPsrLogger; use Joomla\CMS\Log\Log; use Joomla\CMS\User\User; use Joomla\Component\Scheduler\Administrator\Extension\SchedulerComponent; @@ -20,6 +21,7 @@ use Joomla\Component\Scheduler\Administrator\Model\TasksModel; use Joomla\Component\Scheduler\Administrator\Task\Status; use Joomla\Component\Scheduler\Administrator\Task\Task; +use Psr\Log\LoggerInterface; use Symfony\Component\OptionsResolver\Exception\AccessException; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; @@ -123,14 +125,18 @@ public function runTask(array $options): ?Task $app->getLanguage()->load('com_scheduler', JPATH_ADMINISTRATOR); - $options['text_entry_format'] = "{DATE}\t{TIME}\t{PRIORITY}\t{MESSAGE}"; - $options['text_file'] = 'joomla_scheduler.php'; - Log::addLogger($options, Log::ALL, $task->logCategory); + $loggerOptions = [ + 'text_entry_format' => '{DATE} {TIME} {PRIORITY} {MESSAGE}', + 'text_file' => 'joomla_scheduler.php' + ]; + + /** ! No non-static methods to add a sub-logger to {@see DelegatingPsrLogger} (Task::logger) */ + Log::addLogger($loggerOptions, Log::ALL, $task->logCategory); $taskId = $task->get('id'); $taskTitle = $task->get('title'); - $task->log(Text::sprintf('COM_SCHEDULER_SCHEDULER_TASK_START', $taskId, $taskTitle), 'info'); + $task->log(Text::sprintf('COM_SCHEDULER_SCHEDULER_TASK_START', $taskId, $taskTitle)); // Let's try to avoid time-outs if (\function_exists('set_time_limit')) { @@ -234,7 +240,7 @@ public function getTask(array $options = []): ?Task return null; } - return new Task($task); + return new Task($task, Factory::getContainer()->get(LoggerInterface::class)); } /** diff --git a/administrator/components/com_scheduler/src/Task/Task.php b/administrator/components/com_scheduler/src/Task/Task.php index ba779a5d00893..4f3c182de9171 100644 --- a/administrator/components/com_scheduler/src/Task/Task.php +++ b/administrator/components/com_scheduler/src/Task/Task.php @@ -15,6 +15,7 @@ use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; +use Joomla\CMS\Log\DelegatingPsrLogger; use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\PluginHelper; use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; @@ -29,6 +30,7 @@ use Psr\Log\InvalidArgumentException; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; +use Psr\Log\LoggerInterface; // phpcs:disable PSR1.Files.SideEffects \defined('_JEXEC') or die; @@ -126,12 +128,13 @@ class Task implements LoggerAwareInterface /** * Constructor for {@see Task}. * - * @param object $record A task from {@see TaskTable}. + * @param object $record A task from {@see TaskTable}. + * @param LoggerInterface $logger A logger instance for the Task's logs. * * @since 4.1.0 * @throws \Exception */ - public function __construct(object $record) + public function __construct(object $record, LoggerInterface $logger) { // Workaround because Registry dumps private properties otherwise. $taskOption = $record->taskOption; @@ -142,16 +145,22 @@ public function __construct(object $record) $this->set('taskOption', $taskOption); $this->app = Factory::getApplication(); $this->db = Factory::getContainer()->get(DatabaseInterface::class); - $this->setLogger(Log::createDelegatedLogger()); + $this->setLogger($logger); $this->logCategory = 'task' . $this->get('id'); + // ! We use an "injected" logger but still need a static {@see Log} call to set up sub-loggers :/ if ($this->get('params.individual_log')) { $logFile = $this->get('params.log_file') ?? 'task_' . $this->get('id') . '.log.php'; $options['text_entry_format'] = "{DATE}\t{TIME}\t{PRIORITY}\t{MESSAGE}"; $options['text_file'] = $logFile; + + /** No non-static methods on {@see DelegatingPsrLogger} to set up sub-loggers... */ Log::addLogger($options, Log::ALL, [$this->logCategory]); } + + // We need `com_scheduler` language strings for logging + $this->app->getLanguage()->load('com_scheduler', JPATH_ADMINISTRATOR); } /** @@ -219,6 +228,7 @@ public function run(): bool 'routineId' => $this->get('type'), 'langConstPrefix' => $this->get('taskOption')->langConstPrefix, 'params' => $this->get('params'), + 'logger' => [$this, 'log'], ] ); @@ -409,6 +419,8 @@ public function releaseLock(bool $update = true): bool } /** + * Log a message with the task logger. + * * @param string $message Log message * @param string $priority Log level, defaults to 'info' * diff --git a/administrator/components/com_scheduler/src/Traits/TaskPluginTrait.php b/administrator/components/com_scheduler/src/Traits/TaskPluginTrait.php index 57a545cc7f7f4..3102bb1adb66a 100644 --- a/administrator/components/com_scheduler/src/Traits/TaskPluginTrait.php +++ b/administrator/components/com_scheduler/src/Traits/TaskPluginTrait.php @@ -13,8 +13,6 @@ use Joomla\CMS\Event\Model; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; -use Joomla\CMS\Language\Text; -use Joomla\CMS\Log\Log; use Joomla\CMS\Plugin\CMSPlugin; use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; use Joomla\Component\Scheduler\Administrator\Task\Status; @@ -48,7 +46,17 @@ trait TaskPluginTrait protected $snapshot = []; /** - * Set information to {@see $snapshot} when initializing a routine. + * The task logger. We expect this logger from {@see ExecuteTaskEvent}, setting it up in {@see startRoutine}. + * + * @var callable {@signature (string $message, string $level = 'info'): void} + * @since __DEPLOY__VERSION__ + * @todo update doc once phpDocumentor adds callable signature support: + * {@link https://github.com/phpDocumentor/phpDocumentor/issues/1712}. + */ + protected $logger; + + /** + * Setup injected dependencies and {@see $snapshot} on routine initialisation. * * @param ExecuteTaskEvent $event The onExecuteTask event. * @@ -62,6 +70,8 @@ protected function startRoutine(ExecuteTaskEvent $event): void return; } + $this->logger = $event->getArgument('logger'); + $this->snapshot['logCategory'] = $event->getArgument('subject')->logCategory; $this->snapshot['plugin'] = $this->_name; $this->snapshot['startTime'] = microtime(true); @@ -222,29 +232,15 @@ protected function getRoutineId(Form $form, $data): string * @return void * * @since 4.1.0 - * @throws \Exception - * @todo : use dependency injection here (starting from the Task & Scheduler classes). + * @throws \LogicException|\InvalidArgumentException */ protected function logTask(string $message, string $priority = 'info'): void { - static $langLoaded; - static $priorityMap = [ - 'debug' => Log::DEBUG, - 'error' => Log::ERROR, - 'info' => Log::INFO, - 'notice' => Log::NOTICE, - 'warning' => Log::WARNING, - ]; - - if (!$langLoaded) { - $app = $this->getApplication() ?? ($this->app ?? Factory::getApplication()); - $app->getLanguage()->load('com_scheduler', JPATH_ADMINISTRATOR); - $langLoaded = true; + if (empty($this->logger)) + throw new \LogicException("Logger has not been setup!"); } - $category = $this->snapshot['logCategory']; - - Log::add(Text::_('COM_SCHEDULER_ROUTINE_LOG_PREFIX') . $message, $priorityMap[$priority] ?? Log::INFO, $category); + ($this->logger)($message, $priority); } /**