diff --git a/config/install/rules.config.yml b/config/install/rules.config.yml new file mode 100644 index 00000000..9f3222a2 --- /dev/null +++ b/config/install/rules.config.yml @@ -0,0 +1,4 @@ +rules_log_errors: warning +rules_debug_log: false +rules_debug: false +rules_log_level: info diff --git a/config/schema/rules.schema.yml b/config/schema/rules.schema.yml index d89064c7..828510f7 100644 --- a/config/schema/rules.schema.yml +++ b/config/schema/rules.schema.yml @@ -109,3 +109,20 @@ rules_component.configuration.rules_action_set: label: 'Actions' sequence: - type: rules_component.configuration.[id] + +rules.config: + type: mapping + label: 'Rules configuration' + mapping: + rules_log_errors: + type: integer + label: 'Logging of Rules evaluation errors' + rules_debug_log: + type: boolean + label: 'Log debug information to the system log' + rules_debug: + type: boolean + label: 'Show debug information' + rules_log_level: + type: integer + label: 'Log level' diff --git a/rules.module b/rules.module new file mode 100644 index 00000000..b126a21e --- /dev/null +++ b/rules.module @@ -0,0 +1,17 @@ + [ + 'template' => 'rules_debug_element', + 'variables' => [ + 'head' => NULL, + 'link' => NULL, + 'log' => NULL, + ] + ], + ); +} diff --git a/rules.services.yml b/rules.services.yml index ee3d95e2..19f2ddf9 100644 --- a/rules.services.yml +++ b/rules.services.yml @@ -5,3 +5,9 @@ services: plugin.manager.rules_data_processor: class: Drupal\rules\Plugin\RulesDataProcessorManager arguments: ['@container.namespaces', '@module_handler'] + logger.channel.rules: + parent: logger.channel_base + arguments: ['rules'] + rules.log: + class: Drupal\rules\Engine\RulesLog + arguments: ['@logger.channel.rules', '@config.factory'] diff --git a/src/Engine/RulesLog.php b/src/Engine/RulesLog.php index 2feb0ac4..e0de3e7e 100644 --- a/src/Engine/RulesLog.php +++ b/src/Engine/RulesLog.php @@ -6,75 +6,89 @@ */ namespace Drupal\rules\Engine; -use Drupal\rules\Exception; + +use Drupal\Core\Logger\LoggerChannelInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Routing\LinkGeneratorTrait; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\Url; +use Psr\Log\LogLevel; /** * The rules default logging class. */ -class RulesLog { - - const INFO = 1; - const WARN = 2; - const ERROR = 3; +class RulesLog implements RulesLogInterface { + use LinkGeneratorTrait; + use StringTranslationTrait; - static protected $logger; + protected $log = []; + protected $logLevel; + protected $line = 0; /** - * @return RulesLog - * Returns the rules logger instance. + * @var \Drupal\Core\Logger\LoggerChannelInterface */ - public static function logger($log_level = self::INFO) { - if (!isset(self::$logger)) { - $class = __CLASS__; - self::$logger = new $class($log_level); - } - return self::$logger; - } + private $loggerChannel; - protected $log = []; - protected $logLevel; - protected $line = 0; + /** + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + private $config; /** - * This is a singleton. + * @param \Drupal\Core\Logger\LoggerChannelInterface $loggerChannel + * @param \Drupal\Core\Config\ConfigFactoryInterface $config */ - protected function __construct($logLevel = self::WARN) { - $this->logLevel = $logLevel; + public function __construct(LoggerChannelInterface $loggerChannel, ConfigFactoryInterface $config) { + $this->loggerChannel = $loggerChannel; + $this->config = $config; + + $this->logLevel = $config->get('rules.config')->get('rules_log_level'); } public function __clone() { - throw new Exception("Cannot clone the logger."); + throw new \Exception("Cannot clone the logger."); } /** - * Logs a log message. - * - * @see rules_log() + * {@inheritdoc} */ - public function log($msg, $args = [], $logLevel = self::INFO, $scope = NULL, $path = NULL) { + public function log($msg, $args = [], $logLevel = LogLevel::INFO, $scope = NULL, $path = NULL) { + $config = $this->config->get('rules.config'); + + $rules_log_errors = $config->get('rules_log_errors'); + $rules_debug_log = $config->get('rules_debug_log'); + $rules_debug = $config->get('rules_debug'); + + if ($logLevel >= $rules_log_errors) { + $this->loggerChannel->log($logLevel, $msg, $args); + } + + // Do nothing in case debugging is totally disabled. + if (!$rules_debug_log && !$rules_debug) { + return; + } + if ($logLevel >= $this->logLevel) { $this->log[] = [$msg, $args, $logLevel, microtime(TRUE), $scope, $path]; } } /** - * Checks the log and throws an exception if there were any problems. + * {@inheritdoc} */ - public function checkLog($logLevel = self::WARN) { + public function checkLog($logLevel = LogLevel::WARNING) { foreach ($this->log as $entry) { if ($entry[2] >= $logLevel) { - throw new Exception($this->render()); + throw new \Exception($this->render()); } } } /** - * Checks the log for (error) messages with a log level equal or higher than the given one. - * - * @return - * Whether the an error has been logged. + * {@inheritdoc} */ - public function hasErrors($logLevel = self::WARN) { + public function hasErrors($logLevel = LogLevel::WARNING) { foreach ($this->log as $entry) { if ($entry[2] >= $logLevel) { return TRUE; @@ -84,29 +98,38 @@ public function hasErrors($logLevel = self::WARN) { } /** - * Gets an array of logged messages. + * {@inheritdoc} */ public function get() { return $this->log; } /** - * Renders the whole log. + * {@inheritdoc} */ public function render() { $line = 0; $output = []; while (isset($this->log[$line])) { - $vars['head'] = t($this->log[$line][0], $this->log[$line][1]); + $vars['head'] = $this->t($this->log[$line][0], $this->log[$line][1]); $vars['log'] = $this->renderHelper($line); - $output[] = theme('rules_debug_element', $vars); + $output[] = [ + '#theme' => 'rules_debug_element', + '#head' => $vars['head'], + '#log' => $vars['log'], + ]; $line++; } - return implode('', $output); + + return $output; } /** * Renders the log of one event invocation. + * + * @param int $line + * + * @return array */ protected function renderHelper(&$line = 0) { $startTime = isset($this->log[$line][3]) ? $this->log[$line][3] : 0; @@ -115,37 +138,48 @@ protected function renderHelper(&$line = 0) { if ($output && !empty($this->log[$line][4])) { // The next entry stems from another evaluated set, add in its log // messages here. - $vars['head'] = t($this->log[$line][0], $this->log[$line][1]); + $vars['head'] = $this->t($this->log[$line][0], $this->log[$line][1]); if (isset($this->log[$line][5])) { - $vars['link'] = '[' . l('edit', $this->log[$line][5]) . ']'; + $vars['link'] = '[' . $this->l('edit', Url::fromUri($this->log[$line][5])) . ']'; } $vars['log'] = $this->renderHelper($line); - $output[] = theme('rules_debug_element', $vars); + $output[] = [ + '#theme' => 'rules_debug_element', + '#head' => $vars['head'], + '#link' => $vars['link'], + '#log' => $vars['log'], + ]; } else { $formatted_diff = round(($this->log[$line][3] - $startTime) * 1000, 3) .' ms'; - $msg = $formatted_diff .' '. t($this->log[$line][0], $this->log[$line][1]); - if ($this->log[$line][2] >= RulesLog::WARN) { - $level = $this->log[$line][2] == RulesLog::WARN ? 'warn' : 'error'; + $msg = $formatted_diff .' '. $this->t($this->log[$line][0], $this->log[$line][1]); + if ($this->log[$line][2] >= LogLevel::WARNING) { + $level = $this->log[$line][2] == LogLevel::WARNING ? 'warn' : 'error'; $msg = ''. $msg .''; } if (isset($this->log[$line][5]) && !isset($this->log[$line][4])) { - $msg .= ' [' . l('edit', $this->log[$line][5]) . ']'; + $msg .= ' [' . $this->l('edit', Url::fromUri($this->log[$line][5])) . ']'; } $output[] = $msg; - if (isset($this->log[$line][4]) && !$this->log[$line][4]) { + if (isset($this->log[$line][5]) && !$this->log[$line][4]) { // This was the last log entry of this set. - return theme('item_list', ['items' => $output]); + return [ + '#theme' => 'item_list', + '#items' => $output, + ]; } } $line++; } - return theme('item_list', ['items' => $output]); + return [ + '#theme' => 'item_list', + '#items' => $output, + ]; } /** - * Clears the logged messages. + * {@inheritdoc} */ public function clear() { $this->log = []; diff --git a/src/Engine/RulesLogInterface.php b/src/Engine/RulesLogInterface.php new file mode 100644 index 00000000..e99c138b --- /dev/null +++ b/src/Engine/RulesLogInterface.php @@ -0,0 +1,59 @@ +container->get('rules.log'); $logger->clear(); } @@ -74,7 +75,9 @@ public function testConfigAction() { $expression->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); + $log = $logger->get(); $this->assertEqual($log[0][0], 'action called'); } @@ -100,7 +103,9 @@ public function testConfigRule() { $expression->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); + $log = $logger->get(); $this->assertEqual($log[0][0], 'action called'); } diff --git a/src/Tests/NodeIntegrationTest.php b/src/Tests/NodeIntegrationTest.php index 21b4d752..496ee6e4 100644 --- a/src/Tests/NodeIntegrationTest.php +++ b/src/Tests/NodeIntegrationTest.php @@ -31,7 +31,8 @@ public function setUp() { // Clear the log from any stale entries that are bleeding over from previous // tests. - $logger = RulesLog::logger(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); $logger->clear(); $this->installSchema('system', ['sequences']); @@ -85,7 +86,9 @@ public function testNodeDataSelector() { $rule->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); + $log = $logger->get(); $this->assertEqual($log[0][0], 'action called'); } diff --git a/src/Tests/RulesEngineTest.php b/src/Tests/RulesEngineTest.php index ab40711e..f288a08a 100644 --- a/src/Tests/RulesEngineTest.php +++ b/src/Tests/RulesEngineTest.php @@ -25,7 +25,8 @@ public function setUp() { // Clear the log from any stale entries that are bleeding over from previous // tests. - $logger = RulesLog::logger(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); $logger->clear(); } @@ -67,7 +68,9 @@ public function testRuleCreation() { $rule->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); + $log = $logger->get(); $this->assertEqual($log[0][0], 'action called'); } @@ -95,7 +98,9 @@ public function testContextPassing() { $rule->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); + $log = $logger->get(); $this->assertEqual($log[0][0], 'action called'); } @@ -119,7 +124,9 @@ public function testProvidedVariables() { $rule->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); + /** @var \Drupal\rules\Engine\RulesLogInterface $logger */ + $logger = $this->container->get('rules.log'); + $log = $logger->get(); $this->assertEqual($log[0][0], 'action called'); } diff --git a/templates/rules_debug_element.html.twig b/templates/rules_debug_element.html.twig new file mode 100644 index 00000000..b551cac0 --- /dev/null +++ b/templates/rules_debug_element.html.twig @@ -0,0 +1,10 @@ +
+ + {{ log }} +
diff --git a/tests/modules/rules_test/src/Plugin/Action/TestAction.php b/tests/modules/rules_test/src/Plugin/Action/TestAction.php index 53dcc5a9..c72eadf8 100644 --- a/tests/modules/rules_test/src/Plugin/Action/TestAction.php +++ b/tests/modules/rules_test/src/Plugin/Action/TestAction.php @@ -24,7 +24,8 @@ class TestAction extends RulesActionBase { * {@inheritdoc} */ public function execute() { - RulesLog::logger()->log('action called'); + $logger = \Drupal::service('rules.log'); + $logger->log('action called'); } }