diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index da3a277..17d9521 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -64,9 +64,40 @@
 const ERROR_REPORTING_DISPLAY_ALL = 'all';
 
 /**
- * Error reporting level: display all messages, plus backtrace information.
+ * Error reporting: Add stacktrace information to logs.
+ *
+ * Note that ErrorHandlerTest() relies on the following to be numbered
+ * sequentially so if that changes for any reason revise the function.
+ *
+ */
+const ERROR_REPORTING_DISPLAY_LOG_EXTRA = 1;  // Do not start at zero.
+
+/**
+ * Error reporting: Add stacktrace information to messages on page.
+ */
+const ERROR_REPORTING_DISPLAY_OUTPUT_EXTRA = 2;
+
+/**
+ * Error reporting: Include passed parameter types.
+ */
+const ERROR_REPORTING_DISPLAY_PASSED_PARAMS = 3;
+
+/**
+ * Error reporting: If ERROR_REPORTING_DISPLAY_PASSED_PARAMS
+ * then this setting will show data in the scalars.
+ */
+const ERROR_REPORTING_DISPLAY_PASSED_SCALARS = 4;
+
+/**
+ * Error reporting: Args can be comma or line separated.
+ */
+const ERROR_REPORTING_DISPLAY_LINE_PER_ARG = 5;
+
+/**
+ * Error reporting: If ERROR_REPORTING_DISPLAY_PASSED_SCALARS
+ * then this setting will shorten string with DRUPAL_ROOT substrings.
  */
-const ERROR_REPORTING_DISPLAY_VERBOSE = 'verbose';
+const ERROR_REPORTING_DISPLAY_STRIP_DRUPAL_ROOT_STRING_ARGS = 6;
 
 /**
  * @defgroup logging_severity_levels Logging severity levels
diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 3fe23b7..9413745 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -56,7 +56,7 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
   if ($error_level & error_reporting()) {
     $types = drupal_error_levels();
     list($severity_msg, $severity_level) = $types[$error_level];
-    $backtrace = debug_backtrace();
+    $backtrace = debug_backtrace(TRUE);
     $caller = _drupal_get_last_caller($backtrace);
 
     if (!function_exists('filter_xss_admin')) {
@@ -73,7 +73,7 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
       '%file' => $caller['file'],
       '%line' => $caller['line'],
       'severity_level' => $severity_level,
-      'backtrace' => $backtrace,
+      '!backtrace' => $backtrace,
     ), $error_level == E_RECOVERABLE_ERROR);
   }
 }
@@ -118,11 +118,11 @@ function _drupal_decode_exception($exception) {
     // The standard PHP exception handler considers that the exception message
     // is plain-text. We mimick this behavior here.
     '!message' => check_plain($message),
+    '!backtrace' => $backtrace,
     '%function' => $caller['function'],
     '%file' => $caller['file'],
     '%line' => $caller['line'],
     'severity_level' => WATCHDOG_ERROR,
-    'backtrace' => $backtrace,
   );
 }
 
@@ -150,6 +150,10 @@ function _drupal_render_exception_safe($exception) {
  *
  * @param $error
  *   Optional error to examine for ERROR_REPORTING_DISPLAY_SOME.
+ *   An array with the following keys: %type, !message, %function, %file,
+ *   %line, severity_level and backtrace. All the parameters are plain-text,
+ *   with the exception of !message, which needs to be a safe HTML string, and
+ *   backtrace, which is a standard PHP backtrace.
  *
  * @return
  *   TRUE if an error should be displayed.
@@ -157,12 +161,15 @@ function _drupal_render_exception_safe($exception) {
 function error_displayable($error = NULL) {
   $error_level = _drupal_get_error_level();
   $updating = (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update');
-  $all_errors_displayed = ($error_level == ERROR_REPORTING_DISPLAY_ALL) ||
-    ($error_level == ERROR_REPORTING_DISPLAY_VERBOSE);
-  $error_needs_display = ($error_level == ERROR_REPORTING_DISPLAY_SOME &&
-    isset($error) && $error['%type'] != 'Notice' && $error['%type'] != 'Strict warning');
-
-  return ($updating || $all_errors_displayed || $error_needs_display);
+  $all_errors_displayed = ($error_level == ERROR_REPORTING_DISPLAY_ALL);
+  $error_needs_display  = ($error_level == ERROR_REPORTING_DISPLAY_SOME 
+    && isset($error) && !in_array($error['%type'], array('Notice', 'Strict warning')));
+
+  // We always make displayable in the case of PDOException because we cannot
+  // write this type of error to the DB (PHP won't let us). Therefore, if we
+  // don't make it displayable we will lose the error information and may never
+  // track it down.
+  return ($updating || $all_errors_displayed || $error_needs_display || array_has_PDOException($error));
 }
 
 /**
@@ -187,9 +194,12 @@ function _drupal_log_error($error, $fatal = FALSE) {
     drupal_maintenance_theme();
   }
 
-  // Backtrace array is not a valid replacement value for t().
-  $backtrace = $error['backtrace'];
-  unset($error['backtrace']);
+  // Just in case somehow we get here without backtrace information added such
+  $backtrace = isset($error['!backtrace']) ? $error['!backtrace'] : '';
+  unset($error['!backtrace']);
+  if (!$backtrace) {
+    $backtrace = debug_backtrace(TRUE);
+  }
 
   // When running inside the testing framework, we relay the errors
   // to the tested site by the way of HTTP headers.
@@ -211,13 +221,43 @@ function _drupal_log_error($error, $fatal = FALSE) {
     $number++;
   }
 
-  watchdog('php', '%type: !message in %function (line %line of %file).', $error, $error['severity_level']);
+  // Formatted function calls
+  $stacktrace_options = Drupal::config('system.logging')->get('stacktrace_display');
+
+  if (array_sum($stacktrace_options)) {
+    $error['!stacktrace'] = format_stacktrace($backtrace);
+  }
+
+  $show_more = array(ERROR_REPORTING_DISPLAY_LOG_EXTRA, ERROR_REPORTING_DISPLAY_OUTPUT_EXTRA);
+  if (count(array_intersect($stacktrace_options, $show_more))) {
+    $message_log = $message_output = '%type: <pre>!message</pre><br/>LINE: %line<br/>FUNCTION: %function<br/>FILE: %file';
+
+    if ($stacktrace_options[ERROR_REPORTING_DISPLAY_LOG_EXTRA]) {
+      $message_log .= ' !stacktrace';
+    }
+    if ($stacktrace_options[ERROR_REPORTING_DISPLAY_OUTPUT_EXTRA]) {
+      $message_output .= ' !stacktrace';
+    }
+  }
+  else {
+    $message_log = $message_output = '%type: !message in %function (line %line of %file).';
+  }
+
+  // Should not translate the string to avoid errors producing more errors.
+  $message_output = format_string($message_output, $error);
+
+  // If it was a DB error don't write to the DB. Although this check is in
+  // watchdog() we check it here as well to avoid circular function calls since
+  // if watchdog() can't log the error then it will pass it to this function.
+  if (!array_has_PDOException($error)) {
+    watchdog('php', $message_log, $error, $error['severity_level']);
+  }
 
   if (drupal_is_cli()) {
     if ($fatal) {
       // When called from CLI, simply output a plain text message.
       // Should not translate the string to avoid errors producing more errors.
-      print html_entity_decode(strip_tags(format_string('%type: !message in %function (line %line of %file).', $error))). "\n";
+      print html_entity_decode(strip_tags($message_output)). "\n";
       exit;
     }
   }
@@ -225,9 +265,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
   if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
     if ($fatal) {
       if (error_displayable($error)) {
-        // When called from JavaScript, simply output the error message.
-        // Should not translate the string to avoid errors producing more errors.
-        print format_string('%type: !message in %function (line %line of %file).', $error);
+        print $message_output;
       }
       exit;
     }
@@ -247,27 +285,13 @@ function _drupal_log_error($error, $fatal = FALSE) {
       }
 
       // Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path
-      // in the message. This does not happen for (false) security.
+      // in the message.
       $root_length = strlen(DRUPAL_ROOT);
       if (substr($error['%file'], 0, $root_length) == DRUPAL_ROOT) {
         $error['%file'] = substr($error['%file'], $root_length + 1);
       }
-      // Should not translate the string to avoid errors producing more errors.
-      $message = format_string('%type: !message in %function (line %line of %file).', $error);
-
-      // Check if verbose error reporting is on.
-      $error_level = _drupal_get_error_level();
-
-      if ($error_level == ERROR_REPORTING_DISPLAY_VERBOSE) {
-        // First trace is the error itself, already contained in the message.
-        // While the second trace is the error source and also contained in the
-        // message, the message doesn't contain argument values, so we output it
-        // once more in the backtrace.
-        array_shift($backtrace);
-        // Generate a backtrace containing only scalar argument values.
-        $message .= '<pre class="backtrace">' . format_backtrace($backtrace) . '</pre>';
-      }
-      drupal_set_message($message, $class, TRUE);
+
+      drupal_set_message($message_output, $class);
     }
 
     if ($fatal) {
@@ -279,9 +303,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
       $output = theme('maintenance_page', array('content' => 'The website has encountered an error. Please try again later.'));
 
       $response = new Response($output, 500);
-      if ($fatal) {
-        $response->setStatusCode(500, '500 Service unavailable (with message)');
-      }
+      $response->setStatusCode(500, '500 Service unavailable (with message)');
 
       return $response;
     }
@@ -349,38 +371,153 @@ function _drupal_get_last_caller(&$backtrace) {
 }
 
 /**
- * Formats a backtrace into a plain-text string.
+ * We don't want to call watchdog if anywhere in the array there is a PDOException
+ * since PHP won't let us log a PDOException back to the DB. The function will
+ * check for the exception and return TRUE if it finds it in an array. It checks
+ * recursively through the array.
  *
- * The calls show values for scalar arguments and type names for complex ones.
+ * @param type $array
+ *   The array to check, will likely be $error from calling function
+ *
+ * @return boolean
+ *   TRUE if found.
+ */
+function array_has_PDOException($array) {
+  if (array_key_exists('%type', $array) && stripos($array['%type'], 'PDOException') !== FALSE) {
+    return TRUE;
+  }
+  foreach ($array as $value) {
+    if (is_array($value) && array_has_PDOException($value)) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Formats a stacktrace into an HTML table.
  *
  * @param array $backtrace
  *   A standard PHP backtrace.
  *
  * @return string
- *   A plain-text line-wrapped string ready to be put inside <pre>.
+ *   An HTML string.
  */
-function format_backtrace(array $backtrace) {
-  $return = '';
-  foreach ($backtrace as $trace) {
-    $call = array('function' => '', 'args' => array());
-    if (isset($trace['class'])) {
-      $call['function'] = $trace['class'] . $trace['type'] . $trace['function'];
-    }
-    elseif (isset($trace['function'])) {
-      $call['function'] = $trace['function'];
-    }
-    else {
-      $call['function'] = 'main';
+function format_stacktrace($backtrace) {
+
+  $report_type = 'STACKTRACE:';
+
+  $callstack = array_reverse($backtrace, TRUE);
+
+  $stacktrace_options = Drupal::config('system.logging')->get('stacktrace_display');
+  $show_params = $stacktrace_options[ERROR_REPORTING_DISPLAY_PASSED_PARAMS];
+  $show_scalar = $stacktrace_options[ERROR_REPORTING_DISPLAY_PASSED_SCALARS];
+  $cleaner_string_args = $stacktrace_options[ERROR_REPORTING_DISPLAY_STRIP_DRUPAL_ROOT_STRING_ARGS];
+  $args_sep = $stacktrace_options[ERROR_REPORTING_DISPLAY_STRIP_DRUPAL_ROOT_STRING_ARGS] ? '<br/>' : ', ';
+
+  if ($show_params) {
+    $params_table_header1 = '<th >Caller file</th>';
+    $params_table_header2 = '<th class="last_column">Passed types</th>';
+  } 
+  else {
+    $params_table_header1 = '<th class="last_column">Caller file</th>';
+    $params_table_header2 = '';
+  }
+
+  // TODO: Styling should be in CSS or should make use of drupal's existing CSS.
+  $cs =<<<EOT
+<style>
+  .stacktrace td, .stacktrace th {padding: 0 0.5em;}
+  .stacktrace th {border-width: 2px 0;}
+  .stacktrace .row-bunch {border-top: 2px solid #BFBFBA; border-bottom: 0;}
+  .stacktrace .first_column {border-left: 2px solid #BFBFBA;}
+  .stacktrace .last_column {border-right: 2px solid #BFBFBA;}
+  .stacktrace .row-bunch-last {border-bottom: 2px solid #BFBFBA;}
+  pre.stacktrace {font-family: "Andale Mono","Courier New",Courier,Lucidatypewriter,Fixed,monospace;}
+</style>
+<pre class="stacktrace">
+  <table>
+    <thead>
+      <tr>
+        <th class="first_column">Index</th>
+        <th>Function called</th>
+        <th>Caller line</th>
+        $params_table_header1
+        $params_table_header2
+      </tr>
+    </thead>
+    <tbody>
+EOT
+  ;
+
+  $column_names = array('function', 'line', 'file');
+  if ($show_params) {
+    $column_names[] = 'args';
+  }
+  $last_col_name = end($column_names);
+
+  $first_col_class = 'class="first_column"';
+  $last_col_classes[] = 'last_column';
+
+  $row_bunching = $row_bunching_max = 3;
+  foreach ($callstack AS $raw_data_no => &$raw_data) {
+    $classes = array();
+    $row_bunching++;
+    if ($row_bunching >= $row_bunching_max) {
+      $classes[] = 'row-bunch';
+      $row_bunching = 0;
+    } elseif ($raw_data_no == 0) {
+      $classes[] = 'row-bunch-last';
     }
-    foreach ($trace['args'] as $arg) {
-      if (is_scalar($arg)) {
-        $call['args'][] = is_string($arg) ? '\'' . filter_xss($arg) . '\'' : $arg;
-      }
-      else {
-        $call['args'][] = ucfirst(gettype($arg));
+    $row_class = 'class="'.implode(' ', $classes).'"';
+    $cs .= "<tr $row_class><td $first_col_class>$raw_data_no</td>";
+    foreach ($column_names as $column_name) {
+      if (isset($raw_data[$column_name])) {
+        switch ($column_name) {
+          case 'args':
+            $last_col_classes[] = 'row-bunch';
+            $cs .= '<td class="'.implode(' ', $last_col_classes).'">';
+            $data = $raw_data[$column_name];
+            $args = array();
+            foreach ($raw_data as $arg) {
+              if ($show_scalar && is_scalar($arg)) {
+                if (is_string($arg)) {
+                  if ($cleaner_string_args) {
+                    $arg = str_replace(DRUPAL_ROOT, '...', $arg);
+                    $arg = htmlentities($arg);
+                  }
+                  $args[] = '\'' . filter_xss($arg) . '\'';
+                }
+                else {
+                  $args[] = $arg;
+                }
+                $args[] = is_string($arg) ? '\'' . filter_xss($arg) . '\'' : $arg;
+              }
+              else {
+                $args[] = gettype($arg);
+              }
+            }
+            $data = implode($args_sep, $args);
+            break;
+            
+          default:
+            $cs .= $column_name == $last_col_name ? '<td class="'.implode(' ', $last_col_classes).'">' : '<td>';
+            $data = str_replace(DRUPAL_ROOT . '/', '', $raw_data[$column_name]);
+            $data = htmlentities($data);
+        }
+        $cs .= $data;
+      } else {
+        $cs .= $column_name == $last_col_name ? '<td class="'.implode(' ', $last_col_classes).'">' : '<td>';
       }
+      $cs .= '</td>';
     }
-    $return .= $call['function'] . '(' . implode(', ', $call['args']) . ")\n";
+    $cs .= '</tr>';
   }
-  return $return;
+  $cs .=<<<EOT
+    </tbody>
+  </table>
+</pre>
+EOT
+  ;
+  return '<br/><br/>' . $report_type . $cs;
 }
diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module
index 202cfcf..058e85e 100644
--- a/core/modules/dblog/dblog.module
+++ b/core/modules/dblog/dblog.module
@@ -146,20 +146,42 @@ function dblog_watchdog(array $log_entry) {
   // Remove any backtraces since they may contain an unserializable variable.
   unset($log_entry['variables']['backtrace']);
 
-  Database::getConnection('default', 'default')->insert('watchdog')
-    ->fields(array(
-      'uid' => $log_entry['uid'],
-      'type' => substr($log_entry['type'], 0, 64),
-      'message' => $log_entry['message'],
-      'variables' => serialize($log_entry['variables']),
-      'severity' => $log_entry['severity'],
-      'link' => substr($log_entry['link'], 0, 255),
-      'location' => $log_entry['request_uri'],
-      'referer' => $log_entry['referer'],
-      'hostname' => substr($log_entry['ip'], 0, 128),
-      'timestamp' => $log_entry['timestamp'],
-    ))
-    ->execute();
+  if (!array_has_PDOException($log_entry)) { // If it was a DB error don't write to the DB.
+    Database::getConnection('default', 'default')->insert('watchdog')
+      ->fields(array(
+        'uid' => $log_entry['uid'],
+        'type' => substr($log_entry['type'], 0, 64),
+        'message' => $log_entry['message'],
+        'variables' => serialize($log_entry['variables']),
+        'severity' => $log_entry['severity'],
+        'link' => substr($log_entry['link'], 0, 255),
+        'location' => $log_entry['request_uri'],
+        'referer' => $log_entry['referer'],
+        'hostname' => substr($log_entry['ip'], 0, 128),
+        'timestamp' => $log_entry['timestamp'],
+      ))
+      ->execute();
+  } else {
+    $desired_elements = array_flip(array('%type','!message', '%function', '%file', '%line', 'severity_level'));
+    $error = array();
+    if (isset($log_entry['variables'])) {
+      $error = array_intersect_key($log_entry['variables'], $desired_elements);
+    }
+    if (!count($error)) {
+      $error = array_intersect_key($log_entry, $desired_elements);
+    }
+    if (!count($error)) {
+      $error = array(
+          '%type' => $log_entry['type'],
+          '!message' => $log_entry['message'],
+          '%function' => __FUNCTION__,
+          '%file' => __FILE__,
+          '%line' => __LINE__,
+          'severity_level' => $log_entry['severity'],
+        );
+    }
+    _drupal_log_error($error, TRUE);
+  }
 }
 
 /**
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 4040625..12a1b98 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -1127,7 +1127,7 @@ public function errorHandler($severity, $message, $file = NULL, $line = NULL) {
       if ($severity !== E_USER_NOTICE) {
         $verbose_backtrace = $backtrace;
         array_shift($verbose_backtrace);
-        $message .= '<pre class="backtrace">' . format_backtrace($verbose_backtrace) . '</pre>';
+        $message .= format_stacktrace($verbose_backtrace);
       }
 
       $this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace));
@@ -1152,10 +1152,12 @@ protected function exceptionHandler($exception) {
     // The exception message is run through check_plain()
     // by _drupal_decode_exception().
     $decoded_exception = _drupal_decode_exception($exception);
-    unset($decoded_exception['backtrace']);
-    $message = format_string('%type: !message in %function (line %line of %file). <pre class="backtrace">!backtrace</pre>', $decoded_exception + array(
-      '!backtrace' => format_backtrace($verbose_backtrace),
-    ));
+    unset($decoded_exception['!backtrace']);
+    $message = format_string('%type: !message in %function (line %line of %file). !backtrace',
+        $decoded_exception + array(
+          '!backtrace' => format_stacktrace($verbose_backtrace),
+        )
+      );
     $this->error($message, 'Uncaught exception', _drupal_get_last_caller($backtrace));
   }
 
diff --git a/core/modules/system/config/system.logging.yml b/core/modules/system/config/system.logging.yml
index 3ecc76c..6bee54b 100644
--- a/core/modules/system/config/system.logging.yml
+++ b/core/modules/system/config/system.logging.yml
@@ -1 +1,9 @@
 error_level: all
+
+stacktrace_display:
+  1: 0
+  2: 0
+  3: 0
+  4: 0
+  5: 0
+  6: 6
diff --git a/core/modules/system/lib/Drupal/system/Form/LoggingForm.php b/core/modules/system/lib/Drupal/system/Form/LoggingForm.php
index bc74529..917c6bd 100644
--- a/core/modules/system/lib/Drupal/system/Form/LoggingForm.php
+++ b/core/modules/system/lib/Drupal/system/Form/LoggingForm.php
@@ -26,6 +26,7 @@ public function getFormID() {
    */
   public function buildForm(array $form, array &$form_state) {
     $config = $this->configFactory->get('system.logging');
+
     $form['error_level'] = array(
       '#type' => 'radios',
       '#title' => t('Error messages to display'),
@@ -34,11 +35,25 @@ public function buildForm(array $form, array &$form_state) {
         ERROR_REPORTING_HIDE => t('None'),
         ERROR_REPORTING_DISPLAY_SOME => t('Errors and warnings'),
         ERROR_REPORTING_DISPLAY_ALL => t('All messages'),
-        ERROR_REPORTING_DISPLAY_VERBOSE => t('All messages, with backtrace information'),
       ),
       '#description' => t('It is recommended that sites running on production environments do not display any errors.'),
     );
 
+    $form['stacktrace_display'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Choose how to monitor stacktrace information.'),
+      '#default_value' => $config->get('stacktrace_display'),
+      '#options' => array(
+        ERROR_REPORTING_DISPLAY_LOG_EXTRA => t('Add to log'),
+        ERROR_REPORTING_DISPLAY_OUTPUT_EXTRA => t('Show on page'),
+        ERROR_REPORTING_DISPLAY_PASSED_PARAMS => t('Show types of passed parameters'),
+        ERROR_REPORTING_DISPLAY_PASSED_SCALARS => t('Show scalar content if showing types of passed parameters'),
+        ERROR_REPORTING_DISPLAY_LINE_PER_ARG => t('List passed parameters on per line'),
+        ERROR_REPORTING_DISPLAY_STRIP_DRUPAL_ROOT_STRING_ARGS => t('Strip DRUPAL_ROOT from paths and replace with "..." on string parameters (looks cleaner).'),
+      ),
+      '#description' => t('On production environments only use "Add to log" and then only when needed, not "Show on page".'),
+    );
+
     return parent::buildForm($form, $form_state);
   }
 
@@ -48,6 +63,7 @@ public function buildForm(array $form, array &$form_state) {
   public function submitForm(array &$form, array &$form_state) {
     $this->configFactory->get('system.logging')
       ->set('error_level', $form_state['values']['error_level'])
+      ->set('stacktrace_display', $form_state['values']['stacktrace_display'])
       ->save();
 
     parent::submitForm($form, $form_state);
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ErrorHandlerTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ErrorHandlerTest.php
index 11d1356..9856e7d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/ErrorHandlerTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/ErrorHandlerTest.php
@@ -30,64 +30,100 @@ public static function getInfo() {
   }
 
   /**
+   * Must be public being an error trap.
+   * Calls the error handler and is surrounded by re-entrant code blocking so
+   * we don't end up in an infinite loop with triggered errors.
+   */
+  public function testErrorHandler_error_trap($errno = NULL, $errstr = NULL, $errfile = NULL, $errline = NULL, $errcontext = NULL) {
+    static $entered = FALSE;
+    if (is_null($errno)) {
+      return; // Was called by TestBase iterating through functions so ignore it.
+    }
+    if ($entered) {
+      // At this point let's not pick up variables or anything. Let's just print 
+      // a string which is about as safe we can get other than doing nothing.
+      print "<P>Call to: TestBase->errorHandler() about an error during ErrorHandlerTest->testErrorHandler() caused another error.\n</P>";
+      return;
+    }
+    $entered = TRUE;
+    $this->errorHandler($errno, $errstr, $errfile, $errline);
+    $entered = FALSE;
+  }
+
+  /**
    * Test the error handler.
    */
   function testErrorHandler() {
     $config = config('system.logging');
-    $error_notice = array(
-      '%type' => 'Notice',
-      '!message' => 'Undefined variable: bananas',
-      '%function' => 'error_test_generate_warnings()',
-      '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
-    );
-    $error_warning = array(
-      '%type' => 'Warning',
-      '!message' => 'Division by zero',
+
+    // Would prefer to just nest an error handler at this point but for some
+    // reason doing a set_error_handler() before a restore is causing a PHP error.
+    restore_error_handler();
+    set_error_handler(array($this, 'testErrorHandler_error_trap'));
+
+    $caller = array(
       '%function' => 'error_test_generate_warnings()',
       '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
+      '%line' => __LINE__,
     );
-    $error_user_notice = array(
-      '%type' => 'User warning',
-      '!message' => 'Drupal is awesome',
-      '%function' => 'error_test_generate_warnings()',
-      '%file' => drupal_get_path('module', 'error_test') . '/error_test.module',
+
+    $error_display_levels = array(
+        ERROR_REPORTING_HIDE,
+        ERROR_REPORTING_DISPLAY_SOME,
+        ERROR_REPORTING_DISPLAY_ALL
+      );
+
+    $options = array(
+      ERROR_REPORTING_DISPLAY_LOG_EXTRA => 0,
+      ERROR_REPORTING_DISPLAY_OUTPUT_EXTRA => 0,
+      ERROR_REPORTING_DISPLAY_PASSED_PARAMS => 0,
+      ERROR_REPORTING_DISPLAY_PASSED_SCALARS => 0,
+      ERROR_REPORTING_DISPLAY_LINE_PER_ARG => 0,
+      ERROR_REPORTING_DISPLAY_STRIP_DRUPAL_ROOT_STRING_ARGS => 0,
     );
 
-    // Set error reporting to display verbose notices.
-    config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_VERBOSE)->save();
-    $this->drupalGet('error-test/generate-warnings');
-    $this->assertResponse(200, 'Received expected HTTP status code.');
-    $this->assertErrorMessage($error_notice);
-    $this->assertErrorMessage($error_warning);
-    $this->assertErrorMessage($error_user_notice);
-    $this->assertRaw('<pre class="backtrace">', 'Found pre element with backtrace class.');
-
-    // Set error reporting to collect notices.
-    $config->set('error_level', ERROR_REPORTING_DISPLAY_ALL)->save();
-    $this->drupalGet('error-test/generate-warnings');
-    $this->assertResponse(200, 'Received expected HTTP status code.');
-    $this->assertErrorMessage($error_notice);
-    $this->assertErrorMessage($error_warning);
-    $this->assertErrorMessage($error_user_notice);
-    $this->assertNoRaw('<pre class="backtrace">', 'Did not find pre element with backtrace class.');
-
-    // Set error reporting to not collect notices.
-    $config->set('error_level', ERROR_REPORTING_DISPLAY_SOME)->save();
-    $this->drupalGet('error-test/generate-warnings');
-    $this->assertResponse(200, 'Received expected HTTP status code.');
-    $this->assertNoErrorMessage($error_notice);
-    $this->assertErrorMessage($error_warning);
-    $this->assertErrorMessage($error_user_notice);
-    $this->assertNoRaw('<pre class="backtrace">', 'Did not find pre element with backtrace class.');
-
-    // Set error reporting to not show any errors.
-    $config->set('error_level', ERROR_REPORTING_HIDE)->save();
-    $this->drupalGet('error-test/generate-warnings');
-    $this->assertResponse(200, 'Received expected HTTP status code.');
-    $this->assertNoErrorMessage($error_notice);
-    $this->assertNoErrorMessage($error_warning);
-    $this->assertNoErrorMessage($error_user_notice);
-    $this->assertNoRaw('<pre class="backtrace">', 'Did not find pre element with backtrace class.');
+    // Every one we uncomment multiplies out the loops so only use the ones that
+    // make sense in terms of may be handled differently.
+    $error_types = array(
+      E_ERROR,
+//      E_WARNING,
+//      E_NOTICE,
+//      E_USER_ERROR,
+//      E_USER_WARNING,
+//      E_USER_NOTICE,
+//      E_STRICT,
+//      E_DEPRECATED,
+//      E_USER_DEPRECATED,
+//      E_RECOVERABLE_ERROR,
+    );
+    
+    $max = pow(2, count($options));
+    /*
+     * These loops will test all permuations of the error reporting admin
+     * options and error types passed.
+     */
+    foreach ($error_display_levels as $error_display_level) {
+      $config->set('error_level', $error_display_level)->save();
+      for ($i = 0; $i <= $max; $i++) {
+        for ($j = 1; $j <= $max; $j = $j * 2) {
+          $options[$j] = $i & $j;
+          $config->set('stacktrace_display', $options)->save();
+          foreach ($error_types as $error_type) {
+            _drupal_error_handler_real(
+              $error_type,
+              'Options set when _drupal_error_handler_real() called: ' . var_export($options, TRUE),
+              $caller['%file'],
+              $caller['%line'],
+              array()
+            );
+          }
+        }
+      }
+    }
+    
+    // Restore the error handler back to this class.
+    restore_error_handler();
+    set_error_handler(array($this, 'errorHandler'));
   }
 
   /**
