diff --git a/.travis.yml b/.travis.yml index 61ef216c..6e6a0328 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,7 +52,10 @@ before_script: # Reference and enable rules in build site. - ln -s $TESTDIR modules/rules + - drush --yes dl webprofiler - drush --yes pm-enable simpletest rules + - drush cr + - drush --yes pm-enable webprofiler # Start a web server on port 8080, run in the background; wait for # initialization. This is temporarly disabled since there are no web tests diff --git a/config/install/rules.settings.yml b/config/install/rules.settings.yml index 64f5e97a..f3193f8b 100644 --- a/config/install/rules.settings.yml +++ b/config/install/rules.settings.yml @@ -1,4 +1,3 @@ log_errors: warning debug_log: false -debug: false log_level: info diff --git a/config/schema/rules.schema.yml b/config/schema/rules.schema.yml index 6a7b1482..5b26c4b1 100644 --- a/config/schema/rules.schema.yml +++ b/config/schema/rules.schema.yml @@ -125,14 +125,11 @@ rules.settings: label: 'Rules settings' mapping: log_errors: - type: integer + type: string label: 'Logging of Rules evaluation errors' debug_log: type: boolean label: 'Log debug information to the available loggers' - debug: - type: boolean - label: 'Show debug information' log_level: - type: integer + type: string label: 'Log level' diff --git a/rules.info.yml b/rules.info.yml index 563cb89a..467770c3 100644 --- a/rules.info.yml +++ b/rules.info.yml @@ -3,3 +3,5 @@ type: module description: 'React on events and conditionally evaluate actions.' package: Rules core: 8.x +test_dependencies: + - webprofiler \ No newline at end of file diff --git a/src/Logger/RulesLoggerChannel.php b/src/Logger/RulesLoggerChannel.php index 967a2fdb..55ac97ee 100644 --- a/src/Logger/RulesLoggerChannel.php +++ b/src/Logger/RulesLoggerChannel.php @@ -25,13 +25,6 @@ class RulesLoggerChannel extends LoggerChannel { */ protected $config; - /** - * Static storage of log entries. - * - * @var array - */ - protected $logs = []; - /** * Creates RulesLoggerChannel object. * @@ -47,12 +40,6 @@ public function __construct(ConfigFactoryInterface $config_factory) { * {@inheritdoc} */ public function log($level, $message, array $context = []) { - $this->logs[] = [ - 'level' => $level, - 'message' => $message, - 'context' => $context, - ]; - // Log message only if rules logging setting is enabled. if ($this->config->get('debug_log')) { if ($this->levelTranslation[$this->config->get('log_errors')] >= $this->levelTranslation[$level]) { @@ -61,21 +48,4 @@ public function log($level, $message, array $context = []) { } } - /** - * Returns the structured array of entries. - * - * @return array - * Array of stored log entries. - */ - public function getLogs() { - return $this->logs; - } - - /** - * Clears the static logs entries cache. - */ - public function clearLogs() { - $this->logs = []; - } - } diff --git a/src/Logger/RulesStubLogger.php b/src/Logger/RulesStubLogger.php new file mode 100644 index 00000000..943c5d3e --- /dev/null +++ b/src/Logger/RulesStubLogger.php @@ -0,0 +1,54 @@ +logs[] = [ + 'level' => $level, + 'message' => $message, + 'context' => $context, + ]; + } + + /** + * Clears static logs storage. + */ + public function cleanLogs() { + $this->logs = array(); + } + + /** + * Returns statically saved logs. + * + * @return array + * Array of logs. + */ + public function getLogs() { + return $this->logs; + } + +} diff --git a/src/RulesServiceProvider.php b/src/RulesServiceProvider.php new file mode 100644 index 00000000..038b2847 --- /dev/null +++ b/src/RulesServiceProvider.php @@ -0,0 +1,39 @@ +hasDefinition('logger.channel.rules') && $container->hasDefinition('webprofiler.drupal')) { + $container->register('webprofiler.rules', 'Drupal\rules\WebProfiler\DataCollector\RulesDataCollector') + ->addArgument(new Reference('logger.channel.rules')) + ->addTag('data_collector', array( + 'template' => '@rules/Collector/rules.html.twig', + 'id' => 'rules', + 'title' => 'Rules', + 'priority' => 200, + )); + // Replace the regular logger.channel.rules service with a traceable one. + $definition = $container->findDefinition('logger.channel.rules'); + $definition->setClass('Drupal\rules\WebProfiler\RulesChannelLoggerWrapper'); + } + } + +} diff --git a/src/Tests/ConfigEntityTest.php b/src/Tests/ConfigEntityTest.php index 64d05d21..15213df9 100644 --- a/src/Tests/ConfigEntityTest.php +++ b/src/Tests/ConfigEntityTest.php @@ -89,7 +89,6 @@ public function testConfigRule() { // Create the Rules expression object from the configuration. $expression = $loaded_entity->getExpression(); $expression->execute(); - // Test that the action logged something. $this->assertRulesLogEntryExists('action called'); } diff --git a/src/Tests/RulesDrupalTestBase.php b/src/Tests/RulesDrupalTestBase.php index 9ebb97b6..576b9135 100644 --- a/src/Tests/RulesDrupalTestBase.php +++ b/src/Tests/RulesDrupalTestBase.php @@ -7,6 +7,7 @@ namespace Drupal\rules\Tests; +use Drupal\rules\Logger\RulesStubLogger; use Drupal\simpletest\KernelTestBase; /** @@ -35,14 +36,6 @@ abstract class RulesDrupalTestBase extends KernelTestBase { */ protected $typedDataManager; - - /** - * Rules logger. - * - * @var \Drupal\rules\Logger\RulesLoggerChannel - */ - protected $logger; - /** * Modules to enable. * @@ -56,10 +49,15 @@ abstract class RulesDrupalTestBase extends KernelTestBase { public function setUp() { parent::setUp(); - $this->logger = $this->container->get('logger.channel.rules'); - // Clear the log from any stale entries that are bleeding over from previous - // tests. - $this->logger->clearLogs(); + // Prepare Rules logging for testing. + $this->installConfig(array('rules')); + $logger = new RulesStubLogger(); + $this->container->get('config.factory') + ->getEditable('rules.settings') + ->set('debug_log', 1) + ->save(); + $this->container->set('logger', $logger); + $this->container->get('logger')->cleanLogs(); $this->expressionManager = $this->container->get('plugin.manager.rules_expression'); $this->conditionManager = $this->container->get('plugin.manager.condition'); @@ -92,7 +90,7 @@ protected function createCondition($id) { */ protected function assertRulesLogEntryExists($message, $log_item_index = 0) { // Test that the action has logged something. - $logs = $this->logger->getLogs(); + $logs = $this->container->get('logger')->getLogs(); $this->assertEqual($logs[$log_item_index]['message'], $message); } diff --git a/src/Tests/RulesDrupalWebTestBase.php b/src/Tests/RulesDrupalWebTestBase.php new file mode 100644 index 00000000..5d16c084 --- /dev/null +++ b/src/Tests/RulesDrupalWebTestBase.php @@ -0,0 +1,52 @@ +user = $this->drupalCreateUser(); + // @todo uncomment it when patch with permission comes. + // $this->adminUser = $this->drupalCreateUser($permissions); + } + +} diff --git a/src/Tests/RulesEngineTest.php b/src/Tests/RulesEngineTest.php index f9b9baa9..239c7694 100644 --- a/src/Tests/RulesEngineTest.php +++ b/src/Tests/RulesEngineTest.php @@ -79,7 +79,6 @@ public function testContextPassing() { $rule->addAction('rules_test_log'); $rule->setContextValue('test', 'test value'); $rule->execute(); - // Test that the action logged something. $this->assertRulesLogEntryExists('action called'); } @@ -99,7 +98,6 @@ public function testProvidedVariables() { $rule->addAction('rules_test_log'); $rule->execute(); - // Test that the action logged something. $this->assertRulesLogEntryExists('action called'); } diff --git a/src/Tests/RulesWebProfilerTest.php b/src/Tests/RulesWebProfilerTest.php new file mode 100644 index 00000000..0c16a078 --- /dev/null +++ b/src/Tests/RulesWebProfilerTest.php @@ -0,0 +1,76 @@ +container->get('config.factory') + ->getEditable('rules.settings') + ->set('debug_log', 1) + ->set('log_errors', 'debug') + ->save(); + + $this->webProfilerUser = $this->drupalCreateUser(array( + 'access webprofiler', + 'view webprofiler toolbar', + )); + + // Enables rules web debugging with WebProfiler. + $this->container->get('config.factory') + ->getEditable('webprofiler.config') + ->set('active_toolbar_items.rules', 'rules') + ->save(); + + $this->drupalLogin($this->webProfilerUser); + } + + /** + * Goes to WebProfiler page using link from toolbar and check entries there. + */ + public function testWebProfilerPage() { + $this->drupalGet('404', [ + 'query' => [ + 'log' => '1', + 'log-level' => 'critical', + 'log-message' => 'critical message', + 'log-amount' => 5, + ], + ]); + + $this->drupalGet('admin/reports/profiler/list'); + $links = $this->xpath('//main//table[1]//a'); + $url = $this->getAbsoluteUrl($links[0]['href']); + $this->drupalGet($url); + $this->assertText('Rules logs', 'Rules logs table exists'); + $this->assertText('critical message', 'Rules log entry exists'); + } + +} diff --git a/src/WebProfiler/DataCollector/RulesDataCollector.php b/src/WebProfiler/DataCollector/RulesDataCollector.php new file mode 100644 index 00000000..0afca5b5 --- /dev/null +++ b/src/WebProfiler/DataCollector/RulesDataCollector.php @@ -0,0 +1,161 @@ +rulesLogger = $rulesLogger; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = NULL) { + $this->data['logs'] = $this->rulesLogger->getLogs(); + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'rules'; + } + + /** + * {@inheritdoc} + */ + public function getTitle() { + return $this->t('Rules'); + } + + /** + * {@inheritdoc} + */ + public function getPanelSummary() { + return $this->t('Total rules log entries: @count', array('@count' => $this->getLogsCount())); + } + + /** + * Return amount of rules log entries. + * + * @return int + * Amount of rules log entries. + */ + public function getLogsCount() { + return count($this->data['logs']); + } + + /** + * Return amount of Rules log entries with level higher then warning. + * + * @return int + * Amount of error Rules log entries. + */ + public function getErrorLogsCount() { + $logs = array_filter($this->data['logs'], function ($log) { + return in_array($log['level'], array( + LogLevel::ERROR, + LogLevel::CRITICAL, + LogLevel::ALERT, + LogLevel::EMERGENCY + )); + }); + + return count($logs); + } + + /** + * Return amount of Rules log entries with level notice or warning. + * + * @return int + * Amount of error Rules log entries. + */ + public function getNoticeLogsCount() { + $logs = array_filter($this->data['logs'], function ($log) { + return in_array($log['level'], array(LogLevel::WARNING, LogLevel::NOTICE)); + }); + + return count($logs); + } + + /** + * Return amount of Rules info log entries. + * + * @return int + * Amount of error Rules log entries. + */ + public function getInfoLogsCount() { + $logs = array_filter($this->data['logs'], function ($log) { + return in_array($log['level'], array(LogLevel::DEBUG, LogLevel::INFO)); + }); + + return count($logs); + } + + /** + * {@inheritdoc} + */ + public function getPanel() { + $build = array(); + + $build['table_title'] = array( + '#type' => 'inline_template', + '#template' => '

{{ "Rules logs"|t }}

', + ); + + $css_header = array( + 'level', + 'message', + 'passed_context', + ); + + $rows = array_map(function ($log) { + return [ + $log['level'], $log['message'], implode(', ', array_keys($log['context'])) + ]; + }, $this->data['logs']); + + $build['logs_table'] = array( + '#type' => 'table', + '#rows' => $rows, + '#header' => $css_header, + '#sticky' => TRUE, + ); + + return $build; + } + +} diff --git a/src/WebProfiler/RulesChannelLoggerWrapper.php b/src/WebProfiler/RulesChannelLoggerWrapper.php new file mode 100644 index 00000000..5c13edc3 --- /dev/null +++ b/src/WebProfiler/RulesChannelLoggerWrapper.php @@ -0,0 +1,44 @@ +logs[] = [ + 'level' => $level, + 'message' => $message, + 'context' => $context, + ]; + } + + /** + * Return a list of rules log entries. + * + * @return array + * List of rules log entries. + */ + public function getLogs() { + return $this->logs; + } + +} diff --git a/templates/Collector/rules.html.twig b/templates/Collector/rules.html.twig new file mode 100644 index 00000000..7678c8b5 --- /dev/null +++ b/templates/Collector/rules.html.twig @@ -0,0 +1,42 @@ +{% block toolbar %} + {% set icon %} + + {{ 'Rules'|t }} + {{ collector.getlogscount }} + + {% endset %} + {% set text %} + +
+ {{ 'Error log entries'|t }} + {{ collector.geterrorlogscount }} +
+ +
+ {{ 'Notice log entries'|t }} + {{ collector.getnoticelogscount }} +
+ +
+ {{ 'Info log entries'|t }} + {{ collector.getinfologscount }} +
+ + {% endset %} + +
+
{{ icon|default('') }}
+
{{ text|default('') }}
+
+{% endblock %} + +{% block panel %} +
+

{{ 'Rules'|t }}

+
+ +
+ {{ content }} +
+{% endblock %} diff --git a/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php b/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php index a14a7261..67352a81 100644 --- a/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php +++ b/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php @@ -10,6 +10,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\rules\Core\RulesActionBase; use Drupal\rules\Logger\RulesLoggerChannel; +use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -27,7 +28,7 @@ class TestLogAction extends RulesActionBase implements ContainerFactoryPluginInt * * @var \Drupal\rules\Logger\RulesLoggerChannel */ - protected $logger; + protected $loggerChannel; /** * Constructs a TestLogAction object. @@ -38,12 +39,13 @@ class TestLogAction extends RulesActionBase implements ContainerFactoryPluginInt * The plugin ID for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param \Drupal\rules\Logger\RulesLoggerChannel $logger + * @param \Drupal\rules\Logger\RulesLoggerChannel $loggerChannel * Rules logger object. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, RulesLoggerChannel $logger) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, RulesLoggerChannel $loggerChannel, LoggerInterface $logger) { parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->logger = $logger; + $this->loggerChannel = $loggerChannel; + $this->loggerChannel->addLogger($logger); } /** @@ -54,7 +56,8 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('logger.channel.rules') + $container->get('logger.channel.rules'), + $container->get('logger') ); } @@ -62,7 +65,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function execute() { - $this->logger->info('action called'); + $this->loggerChannel->critical('action called'); } } diff --git a/tests/modules/rules_webprofiler_test/rules_webprofiler_test.info.yml b/tests/modules/rules_webprofiler_test/rules_webprofiler_test.info.yml new file mode 100644 index 00000000..94c461c0 --- /dev/null +++ b/tests/modules/rules_webprofiler_test/rules_webprofiler_test.info.yml @@ -0,0 +1,9 @@ +name: Rules Web Profiler test +type: module +description: 'Support module for the Rules Web Profiler integration tests.' +package: Rules +core: 8.x +hidden: true +dependencies: + - rules + - webprofiler diff --git a/tests/modules/rules_webprofiler_test/rules_webprofiler_test.module b/tests/modules/rules_webprofiler_test/rules_webprofiler_test.module new file mode 100644 index 00000000..98bd2a66 --- /dev/null +++ b/tests/modules/rules_webprofiler_test/rules_webprofiler_test.module @@ -0,0 +1,17 @@ +createAction('rules_test_webprofiler_log'); + $action->execute(); +} diff --git a/tests/modules/rules_webprofiler_test/rules_webprofiler_test.services.yml b/tests/modules/rules_webprofiler_test/rules_webprofiler_test.services.yml new file mode 100644 index 00000000..79b4970a --- /dev/null +++ b/tests/modules/rules_webprofiler_test/rules_webprofiler_test.services.yml @@ -0,0 +1,5 @@ +services: + rules_webprofiler_test_event_subscriber: + class: Drupal\rules_webprofiler_test\EventSubscriber\RulesWebProfilerTestSubscriber + tags: + - {name: event_subscriber} \ No newline at end of file diff --git a/tests/modules/rules_webprofiler_test/src/EventSubscriber/RulesWebProfilerTestSubscriber.php b/tests/modules/rules_webprofiler_test/src/EventSubscriber/RulesWebProfilerTestSubscriber.php new file mode 100644 index 00000000..e9140952 --- /dev/null +++ b/tests/modules/rules_webprofiler_test/src/EventSubscriber/RulesWebProfilerTestSubscriber.php @@ -0,0 +1,30 @@ +createAction('rules_test_webprofiler_log'); + $action->execute(); + } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + $events[KernelEvents::REQUEST][] = array('executeRulesAction'); + return $events; + } + +} diff --git a/tests/modules/rules_webprofiler_test/src/Plugin/Action/TestWebProfilerAction.php b/tests/modules/rules_webprofiler_test/src/Plugin/Action/TestWebProfilerAction.php new file mode 100644 index 00000000..3a3a8954 --- /dev/null +++ b/tests/modules/rules_webprofiler_test/src/Plugin/Action/TestWebProfilerAction.php @@ -0,0 +1,91 @@ +logger = $logger; + $this->request = $request_stack->getCurrentRequest(); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('logger.channel.rules'), + $container->get('request_stack') + ); + } + + /** + * {@inheritdoc} + */ + public function execute() { + // Get level passed via GET param. + if (!$log = $this->request->get('log', 'enabled')) { + return; + } + $level = $this->request->get('log-level', 'debug'); + $amount = $this->request->get('log-amount', 1); + $message = $this->request->get('log-message', 'debug message'); + $contexts = $this->request->get('log-contexts', ''); + for ($i = 0; $i < $amount; $i++) { + $this->logger->log($level, $message . '#' . $i, array_flip(explode('|', $contexts))); + } + } + +}