Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/install/rules.config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
rules_log_errors: warning
rules_debug_log: false
rules_debug: false
rules_log_level: info
17 changes: 17 additions & 0 deletions config/schema/rules.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
17 changes: 17 additions & 0 deletions rules.module
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/**
* Implements hook_theme().
*/
function rules_theme() {
return array(
'rules_debug_element' => [
'template' => 'rules_debug_element',
'variables' => [
'head' => NULL,
'link' => NULL,
'log' => NULL,
]
],
);
}
6 changes: 6 additions & 0 deletions rules.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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']
136 changes: 85 additions & 51 deletions src/Engine/RulesLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RulesLog should extend AbstractLogger, no?

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;
Expand All @@ -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;
Expand All @@ -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 = '<span class="rules-debug-' . $level . '">'. $msg .'</span>';
}
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 = [];
Expand Down
59 changes: 59 additions & 0 deletions src/Engine/RulesLogInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Drupal\rules\Engine;

use Psr\Log\LogLevel;

/**
* Interface RulesLogInterface
*/
interface RulesLogInterface {

/**
* Logs a log message.
*
* @param $msg
* @param array $args
* @param string $logLevel
* @param null $scope
* @param null $path
*
* @see rules_log()
*/
public function log($msg, $args = [], $logLevel = LogLevel::INFO, $scope = NULL, $path = NULL);

/**
* Checks the log and throws an exception if there were any problems.
*
* @param string $logLevel
*
* @throws \Exception
*/
public function checkLog($logLevel = LogLevel::WARNING);

/**
* Checks the log for (error) messages with a log level equal
* or higher than the given one.
*
* @param string $logLevel
*
* @return bool
* Whether the an error has been logged.
*/
public function hasErrors($logLevel = LogLevel::WARNING);

/**
* Gets an array of logged messages.
*/
public function get();

/**
* Renders the whole log.
*/
public function render();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove the render() part. The rules logger should simply extend AbstractLogger. Rendering should not be part of it but rather be outsourced into a separate service which can then be invoked from a specific controller which shows all log entries on a distinct page and can also be used to render the current rule log for the active request somewhere on the page.


/**
* Clears the logged messages.
*/
public function clear();
}
11 changes: 8 additions & 3 deletions src/Tests/ConfigEntityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,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();
}

Expand Down Expand Up @@ -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');
}

Expand All @@ -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');
}

Expand Down
7 changes: 5 additions & 2 deletions src/Tests/NodeIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand Down Expand Up @@ -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');
}

Expand Down
Loading