includeStacktraces( $includeStacktraces ); } /** * @inheritDoc */ public function format( array $record ) { // Drop the 'private' flag from the context unset( $record['context']['private'] ); // Handle exceptions specially: pretty format and remove from context // Will be output for a '%exception%' placeholder in format $prettyException = ''; if ( isset( $record['context']['exception'] ) && strpos( $this->format, '%exception%' ) !== false ) { $e = $record['context']['exception']; unset( $record['context']['exception'] ); if ( $e instanceof Exception ) { $prettyException = $this->normalizeException( $e ); } elseif ( is_array( $e ) ) { $prettyException = $this->normalizeExceptionArray( $e ); } else { $prettyException = $this->stringify( $e ); } } $output = parent::format( $record ); if ( strpos( $output, '%exception%' ) !== false ) { $output = str_replace( '%exception%', $prettyException, $output ); } return $output; } /** * Convert an Exception to a string. * * @param Exception $e * @return string */ protected function normalizeException( $e ) { return $this->normalizeExceptionArray( $this->exceptionAsArray( $e ) ); } /** * Convert an exception to an array of structured data. * * @param Exception $e * @return array */ protected function exceptionAsArray( Exception $e ) { $out = [ 'class' => get_class( $e ), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => MWExceptionHandler::redactTrace( $e->getTrace() ), ]; $prev = $e->getPrevious(); if ( $prev ) { $out['previous'] = $this->exceptionAsArray( $prev ); } return $out; } /** * Convert an array of Exception data to a string. * * @param array $e * @return string */ protected function normalizeExceptionArray( array $e ) { $defaults = [ 'class' => 'Unknown', 'file' => 'unknown', 'line' => null, 'message' => 'unknown', 'trace' => [], ]; $e = array_merge( $defaults, $e ); $str = "\n[Exception {$e['class']}] (" . "{$e['file']}:{$e['line']}) {$e['message']}"; if ( $this->includeStacktraces && $e['trace'] ) { $str .= "\n" . MWExceptionHandler::prettyPrintTrace( $e['trace'], ' ' ); } if ( isset( $e['previous'] ) ) { $prev = $e['previous']; while ( $prev ) { $prev = array_merge( $defaults, $prev ); $str .= "\nCaused by: [Exception {$prev['class']}] (" . "{$prev['file']}:{$prev['line']}) {$prev['message']}"; if ( $this->includeStacktraces && $prev['trace'] ) { $str .= "\n" . MWExceptionHandler::prettyPrintTrace( $prev['trace'], ' ' ); } $prev = $prev['previous'] ?? null; } } return $str; } }