function _drupal_log_error in Drupal 9
Same name and namespace in other branches
- 8 core/includes/errors.inc \_drupal_log_error()
- 7 includes/errors.inc \_drupal_log_error()
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, @backtrace_string, severity_level, backtrace, and exception. All the parameters are plain-text, with the exception of @message, which needs to be an HTML string, backtrace, which is a standard PHP backtrace, and exception, which is the exception object (or NULL if the error is not an exception).
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.
2 calls to _drupal_log_error()
- _drupal_error_handler_real in core/
includes/ errors.inc - Provides custom PHP error handling.
- _drupal_exception_handler in core/
includes/ bootstrap.inc - Provides custom PHP exception handling.
File
- core/
includes/ errors.inc, line 149 - Functions for error handling.
Code
function _drupal_log_error($error, $fatal = FALSE) {
$is_installer = InstallerKernel::installationAttempted();
// Backtrace, exception and 'severity_level' are not valid replacement values
// for t().
$backtrace = $error['backtrace'];
$exception = $error['exception'];
$severity = $error['severity_level'];
unset($error['backtrace'], $error['exception'], $error['severity_level']);
// 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)) {
_drupal_error_header($error['@message'], $error['%type'], $error['%function'], $error['%file'], $error['%line']);
}
$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 {
// Provide the PHP backtrace and exception to logger implementations. Add
// 'severity_level' to the context to maintain BC and allow logging
// implementations to use it.
\Drupal::logger('php')
->log($severity, '%type: @message in %function (line %line of %file) @backtrace_string.', $error + [
'backtrace' => $backtrace,
'exception' => $exception,
'severity_level' => $severity,
]);
} 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). @backtrace_string', $error));
}
}
// Log fatal errors, so developers can find and debug them.
if ($fatal) {
error_log(sprintf('%s: %s in %s on line %d %s', $error['%type'], $error['@message'], $error['%file'], $error['%line'], $error['@backtrace_string']));
}
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(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error))) . "\n");
$response
->send();
exit(1);
}
}
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(new FormattableMarkup('%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.
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('kernel')) {
$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 use \Drupal\Component\Render\FormattableMarkup 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 = new FormattableMarkup('%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 = new FormattableMarkup('%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 = [
'#title' => 'Error',
'#markup' => $message,
];
install_display_output($output, $GLOBALS['install_state']);
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::messenger()
->addMessage($message, $class, TRUE);
}
else {
print $message;
}
}
}
}