Commit 72db95c9 authored by Dries Buytaert's avatar Dries Buytaert
Browse files

- Patch #711108 by Berdir: better exception reporting for watchdog() in the database system.

parent 164ac036
Loading
Loading
Loading
Loading
+40 −5
Changes for includes/bootstrap.inc: 40 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -1534,6 +1534,46 @@ function request_uri() {
  return $uri;
}

/**
 * Log an exception.
 *
 * This is a wrapper function for watchdog() which automatically decodes an
 * exception.
 *
 * @param $type
 *   The category to which this message belongs.
 * @param $exception
 *   The exception that is going to be logged.
 * @param $message
 *   The message to store in the log. If empty, a text that contains all useful
 *   information about the passed in exception is used.
 * @param $variables
 *   Array of variables to replace in the message on display. Defaults to the
 *   return value of drupal_decode_exception().
 * @param $severity
 *   The severity of the message, as per RFC 3164.
 * @param $link
 *   A link to associate with the message.
 *
 * @see watchdog()
 * @see drupal_decode_exception()
 */
function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) {

   // Use a default value if $message is not set.
   if (empty($message)) {
     $message = '%type: %message in %function (line %line of %file).';
   }
   // $variables must be an array so that we can add the exception information.
   if (!is_array($variables)) {
     $variables = array();
   }

   require_once DRUPAL_ROOT . '/includes/errors.inc';
   $variables += _drupal_decode_exception($exception);
   watchdog($type, $message, $variables, $severity, $link);
}

/**
 * Log a system message.
 *
@@ -1556,8 +1596,6 @@ function request_uri() {
 *
 * @see watchdog_severity_levels()
 * @see hook_watchdog()
 * @see DatabaseConnection::rollback()
 * @see DatabaseTransaction::rollback()
 */
function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
  global $user, $base_root;
@@ -2105,9 +2143,6 @@ function _drupal_bootstrap_database() {
  // won't be initialized until it is actually requested.
  require_once DRUPAL_ROOT . '/includes/database/database.inc';

  // Set Drupal's watchdog as the logging callback.
  Database::setLoggingCallback('watchdog', WATCHDOG_NOTICE, WATCHDOG_ERROR);

  // Register autoload functions so that we can access classes and interfaces.
  // The database autoload routine comes first so that we can load the database
  // system without hitting the database. That is especially important during
+13 −151
Changes for includes/database/database.inc: 13 added lines, 151 removed lines.
Original line number Diff line number Diff line
@@ -145,14 +145,14 @@
 *     return $id;
 *   }
 *   catch (Exception $e) {
 *     // Something went wrong somewhere, so flag the entire transaction to
 *     // roll back instead of getting committed.  It doesn't actually roll back
 *     // yet, just gets flagged to do so.
 *     // Something went wrong somewhere, so roll back now.
 *     $txn->rollback();
 *     // Log the exception to watchdog.
 *     watchdog_exception('type', $e);
 *   }
 *
 *   // $txn goes out of scope here.  If there was a problem, it rolls back
 *   // automatically.  If not, it commits automatically.
 *   // $txn goes out of scope here.  Unless the transaction was rolled back, it
 *   // gets automatically commited here.
 * }
 *
 * function my_other_function($id) {
@@ -208,13 +208,6 @@ abstract class DatabaseConnection extends PDO {
   */
  protected $transactionLayers = array();

  /**
   * Array of argument arrays for logging post-rollback.
   *
   * @var array
   */
  protected $rollbackLogs = array();

  /**
   * Index of what driver-specific class to use for various operations.
   *
@@ -863,26 +856,10 @@ public function startTransaction($name = '') {
   * @param $savepoint_name
   *   The name of the savepoint. The default, 'drupal_transaction', will roll
   *   the entire transaction back.
   * @param $type
   *   The category to which the rollback message belongs.
   * @param $message
   *   The message to store in the log. Keep $message translatable
   *   by not concatenating dynamic values into it! Variables in the
   *   message should be added by using placeholder strings alongside
   *   the variables argument to declare the value of the placeholders.
   * @param $variables
   *   Array of variables to replace in the message on display or
   *   NULL if message is already translated or not possible to
   *   translate.
   * @param $severity
   *   The severity of the message, as per RFC 3164.
   * @param $link
   *   A link to associate with the message.
   *
   * @see DatabaseTransaction::rollback()
   * @see watchdog()
   */
  public function rollback($savepoint_name = 'drupal_transaction', $type = NULL, $message = NULL, $variables = array(), $severity = NULL, $link = NULL) {
  public function rollback($savepoint_name = 'drupal_transaction') {
    if (!$this->inTransaction()) {
      throw new DatabaseTransactionNoActiveException();
    }
@@ -892,29 +869,6 @@ public function rollback($savepoint_name = 'drupal_transaction', $type = NULL, $
      return;
    }

    // Set the severity to the configured default if not specified.
    if (!isset($severity)) {
      $logging = Database::getLoggingCallback();
      if (is_array($logging)) {
        $severity = $logging['default_severity'];
      }
    }

    // Record in an array to send to the log after transaction rollback.
    // Messages written directly to a log (with a database back-end) will roll
    // back during the following transaction rollback. This is an array because
    // rollback could be requested multiple times during a transaction, and all
    // such errors ought to be logged.
    if (isset($message)) {
      $this->rollbackLogs[] = array(
        'type' => $type,
        'message' => $message,
        'variables' => $variables,
        'severity' => $severity,
        'link' => $link,
      );
    }

    // We need to find the point we're rolling back to, all other savepoints
    // before are no longer needed.
    while ($savepoint = array_pop($this->transactionLayers)) {
@@ -932,37 +886,6 @@ public function rollback($savepoint_name = 'drupal_transaction', $type = NULL, $
    if ($this->supportsTransactions()) {
      parent::rollBack();
    }
    else {
      // Log unsupported rollback.
      $this->rollbackLogs[] = array(
        'type' => 'database',
        'message' => t('Explicit rollback failed: not supported on active connection.'),
        'variables' => array(),
      );
    }
    $this->logRollback();
  }

  /**
   * Logs messages from rollback().
   */
  protected function logRollback() {
    $logging = Database::getLoggingCallback();
    // If there is no callback defined. We can't do anything.
    if (!is_array($logging)) {
      return;
    }

    $logging_callback = $logging['callback'];

    // Play back the logged errors to the specified logging callback post-
    // rollback.
    foreach ($this->rollbackLogs as $log_item) {
      call_user_func($logging_callback, $log_item['type'], $log_item['message'], $log_item['variables'], $log_item['severity'], $log_item['link']);
    }

    // Reset the error logs.
    $this->rollbackLogs = array();
  }

  /**
@@ -1253,17 +1176,6 @@ abstract class Database {
   */
  static protected $logs = array();

  /**
   * A logging function callback array.
   *
   * The function must accept the same function signature as Drupal's
   * watchdog(). The array contains key/value pairs for callback (string),
   * default_severity (int), and error_severity (int).
   *
   * @var string
   */
  static protected $logging_callback = NULL;

  /**
   * Starts logging a given logging key on the specified connection.
   *
@@ -1296,38 +1208,6 @@ final public static function startLog($logging_key, $key = 'default') {
    return self::$logs[$key];
  }

  /**
   * Sets a logging callback for notices and errors.
   *
   * @param $logging_callback
   *   The function to use as the logging callback.
   * @param $logging_default_severity
   *   The default severity level to use for logged messages.
   * @param $logging_error_severity
   *   The severity level to use for logging error messages.
   *
   * @see watchdog()
   */
  final public static function setLoggingCallback($callback, $default_severity, $error_severity) {
    self::$logging_callback = array(
      'callback' => $callback,
      'default_severity' => $default_severity,
      'error_severity' => $error_severity,
    );
  }

  /**
   * Gets the logging callback for notices and errors.
   *
   * @return
   *   An array with the logging callback and severity levels.
   *
   * @see watchdog()
   */
  final public static function getLoggingCallback() {
    return self::$logging_callback;
  }

  /**
   * Retrieves the queries logged on for given logging key.
   *
@@ -1720,35 +1600,17 @@ public function name() {
   * Rolls back the current transaction.
   *
   * This is just a wrapper method to rollback whatever transaction stack we are
   * currently in, which is managed by the connection object itself.
   *
   * @param $type
   *   The category to which the rollback message belongs.
   * @param $message
   *   The message to store in the log. Keep $message translatable by not
   *   concatenating dynamic values into it! Variables in the message should be
   *   added by using placeholder strings alongside the variable's argument to
   *   declare the value of the placeholders.
   * @param $variables
   *   Array of variables to replace in the message on display or NULL if the
   *   message is already translated or not possible to translate.
   * @param $severity
   *   The severity of the message, as per RFC 3164.
   * @param $link
   *   A link to associate with the message.
   * currently in, which is managed by the connection object itself. Note that
   * logging (preferable with watchdog_exception()) needs to happen after a
   * transaction has been rolled back or the log messages will be rolled back
   * too.
   *
   * @see DatabaseConnection::rollback()
   * @see watchdog()
   * @see watchdog_exception()
   */
  public function rollback($type = NULL, $message = NULL, $variables = array(), $severity = NULL, $link = NULL) {
  public function rollback() {
    $this->rolledBack = TRUE;
    if (!isset($severity)) {
      $logging = Database::getLoggingCallback();
      if (is_array($logging)) {
        $severity = $logging['default_severity'];
      }
    }
    $this->connection->rollback($this->name, $type, $message, $variables, $severity, $link);
    $this->connection->rollback($this->name);
  }
}

+1 −34
Changes for includes/database/sqlite/database.inc: 1 added line, 34 removed lines.
Original line number Diff line number Diff line
@@ -210,7 +210,7 @@ public function nextId($existing_id = 0) {
    return (int) $this->query('SELECT value FROM {sequences}')->fetchField();
  }

  public function rollback($savepoint_name = 'drupal_transaction', $type = NULL, $message = NULL, $variables = array(), $severity = NULL, $link = NULL) {
  public function rollback($savepoint_name = 'drupal_transaction') {
    if ($this->savepointSupport) {
      return parent::rollBack($savepoint_name, $type, $message, $variables, $severity, $link);
    }
@@ -224,29 +224,6 @@ public function rollback($savepoint_name = 'drupal_transaction', $type = NULL, $
      return;
    }

    // Set the severity to the configured default if not specified.
    if (!isset($severity)) {
      $logging = Database::getLoggingCallback();
      if (is_array($logging)) {
        $severity = $logging['default_severity'];
      }
    }

    // Record in an array to send to the log after transaction rollback.
    // Messages written directly to a log (with a database back-end) will roll
    // back during the following transaction rollback. This is an array because
    // rollback could be requested multiple times during a transaction, and all
    // such errors ought to be logged.
    if (isset($message)) {
      $this->rollbackLogs[] = array(
        'type' => $type,
        'message' => $message,
        'variables' => $variables,
        'severity' => $severity,
        'link' => $link,
      );
    }

    // We need to find the point we're rolling back to, all other savepoints
    // before are no longer needed.
    while ($savepoint = array_pop($this->transactionLayers)) {
@@ -265,15 +242,6 @@ public function rollback($savepoint_name = 'drupal_transaction', $type = NULL, $
    if ($this->supportsTransactions()) {
      PDO::rollBack();
    }
    else {
      // Log unsupported rollback.
      $this->rollbackLogs[] = array(
        'type' => 'database',
        'message' => t('Explicit rollback failed: not supported on active connection.'),
        'variables' => array(),
      );
    }
    $this->logRollback();
  }

  public function pushTransaction($name) {
@@ -313,7 +281,6 @@ public function popTransaction($name) {
        if ($this->willRollback) {
          $this->willRollback = FALSE;
          PDO::rollBack();
          $this->logRollback();
        }
        elseif (!PDO::commit()) {
          throw new DatabaseTransactionCommitFailedException();
+2 −1
Changes for includes/menu.inc: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -2307,7 +2307,8 @@ function menu_rebuild() {
    }
  }
  catch (Exception $e) {
    $transaction->rollback('menu', $e->getMessage(), array(), WATCHDOG_ERROR);
    $transaction->rollback();
    watchdog_exception('menu', $exception);
  }

  lock_release('menu_rebuild');
+2 −1
Changes for modules/comment/comment.module: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -1529,7 +1529,8 @@ function comment_save($comment) {
    }
  }
  catch (Exception $e) {
    $transaction->rollback('comment', $e->getMessage(), array(), WATCHDOG_ERROR);
    $transaction->rollback('comment');
    watchdog_exception('comment', $e);
    throw $e;
  }

Loading