Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.940
diff -u -r1.940 common.inc
--- includes/common.inc	23 Jul 2009 21:20:16 -0000	1.940
+++ includes/common.inc	26 Jul 2009 18:39:27 -0000
@@ -751,6 +751,10 @@
  *   The exception object that was thrown.
  */
 function _drupal_exception_handler($exception) {
+  if ($exception instanceof DrupalException) {
+    // Since the exception wasn't caught, increase the error level.
+    $exception->setSeverity(WATCHDOG_ERROR);
+  }
   // Log the message to the watchdog and return an error page to the user.
   _drupal_log_error(_drupal_decode_exception($exception), TRUE);
 }
@@ -765,6 +769,11 @@
 function _drupal_decode_exception($exception) {
   $message = $exception->getMessage();
 
+  // DrupalExceptions have translatable variables.
+  if ($exception instanceof DrupalException) {
+    $message = t($message, $exception->getVariables());
+  }
+
   $backtrace = $exception->getTrace();
   // Add the line throwing the exception to the backtrace.
   array_unshift($backtrace, array('line' => $exception->getLine(), 'file' => $exception->getFile()));
Index: includes/bootstrap.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v
retrieving revision 1.291
diff -u -r1.291 bootstrap.inc
--- includes/bootstrap.inc	22 Jul 2009 04:45:35 -0000	1.291
+++ includes/bootstrap.inc	26 Jul 2009 18:39:24 -0000
@@ -236,6 +236,124 @@
  * @} End of "Title text filtering flags".
  */
 
+/**
+ * This Exception class is the base for all other Exception types in Drupal.
+ * By using this wrapper, you will allow global control over logging behavior,
+ * allow modules to create Exception handling hooks, etc.
+ *
+ * All custom Exception classes should extend DrupalException.
+ */
+class DrupalException extends Exception {
+
+  /**
+   * This has to default to FALSE so that if it gets turned on,
+   * the shutdown function will be registered correctly.
+   */
+  protected $shouldLog = FALSE;
+
+  /**
+   * This next block of variables are for watchdog logging.
+   */
+  protected $variables = array();
+  protected $severity = WATCHDOG_NOTICE;
+  protected $link = NULL;
+
+  /**
+   * The parent class Exception has two params: $message and $code.
+   *
+   * To better suit the Drupal use-case, this method also supports
+   * the t() param format, for example:
+   * new DrupalException('example !var', array('!var' => 'some text'))
+   *
+   * When the params are provided in that format, $code will be set to 0.
+   * It should not need to be set to anything else often, since core doesn't
+   * use it for anything.
+   *
+   * @param $message
+   *   Translatable message string
+   * @param $mixed
+   *   Typically an array of variables for translation,  or an error code integer
+   * @param $log
+   *   Starting setting for shouldLog.  This would normally be left as true.
+   */
+  function __construct($message = NULL, $mixed = 0, $log = TRUE) {
+    $this->setShouldLog($log);
+    $code = 0;
+    if (is_array($mixed)) {
+      $this->variables = $mixed;
+    }
+    else {
+      $code = intval($mixed);
+    }
+    parent::__construct($message, $code);
+  }
+
+  function setShouldLog($shouldLog) {
+    // Avoid registering the shutdown function multiple times if called directly.
+    if ($shouldLog === TRUE && $this->shouldLog !== TRUE) {
+      // Use a shutdown function instead of a destructor so that it will
+      // be called before the DB is destructed.
+      register_shutdown_function(array(&$this, "shutdown"));
+    }
+    $this->shouldLog = $shouldLog;
+  }
+
+  function setSeverity($severity) {
+    $this->severity = $severity;
+  }
+
+  function setLink($link) {
+    $this->link = $link;
+  }
+
+  function addVariables($vars) {
+    if (!is_array($vars)) {
+      return;
+    }
+    foreach ($vars as $key => $value) {
+      // Because addVariables can be called more than once in different scopes
+      // as the exception bubbles up, avoid blowing out the most specific data
+      // in the event of a namespacing collision.
+      if (!isset($this->variables[$key])) {
+        $this->variables[$key] = $value;
+      }
+    }
+  }
+
+  function getVariables() {
+    return $this->variables;
+  }
+
+  /**
+   * Shutdown function, gets called during shutdown but before DB connection
+   * is destroyed normally.
+   *
+   * Child classes can prevent or alter logging behavior by implementing this function and not calling parent.
+   *
+   * TODO: add hook?
+   */
+  function shutdown() {
+    $this->logToWatchdog();
+  }
+
+  function logToWatchdog() {
+    if ($this->shouldLog === TRUE) {
+      // Use the class the object was instanciated with as the watchdog type.
+      // Typically it will be 'DrupalException' but sometimes a child class.
+      try {
+        watchdog(
+          get_class($this),
+          $this->getMessage(),
+          $this->variables,
+          $this->severity,
+          $this->link
+        );
+      } catch (Exception $e) {
+        //TODO: If the watchdog attempt fails, optionally log to Apache log file via trigger_error?
+      }
+    }
+  }
+}
 
 /**
  * Start the timer with the specified name. If you start and stop
Index: modules/field/field.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/field/field.module,v
retrieving revision 1.20
diff -u -r1.20 field.module
--- modules/field/field.module	15 Jul 2009 17:55:18 -0000	1.20
+++ modules/field/field.module	26 Jul 2009 18:39:30 -0000
@@ -115,7 +115,7 @@
  * This class has no functionality of its own other than allowing all
  * Field API exceptions to be caught by a single catch block.
  */
-class FieldException extends Exception {}
+class FieldException extends DrupalException {}
 
 /**
  * Implement hook_flush_caches.
Index: modules/system/system.tar.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.tar.inc,v
retrieving revision 1.1
diff -u -r1.1 system.tar.inc
--- modules/system/system.tar.inc	17 Jun 2009 10:46:49 -0000	1.1
+++ modules/system/system.tar.inc	26 Jul 2009 18:39:32 -0000
@@ -51,7 +51,7 @@
 * @license  http://www.opensource.org/licenses/bsd-license.php New BSD License
 * @package  Archive_Tar
 */
-class Archive_Tar 
+class Archive_Tar
 {
     /**
     * @var string Name of the Tar
@@ -622,7 +622,7 @@
     function _error($p_message)
     {
         // ----- To be completed
-        throw new Exception($p_message);
+        throw new DrupalException($p_message);
     }
     // }}}
 
@@ -630,7 +630,7 @@
     function _warning($p_message)
     {
         // ----- To be completed
-        throw new Exception($p_message);
+        throw new DrupalException($p_message);
     }
     // }}}
 
Index: includes/filetransfer/filetransfer.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/filetransfer/filetransfer.inc,v
retrieving revision 1.2
diff -u -r1.2 filetransfer.inc
--- includes/filetransfer/filetransfer.inc	1 Jul 2009 13:44:53 -0000	1.2
+++ includes/filetransfer/filetransfer.inc	26 Jul 2009 18:39:30 -0000
@@ -178,7 +178,7 @@
    *   The destination file to be removed.
    */
   abstract protected function removeFileJailed($destination);
-  
+
   /**
    * Checks if a particular path is a directory
    *
@@ -193,7 +193,7 @@
 /**
  * FileTransferException class.
  */
-class FileTransferException extends Exception {
+class FileTransferException extends DrupalException {
   public $arguments;
 
   function __construct($message, $code = 0, $arguments = array()) {
Index: modules/dblog/dblog.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/dblog/dblog.admin.inc,v
retrieving revision 1.22
diff -u -r1.22 dblog.admin.inc
--- modules/dblog/dblog.admin.inc	6 Jun 2009 10:27:42 -0000	1.22
+++ modules/dblog/dblog.admin.inc	26 Jul 2009 18:39:30 -0000
@@ -173,6 +173,10 @@
         _dblog_format_message($dblog),
       ),
       array(
+        array('data' => t('Variables'), 'header' => TRUE),
+        _dblog_format_variables($dblog->variables),
+      ),
+      array(
         array('data' => t('Severity'), 'header' => TRUE),
         $severity[$dblog->severity],
       ),
@@ -268,6 +272,17 @@
 
 
 /**
+ * Returns a screen-printable view of the variables for this entry.
+ *
+ * @param $variables
+ *  Serialized variables array from watchdog row
+ */
+function _dblog_format_variables($variables) {
+  return check_plain( var_export( unserialize($variables), TRUE) );
+}
+
+
+/**
  * Return form for dblog administration filters.
  *
  * @ingroup forms
Index: includes/database/database.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/database.inc,v
retrieving revision 1.65
diff -u -r1.65 database.inc
--- includes/database/database.inc	22 Jul 2009 04:45:35 -0000	1.65
+++ includes/database/database.inc	26 Jul 2009 18:39:29 -0000
@@ -1326,11 +1326,11 @@
     try {
       // If the requested database does not exist then it is an unrecoverable error.
       if (!isset(self::$databaseInfo[$key])) {
-        throw new Exception('DB does not exist');
+        throw new DrupalException('DB does not exist');
       }
 
       if (!$driver = self::$databaseInfo[$key][$target]['driver']) {
-        throw new Exception('Drupal is not set up');
+        throw new DrupalException('Drupal is not set up');
       }
 
       // We cannot rely on the registry yet, because the registry requires
@@ -1390,12 +1390,12 @@
  * in use does not support transactions. The calling code must then take
  * appropriate action.
  */
-class TransactionsNotSupportedException extends Exception { }
+class TransactionsNotSupportedException extends DrupalException { }
 
 /**
  * Exception to throw when popTransaction() is called when no transaction is active.
  */
-class NoActiveTransactionException extends Exception { }
+class NoActiveTransactionException extends DrupalException { }
 
 /**
  * Exception to deny attempts to explicitly manage transactions.
@@ -1403,7 +1403,7 @@
  * This exception will be thrown when the PDO connection commit() is called.
  * Code should never call this method directly.
  */
-class ExplicitTransactionsNotSupportedException extends Exception { }
+class ExplicitTransactionsNotSupportedException extends DrupalException { }
 
 /**
  * Exception thrown for merge queries that do not make semantic sense.
@@ -1411,7 +1411,7 @@
  * There are many ways that a merge query could be malformed.  They should all
  * throw this exception and set an appropriately descriptive message.
  */
-class InvalidMergeQueryException extends Exception {}
+class InvalidMergeQueryException extends DrupalException {}
 
 /**
  * Exception thrown if an insert query specifies a field twice.
@@ -1419,12 +1419,12 @@
  * It is not allowed to specify a field as default and insert field, this
  * exception is thrown if that is the case.
  */
-class FieldsOverlapException extends Exception {}
+class FieldsOverlapException extends DrupalException {}
 
 /**
  * Exception thrown if an insert query doesn't specify insert or default fields.
  */
-class NoFieldsException extends Exception {}
+class NoFieldsException extends DrupalException {}
 
 /**
  * A wrapper class for creating and managing database transactions.
Index: includes/database/sqlite/schema.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/sqlite/schema.inc,v
retrieving revision 1.7
diff -u -r1.7 schema.inc
--- includes/database/sqlite/schema.inc	9 Jul 2009 10:12:14 -0000	1.7
+++ includes/database/sqlite/schema.inc	26 Jul 2009 18:39:30 -0000
@@ -326,7 +326,7 @@
         }
       }
       else {
-        new Exception("Unable to parse the column type " . $row->type);
+        new DrupalException('Unable to parse the column type !type',array('!type' => $row->type));
       }
     }
     $indexes = array();
