diff --git a/config/install/rules.settings.yml b/config/install/rules.settings.yml new file mode 100644 index 00000000..64f5e97a --- /dev/null +++ b/config/install/rules.settings.yml @@ -0,0 +1,4 @@ +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 008f8608..6a7b1482 100644 --- a/config/schema/rules.schema.yml +++ b/config/schema/rules.schema.yml @@ -118,4 +118,21 @@ rules.context.definition: label: 'Type' label: type: string - label: 'Label' \ No newline at end of file + label: 'Label' + +rules.settings: + type: config_object + label: 'Rules settings' + mapping: + log_errors: + type: integer + 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 + label: 'Log level' diff --git a/rules.services.yml b/rules.services.yml index 88d387b5..36e7ab12 100644 --- a/rules.services.yml +++ b/rules.services.yml @@ -5,3 +5,6 @@ services: plugin.manager.rules_data_processor: class: Drupal\rules\Context\DataProcessorManager arguments: ['@container.namespaces', '@module_handler'] + logger.channel.rules: + class: Drupal\rules\Logger\RulesLoggerChannel + arguments: ['@config.factory'] diff --git a/src/Engine/RulesLog.php b/src/Engine/RulesLog.php deleted file mode 100644 index 2feb0ac4..00000000 --- a/src/Engine/RulesLog.php +++ /dev/null @@ -1,153 +0,0 @@ -logLevel = $logLevel; - } - - public function __clone() { - throw new Exception("Cannot clone the logger."); - } - - /** - * Logs a log message. - * - * @see rules_log() - */ - public function log($msg, $args = [], $logLevel = self::INFO, $scope = NULL, $path = NULL) { - 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. - */ - public function checkLog($logLevel = self::WARN) { - foreach ($this->log as $entry) { - if ($entry[2] >= $logLevel) { - 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. - */ - public function hasErrors($logLevel = self::WARN) { - foreach ($this->log as $entry) { - if ($entry[2] >= $logLevel) { - return TRUE; - } - } - return FALSE; - } - - /** - * Gets an array of logged messages. - */ - public function get() { - return $this->log; - } - - /** - * Renders the whole log. - */ - public function render() { - $line = 0; - $output = []; - while (isset($this->log[$line])) { - $vars['head'] = t($this->log[$line][0], $this->log[$line][1]); - $vars['log'] = $this->renderHelper($line); - $output[] = theme('rules_debug_element', $vars); - $line++; - } - return implode('', $output); - } - - /** - * Renders the log of one event invocation. - */ - protected function renderHelper(&$line = 0) { - $startTime = isset($this->log[$line][3]) ? $this->log[$line][3] : 0; - $output = []; - while ($line < count($this->log)) { - 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]); - if (isset($this->log[$line][5])) { - $vars['link'] = '[' . l('edit', $this->log[$line][5]) . ']'; - } - $vars['log'] = $this->renderHelper($line); - $output[] = theme('rules_debug_element', $vars); - } - 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 = ''. $msg .''; - } - if (isset($this->log[$line][5]) && !isset($this->log[$line][4])) { - $msg .= ' [' . l('edit', $this->log[$line][5]) . ']'; - } - $output[] = $msg; - - if (isset($this->log[$line][4]) && !$this->log[$line][4]) { - // This was the last log entry of this set. - return theme('item_list', ['items' => $output]); - } - } - $line++; - } - return theme('item_list', ['items' => $output]); - } - - /** - * Clears the logged messages. - */ - public function clear() { - $this->log = []; - } -} diff --git a/src/Logger/RulesLoggerChannel.php b/src/Logger/RulesLoggerChannel.php new file mode 100644 index 00000000..967a2fdb --- /dev/null +++ b/src/Logger/RulesLoggerChannel.php @@ -0,0 +1,81 @@ +config = $config_factory->get('rules.settings'); + } + + /** + * {@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]) { + parent::log($level, $message, $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/Tests/ConfigEntityTest.php b/src/Tests/ConfigEntityTest.php index 65ad424e..64d05d21 100644 --- a/src/Tests/ConfigEntityTest.php +++ b/src/Tests/ConfigEntityTest.php @@ -7,8 +7,6 @@ namespace Drupal\rules\Tests; -use Drupal\rules\Engine\RulesLog; - /** * Tests storage and loading of Rules config entities. * @@ -30,11 +28,6 @@ public function setUp() { parent::setUp(); $this->storage = $this->container->get('entity.manager')->getStorage('rules_component'); - - // Clear the log from any stale entries that are bleeding over from previous - // tests. - $logger = RulesLog::logger(); - $logger->clear(); } /** @@ -72,8 +65,7 @@ public function testConfigAction() { $expression->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); - $this->assertEqual($log[0][0], 'action called'); + $this->assertRulesLogEntryExists('action called'); } /** @@ -99,8 +91,7 @@ public function testConfigRule() { $expression->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); - $this->assertEqual($log[0][0], 'action called'); + $this->assertRulesLogEntryExists('action called'); } /** diff --git a/src/Tests/NodeIntegrationTest.php b/src/Tests/NodeIntegrationTest.php index d75048ed..e349a4be 100644 --- a/src/Tests/NodeIntegrationTest.php +++ b/src/Tests/NodeIntegrationTest.php @@ -30,11 +30,6 @@ class NodeIntegrationTest extends RulesDrupalTestBase { public function setUp() { parent::setUp(); - // Clear the log from any stale entries that are bleeding over from previous - // tests. - $logger = RulesLog::logger(); - $logger->clear(); - $this->installSchema('system', ['sequences']); $this->installEntitySchema('user'); $this->installEntitySchema('node'); @@ -85,10 +80,6 @@ public function testNodeDataSelector() { $rule->addAction('rules_test_log'); $rule->setContextValue('node', $node); $rule->execute(); - - // Test that the action logged something. - $log = RulesLog::logger()->get(); - $this->assertEqual($log[0][0], 'action called'); } /** diff --git a/src/Tests/RulesDrupalTestBase.php b/src/Tests/RulesDrupalTestBase.php index b7976f66..9ebb97b6 100644 --- a/src/Tests/RulesDrupalTestBase.php +++ b/src/Tests/RulesDrupalTestBase.php @@ -35,6 +35,14 @@ abstract class RulesDrupalTestBase extends KernelTestBase { */ protected $typedDataManager; + + /** + * Rules logger. + * + * @var \Drupal\rules\Logger\RulesLoggerChannel + */ + protected $logger; + /** * Modules to enable. * @@ -47,6 +55,12 @@ 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(); + $this->expressionManager = $this->container->get('plugin.manager.rules_expression'); $this->conditionManager = $this->container->get('plugin.manager.condition'); $this->typedDataManager = $this->container->get('typed_data_manager'); @@ -68,4 +82,18 @@ protected function createCondition($id) { return $condition; } + /** + * Checks if particular message is in the log with given delta. + * + * @param string $message + * Log message. + * @param int $log_item_index + * Log item's index in log entries stack. + */ + protected function assertRulesLogEntryExists($message, $log_item_index = 0) { + // Test that the action has logged something. + $logs = $this->logger->getLogs(); + $this->assertEqual($logs[$log_item_index]['message'], $message); + } + } diff --git a/src/Tests/RulesEngineTest.php b/src/Tests/RulesEngineTest.php index e1fa9c34..f9b9baa9 100644 --- a/src/Tests/RulesEngineTest.php +++ b/src/Tests/RulesEngineTest.php @@ -18,18 +18,6 @@ */ class RulesEngineTest extends RulesDrupalTestBase { - /** - * {@inheritdoc} - */ - public function setUp() { - parent::setUp(); - - // Clear the log from any stale entries that are bleeding over from previous - // tests. - $logger = RulesLog::logger(); - $logger->clear(); - } - /** * Tests creating a rule and iterating over the rule elements. */ @@ -68,8 +56,7 @@ public function testRuleCreation() { $rule->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); - $this->assertEqual($log[0][0], 'action called'); + $this->assertRulesLogEntryExists('action called'); } /** @@ -94,8 +81,7 @@ public function testContextPassing() { $rule->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); - $this->assertEqual($log[0][0], 'action called'); + $this->assertRulesLogEntryExists('action called'); } /** @@ -115,8 +101,7 @@ public function testProvidedVariables() { $rule->execute(); // Test that the action logged something. - $log = RulesLog::logger()->get(); - $this->assertEqual($log[0][0], 'action called'); + $this->assertRulesLogEntryExists('action called'); } /** diff --git a/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php b/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php index b11d0860..a14a7261 100644 --- a/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php +++ b/tests/modules/rules_test/src/Plugin/Action/TestLogAction.php @@ -7,8 +7,10 @@ namespace Drupal\rules_test\Plugin\Action; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\rules\Core\RulesActionBase; -use Drupal\rules\Engine\RulesLog; +use Drupal\rules\Logger\RulesLoggerChannel; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides an action writing something to the Rules log. @@ -18,13 +20,49 @@ * label = @Translation("Test action logging.") * ) */ -class TestLogAction extends RulesActionBase { +class TestLogAction extends RulesActionBase implements ContainerFactoryPluginInterface { + + /** + * Rules logger instance. + * + * @var \Drupal\rules\Logger\RulesLoggerChannel + */ + protected $logger; + + /** + * Constructs a TestLogAction object. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin ID for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\rules\Logger\RulesLoggerChannel $logger + * Rules logger object. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, RulesLoggerChannel $logger) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->logger = $logger; + } + + /** + * {@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') + ); + } /** * {@inheritdoc} */ public function execute() { - RulesLog::logger()->log('action called'); + $this->logger->info('action called'); } } diff --git a/tests/src/Integration/Action/DataConvertTest.php b/tests/src/Integration/Action/DataConvertTest.php index 144de03d..f08dfaeb 100644 --- a/tests/src/Integration/Action/DataConvertTest.php +++ b/tests/src/Integration/Action/DataConvertTest.php @@ -18,7 +18,7 @@ class DataConvertTest extends RulesIntegrationTestBase { /** * The action to be tested. * - * @var \Drupal\rules\Engine\RulesActionInterface + * @var \Drupal\rules\Core\RulesActionInterface */ protected $action; diff --git a/tests/src/Unit/RulesLoggerChannelTest.php b/tests/src/Unit/RulesLoggerChannelTest.php new file mode 100644 index 00000000..9112b880 --- /dev/null +++ b/tests/src/Unit/RulesLoggerChannelTest.php @@ -0,0 +1,71 @@ +getConfigFactoryStub([ + 'rules.settings' => [ + 'debug_log' => $debug_log, + 'log_errors' => $psr3_log_error_level, + ], + ]); + $channel = new RulesLoggerChannel($config); + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $channel->addLogger($logger); + + $logger->expects($this->exactly($expect_log)) + ->method('log') + ->with($rfc_message_level, $message); + + $channel->log($psr3_message_level, $message); + } + + /** + * Data provider for self::testLog(). + */ + public function providerTestLog() { + return [ + [LogLevel::DEBUG, RfcLogLevel::DEBUG, 0, LogLevel::DEBUG, 0, 'apple'], + [LogLevel::CRITICAL, RfcLogLevel::CRITICAL, 1, LogLevel::DEBUG, 1, 'banana'], + [LogLevel::CRITICAL, RfcLogLevel::CRITICAL, 1, LogLevel::DEBUG, 1, 'orange'], + [LogLevel::INFO, RfcLogLevel::INFO, 1, LogLevel::CRITICAL, 0, 'cucumber'], + ]; + } + +}