From 8f8226bcfaeb209010f7c47309cd106d266c58d6 Mon Sep 17 00:00:00 2001 From: Tom Whiston Date: Sun, 11 Dec 2016 17:14:59 +0100 Subject: [PATCH] update base class, refactor debug and poll commands --- src/Command/Database/DatabaseLogBase.php | 276 +++++++++++++++ src/Command/Database/LogDebugCommand.php | 417 ++++++++--------------- src/Command/Database/LogPollCommand.php | 95 ++++++ 3 files changed, 514 insertions(+), 274 deletions(-) create mode 100644 src/Command/Database/DatabaseLogBase.php create mode 100644 src/Command/Database/LogPollCommand.php diff --git a/src/Command/Database/DatabaseLogBase.php b/src/Command/Database/DatabaseLogBase.php new file mode 100644 index 000000000..87bacc5b6 --- /dev/null +++ b/src/Command/Database/DatabaseLogBase.php @@ -0,0 +1,276 @@ +database = $database; + $this->dateFormatter = $dateFormatter; + $this->entityTypeManager = $entityTypeManager; + $this->stringTranslation = $stringTranslation; + $this->userStorage = $this->entityTypeManager->getStorage('user'); + $this->severityList = RfcLogLevel::getLevels(); + parent::__construct(); + } + + /** + * + */ + protected function addDefaultLoggingOptions() { + + $this + ->addOption( + 'type', + '', + InputOption::VALUE_OPTIONAL, + $this->trans('commands.database.log.common.options.type') + ) + ->addOption( + 'severity', + '', + InputOption::VALUE_OPTIONAL, + $this->trans('commands.database.log.common.options.severity') + ) + ->addOption( + 'user-id', + '', + InputOption::VALUE_OPTIONAL, + $this->trans('commands.database.log.common.options.user-id') + ); + + } + + /** + * @param \Symfony\Component\Console\Input\InputInterface $input + */ + protected function getDefaultOptions(InputInterface $input) { + + $this->eventType = $input->getOption('type'); + $this->eventSeverity = $input->getOption('severity'); + $this->userId = $input->getOption('user-id'); + + } + + /** + * @param \Drupal\Console\Style\DrupalStyle $io + * @param null $offset + * @param int $range + * @return bool|\Drupal\Core\Database\Query\SelectInterface + */ + protected function makeQuery(DrupalStyle $io, $offset = NULL, $range = 1000) { + + $query = $this->database->select('watchdog', 'w'); + $query->fields( + 'w', + [ + 'wid', + 'uid', + 'severity', + 'type', + 'timestamp', + 'message', + 'variables', + ] + ); + + if ($this->eventType) { + $query->condition('type', $this->eventType); + } + + if ($this->eventSeverity) { + if (!in_array($this->eventSeverity, $this->severityList)) { + $io->error( + sprintf( + $this->trans('database.log.common.messages.invalid-severity'), + $this->eventSeverity + ) + ); + return FALSE; + } + $query->condition('severity', + array_search($this->eventSeverity, + $this->severityList)); + } + + if ($this->userId) { + $query->condition('uid', $this->userId); + } + + $query->orderBy('wid', 'ASC'); + + if ($offset) { + $query->range($offset, $range); + } + + return $query; + + } + + /** + * Generic logging table header + * + * @return array + */ + protected function createTableHeader() { + return [ + $this->trans('commands.database.log.common.messages.event-id'), + $this->trans('commands.database.log.common.messages.type'), + $this->trans('commands.database.log.common.messages.date'), + $this->trans('commands.database.log.common.messages.message'), + $this->trans('commands.database.log.common.messages.user'), + $this->trans('commands.database.log.common.messages.severity'), + ]; + } + + + /** + * @param \stdClass $dblog + * @return array + */ + protected function createTableRow(\stdClass $dblog) { + + /** @var User $user */ + $user = $this->userStorage->load($dblog->uid); + + return [ + $dblog->wid, + $dblog->type, + $this->dateFormatter->format($dblog->timestamp, 'short'), + Unicode::truncate(Html::decodeEntities(strip_tags($this->formatMessage($dblog))), + 500, + TRUE, + TRUE), + $user->getUsername() . ' (' . $user->id() . ')', + $this->severityList[$dblog->severity]->render(), + ]; + } + + /** + * Formats a database log message. + * + * @param $event + * The record from the watchdog table. The object properties are: wid, uid, + * severity, type, timestamp, message, variables, link, name. + * + * @return string|false + * The formatted log message or FALSE if the message or variables properties + * are not set. + */ + protected function formatMessage(\stdClass $event) { + $message = FALSE; + + // Check for required properties. + if (isset($event->message, $event->variables)) { + // Messages without variables or user specified text. + if ($event->variables === 'N;') { + return $event->message; + } + + return $this->stringTranslation->translate( + $event->message, + unserialize($event->variables) + ); + } + + return $message; + } + + /** + * @param $dblog + * @return array + */ + protected function formatSingle($dblog) { + return array_combine($this->createTableHeader(), + $this->createTableRow($dblog)); + } + +} \ No newline at end of file diff --git a/src/Command/Database/LogDebugCommand.php b/src/Command/Database/LogDebugCommand.php index dc02baacf..a643eb7d4 100644 --- a/src/Command/Database/LogDebugCommand.php +++ b/src/Command/Database/LogDebugCommand.php @@ -11,295 +11,164 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Command\Command; -use Drupal\Core\StringTranslation\Translator\TranslatorInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Datetime\DateFormatterInterface; -use Drupal\Core\Database\Connection; -use Drupal\Component\Utility\Unicode; use Drupal\Component\Serialization\Yaml; -use Drupal\Component\Utility\Html; -use Drupal\Core\Logger\RfcLogLevel; -use Drupal\Console\Command\Shared\CommandTrait; use Drupal\Console\Style\DrupalStyle; -class LogDebugCommand extends Command -{ - use CommandTrait; - - /** - * @var Connection - */ - protected $database; - - /** - * @var DateFormatterInterface - */ - protected $dateFormatter; - - /** - * @var EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * @var TranslatorInterface - */ - protected $stringTranslation; - - /** - * LogDebugCommand constructor. - * @param Connection $database - * @param DateFormatterInterface $dateFormatter - * @param EntityTypeManagerInterface $entityTypeManager - * @param TranslatorInterface $stringTranslation - */ - public function __construct( - Connection $database, - DateFormatterInterface $dateFormatter, - EntityTypeManagerInterface $entityTypeManager, - TranslatorInterface $stringTranslation - ) { - $this->database = $database; - $this->dateFormatter = $dateFormatter; - $this->entityTypeManager = $entityTypeManager; - $this->stringTranslation = $stringTranslation; - parent::__construct(); +/** + * Class LogDebugCommand + * + * @package Drupal\Console\Command\Database + */ +class LogDebugCommand extends DatabaseLogBase { + + /** + * @var + */ + protected $eventId; + + /** + * @var + */ + protected $asc; + /** + * @var + */ + protected $limit; + /** + * @var + */ + protected $offset; + + /** + * Print in yml style if true + * + * @var bool + */ + protected $ymlStyle; + + /** + * {@inheritdoc} + */ + protected function configure() { + $this + ->setName('database:log:debug') + ->setDescription($this->trans('commands.database.log.debug.description')); + + $this->addDefaultLoggingOptions(); + + $this + ->addArgument( + 'event-id', + InputArgument::OPTIONAL, + $this->trans('commands.database.log.debug.arguments.event-id') + ) + ->addOption( + 'asc', + FALSE, + InputOption::VALUE_NONE, + $this->trans('commands.database.log.debug.options.asc') + ) + ->addOption( + 'limit', + NULL, + InputOption::VALUE_OPTIONAL, + $this->trans('commands.database.log.debug.options.limit') + ) + ->addOption( + 'offset', + NULL, + InputOption::VALUE_OPTIONAL, + $this->trans('commands.database.log.debug.options.offset'), + 0 + ) + ->addOption( + 'yml', + NULL, + InputOption::VALUE_NONE, + $this->trans('commands.database.log.debug.options.yml'), + NULL + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) { + + $io = new DrupalStyle($input, $output); + + $this->getDefaultOptions($input); + $this->eventId = $input->getArgument('event-id'); + $this->asc = $input->getOption('asc'); + $this->limit = $input->getOption('limit'); + $this->offset = $input->getOption('offset'); + $this->ymlStyle = $input->getOption('yml'); + + + if ($this->eventId) { + return $this->getEventDetails($io); + } else { + return $this->getAllEvents($io); } - - /** - * {@inheritdoc} - */ - protected function configure() - { - $this - ->setName('database:log:debug') - ->setDescription($this->trans('commands.database.log.debug.description')) - ->addArgument( - 'event-id', - InputArgument::OPTIONAL, - $this->trans('commands.database.log.debug.arguments.event-id') - ) - ->addOption( - 'type', - '', - InputOption::VALUE_OPTIONAL, - $this->trans('commands.database.log.debug.options.type') - ) - ->addOption( - 'severity', - '', - InputOption::VALUE_OPTIONAL, - $this->trans('commands.database.log.debug.options.severity') - ) - ->addOption( - 'user-id', - '', - InputOption::VALUE_OPTIONAL, - $this->trans('commands.database.log.debug.options.user-id') - ) - ->addOption( - 'asc', - false, - InputOption::VALUE_NONE, - $this->trans('commands.database.log.debug.options.asc') - ) - ->addOption( - 'limit', - null, - InputOption::VALUE_OPTIONAL, - $this->trans('commands.database.log.debug.options.limit') - ) - ->addOption( - 'offset', - null, - InputOption::VALUE_OPTIONAL, - $this->trans('commands.database.log.debug.options.offset'), - 0 - ); - ; + } + + /** + * @param $io + * @param $eventId + * @return bool + */ + private function getEventDetails(DrupalStyle $io) { + + $dblog = $this->database + ->query( + 'SELECT w.*, u.uid FROM {watchdog} w LEFT JOIN {users} u ON u.uid = w.uid WHERE w.wid = :id', + [':id' => $this->eventId] + ) + ->fetchObject(); + + if (!$dblog) { + $io->error( + sprintf( + $this->trans('commands.database.log.debug.messages.not-found'), + $this->eventId + ) + ); + return 1; } - /** - * {@inheritdoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $io = new DrupalStyle($input, $output); - - $eventId = $input->getArgument('event-id'); - $eventType = $input->getOption('type'); - $eventSeverity = $input->getOption('severity'); - $userId = $input->getOption('user-id'); - $asc = $input->getOption('asc'); - $limit = $input->getOption('limit'); - $offset = $input->getOption('offset'); - - if ($eventId) { - $this->getEventDetails($io, $eventId); - } else { - $this->getAllEvents($io, $eventType, $eventSeverity, $userId, $asc, $offset, $limit); - } - - return 0; + if ($this->ymlStyle) { + $io->writeln(Yaml::encode($this->formatSingle($dblog))); + } else { + $io->table( + $this->createTableHeader(), + [$this->createTableRow($dblog)] + ); } - /** - * @param $io - * @param $eventId - * @return bool - */ - private function getEventDetails(DrupalStyle $io, $eventId) - { - $userStorage = $this->entityTypeManager->getStorage('user'); - $severity = RfcLogLevel::getLevels(); - - $dblog = $this->database - ->query( - 'SELECT w.*, u.uid FROM {watchdog} w LEFT JOIN {users} u ON u.uid = w.uid WHERE w.wid = :id', - [':id' => $eventId] - ) - ->fetchObject(); - if (!$dblog) { - $io->error( - sprintf( - $this->trans('commands.database.log.debug.messages.not-found'), - $eventId - ) - ); + } - return false; - } + /** + * @param \Drupal\Console\Style\DrupalStyle $io + * @return bool + */ + private function getAllEvents(DrupalStyle $io) { - $user = $userStorage->load($dblog->uid); + $query = $this->makeQuery($io); - $configuration = [ - $this->trans('commands.database.log.debug.messages.event-id') => $eventId, - $this->trans('commands.database.log.debug.messages.type') => $dblog->type, - $this->trans('commands.database.log.debug.messages.date') => $this->dateFormatter->format($dblog->timestamp, 'short'), - $this->trans('commands.database.log.debug.messages.user') => $user->getUsername() . ' (' . $user->id() .')', - $this->trans('commands.database.log.debug.messages.severity') => (string) $severity[$dblog->severity], - $this->trans('commands.database.log.debug.messages.message') => Html::decodeEntities(strip_tags($this->formatMessage($dblog))) - ]; + $result = $query->execute(); - $io->writeln(Yaml::encode($configuration)); - - return true; + $tableRows = []; + foreach ($result as $dblog) { + $tableRows[] = $this->createTableRow($dblog); } - private function getAllEvents(DrupalStyle $io, $eventType, $eventSeverity, $userId, $asc, $offset, $limit) - { - $userStorage = $this->entityTypeManager->getStorage('user'); - $severity = RfcLogLevel::getLevels(); - - $query = $this->database->select('watchdog', 'w'); - $query->fields( - 'w', - [ - 'wid', - 'uid', - 'severity', - 'type', - 'timestamp', - 'message', - 'variables', - ] - ); - - if ($eventType) { - $query->condition('type', $eventType); - } - - if ($eventSeverity) { - if (!in_array($eventSeverity, $severity)) { - $io->error( - sprintf( - $this->trans('commands.database.log.debug.messages.invalid-severity'), - $eventSeverity - ) - ); + $io->table( + $this->createTableHeader(), + $tableRows + ); - return false; - } + return TRUE; + } - $query->condition('severity', array_search($eventSeverity, $severity)); - } - - if ($userId) { - $query->condition('uid', $userId); - } - - if ($asc) { - $query->orderBy('wid', 'ASC'); - } else { - $query->orderBy('wid', 'DESC'); - } - - if ($limit) { - $query->range($offset, $limit); - } - - $result = $query->execute(); - - $tableHeader = [ - $this->trans('commands.database.log.debug.messages.event-id'), - $this->trans('commands.database.log.debug.messages.type'), - $this->trans('commands.database.log.debug.messages.date'), - $this->trans('commands.database.log.debug.messages.message'), - $this->trans('commands.database.log.debug.messages.user'), - $this->trans('commands.database.log.debug.messages.severity'), - ]; - - $tableRows = []; - foreach ($result as $dblog) { - $user= $userStorage->load($dblog->uid); - - $tableRows[] = [ - $dblog->wid, - $dblog->type, - $this->dateFormatter->format($dblog->timestamp, 'short'), - Unicode::truncate(Html::decodeEntities(strip_tags($this->formatMessage($dblog))), 56, true, true), - $user->getUsername() . ' (' . $user->id() .')', - $severity[$dblog->severity] - ]; - } - - $io->table( - $tableHeader, - $tableRows - ); - - return true; - } - - /** - * Formats a database log message. - * - * @param $event - * The record from the watchdog table. The object properties are: wid, uid, - * severity, type, timestamp, message, variables, link, name. - * - * @return string|false - * The formatted log message or FALSE if the message or variables properties - * are not set. - */ - private function formatMessage($event) - { - $message = false; - - // Check for required properties. - if (isset($event->message) && isset($event->variables)) { - // Messages without variables or user specified text. - if ($event->variables === 'N;') { - return $event->message; - } - - return $this->stringTranslation->translate( - $event->message, unserialize($event->variables) - ); - } - - return $message; - } } diff --git a/src/Command/Database/LogPollCommand.php b/src/Command/Database/LogPollCommand.php new file mode 100644 index 000000000..8e80e020e --- /dev/null +++ b/src/Command/Database/LogPollCommand.php @@ -0,0 +1,95 @@ +setName('database:log:poll') + ->setDescription($this->trans('commands.database.log.poll.description')); + + $this->addDefaultLoggingOptions(); + + $this->addArgument( + 'duration', + InputArgument::OPTIONAL, + $this->trans('commands.database.log.poll.arguments.duration'), + '10' + ); + + } + + /** + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + */ + protected function execute(InputInterface $input, OutputInterface $output) { + + $io = new DrupalStyle($input, $output); + + $io->note($this->trans('commands.database.log.poll.messages.warning')); + + $this->getDefaultOptions($input); + $this->duration = $input->getArgument('duration'); + + $this->pollForEvents($io); + + } + + /** + * @param \Drupal\Console\Style\DrupalStyle $io + */ + protected function pollForEvents(DrupalStyle $io) { + + $query = $this->makeQuery($io)->countQuery(); + $results = $query->execute()->fetchAssoc(); + $count = $results['expression'] - 1;//minus 1 so the newest message always prints + + $tableHeader = $this->createTableHeader(); + + //Poll, force no wait on first loop + $lastExec = time() - $this->duration; + while (1) { + + if (time() > $lastExec + $this->duration) { + //Print out any new db logs + $query = $this->makeQuery($io, $count); + $results = $query->execute()->fetchAll(); + $count += count($results); + $tableRows = []; + foreach ($results as $r) { + $tableRows[] = $this->createTableRow($r); + } + if (!empty($tableRows)) { + $io->table($tableHeader, $tableRows); + } + //update the last exec time + $lastExec = time(); + } + } + + } + + +}