function _drupal_log_error

_drupal_log_error($error, $fatal = FALSE)

Logs a PHP error or exception and displays an error page in fatal cases.

Parameters

$error: 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 an HTML string, and backtrace, which is a standard PHP backtrace.

bool $fatal: TRUE for:

  • An exception is thrown and not caught by something else.
  • A recoverable fatal error, which is a fatal error.

Non-recoverable fatal errors cannot be logged by Drupal.

File

core/includes/errors.inc, line 128
Functions for error handling.

Code

function _drupal_log_error($error, $fatal = FALSE) {
  $is_installer = drupal_installation_attempted();

  // Backtrace array is not a valid replacement value for t().
  $backtrace = $error['backtrace'];
  unset($error['backtrace']);

  // When running inside the testing framework, we relay the errors
  // to the tested site by the way of HTTP headers.
  if (DRUPAL_TEST_IN_CHILD_SITE && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
    // $number does not use drupal_static as it should not be reset
    // as it uniquely identifies each PHP error.
    static $number = 0;
    $assertion = array(
      $error['@message'],
      $error['%type'],
      array(
        'function' => $error['%function'],
        'file' => $error['%file'],
        'line' => $error['%line'],
      ),
    );
    // For non-fatal errors (e.g. PHP notices) _drupal_log_error can be called
    // multiple times per request. In that case the response is typically
    // generated outside of the error handler, e.g., in a controller. As a
    // result it is not possible to use a Response object here but instead the
    // headers need to be emitted directly.
    header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
    $number++;
  }

  $response = new Response();

  // Only call the logger if there is a logger factory available. This can occur
  // if there is an error while rebuilding the container or during the
  // installer.
  if (\Drupal::hasService('logger.factory')) {
    try {
      \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file).', $error);
    }
    catch (\Exception $e) {
      // We can't log, for example because the database connection is not
      // available. At least try to log to PHP error log.
      error_log(strtr('Failed to log error: %type: @message in %function (line %line of %file).', $error));
    }
  }

  // Log fatal errors, so developers can find and debug them.
  if ($fatal) {
    error_log(sprintf('%s: %s in %s on line %d', $error['%type'], $error['@message'], $error['%file'], $error['%line']));
  }

  if (PHP_SAPI === 'cli') {
    if ($fatal) {
      // When called from CLI, simply output a plain text message.
      // Should not translate the string to avoid errors producing more errors.
      $response->setContent(html_entity_decode(strip_tags(SafeMarkup::format('%type: @message in %function (line %line of %file).', $error))) . "\n");
      $response->send();
      exit;
    }
  }

  if (\Drupal::hasRequest() && \Drupal::request()->isXmlHttpRequest()) {
    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.
        $response->setContent(SafeMarkup::format('%type: @message in %function (line %line of %file).', $error));
        $response->send();
      }
      exit;
    }
  }
  else {
    // Display the message if the current error reporting level allows this type
    // of message to be displayed, and unconditionally in update.php.
    $message = '';
    $class = NULL;
    if (error_displayable($error)) {
      $class = 'error';

      // If error type is 'User notice' then treat it as debug information
      // instead of an error message.
      // @see debug()
      if ($error['%type'] == 'User notice') {
        $error['%type'] = 'Debug';
        $class = 'status';
      }

      // Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path
      // in the message. This does not happen for (false) security.
      if (\Drupal::hasService('app.root')) {
        $root_length = strlen(\Drupal::root());
        if (substr($error['%file'], 0, $root_length) == \Drupal::root()) {
          $error['%file'] = substr($error['%file'], $root_length + 1);
        }
      }

      // Check if verbose error reporting is on.
      $error_level = _drupal_get_error_level();

      if ($error_level != ERROR_REPORTING_DISPLAY_VERBOSE) {
        // Without verbose logging, use a simple message.

        // We call SafeMarkup::format() directly here, rather than use t() since
        // we are in the middle of error handling, and we don't want t() to
        // cause further errors.
        $message = SafeMarkup::format('%type: @message in %function (line %line of %file).', $error);
      }
      else {
        // With verbose logging, we will also include a backtrace.

        // 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.
        $error['@backtrace'] = Error::formatBacktrace($backtrace);
        $message = SafeMarkup::format('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $error);
      }
    }

    if ($fatal) {
      // We fallback to a maintenance page at this point, because the page generation
      // itself can generate errors.
      // Should not translate the string to avoid errors producing more errors.
      $message = 'The website encountered an unexpected error. Please try again later.' . '<br />' . $message;

      if ($is_installer) {
        // install_display_output() prints the output and ends script execution.
        $output = array(
          '#title' => 'Error',
          '#markup' => $message,
        );
        install_display_output($output, $GLOBALS['install_state'], $response->headers->all());
        exit;
      }

      $response->setContent($message);
      $response->setStatusCode(500, '500 Service unavailable (with message)');

      $response->send();
      // An exception must halt script execution.
      exit;
    }

    if ($message) {
      if (\Drupal::hasService('session')) {
        // Message display is dependent on sessions being available.
        drupal_set_message($message, $class, TRUE);
      }
      else {
        print $message;
      }
    }
  }
}

© 2001–2016 by the original authors
Licensed under the GNU General Public License, version 2 and later.
Drupal is a registered trademark of Dries Buytaert.
https://api.drupal.org/api/drupal/core!includes!errors.inc/function/_drupal_log_error/8.1.x