Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / debug / logger / monolog / LineFormatter.php
blob13a59fa6e1afc8412a50a927bd0b849a0cd02f0f
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
18 * @file
21 namespace MediaWiki\Logger\Monolog;
23 use Error;
24 use Monolog\Formatter\LineFormatter as MonologLineFormatter;
25 use MWExceptionHandler;
26 use Throwable;
28 /**
29 * Formats incoming records into a one-line string.
31 * An 'exeception' in the log record's context will be treated specially.
32 * It will be output for an '%exception%' placeholder in the format and
33 * excluded from '%context%' output if the '%exception%' placeholder is
34 * present.
36 * Throwables that are logged with this formatter will optional have their
37 * stack traces appended. If that is done, MWExceptionHandler::redactedTrace()
38 * will be used to redact the trace information.
40 * @since 1.26
41 * @ingroup Debug
42 * @copyright © 2015 Wikimedia Foundation and contributors
44 class LineFormatter extends MonologLineFormatter {
46 /**
47 * @param string|null $format The format of the message
48 * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
49 * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
50 * @param bool $ignoreEmptyContextAndExtra
51 * @param bool $includeStacktraces
53 public function __construct(
54 $format = null, $dateFormat = null, $allowInlineLineBreaks = false,
55 $ignoreEmptyContextAndExtra = false, $includeStacktraces = false
56 ) {
57 parent::__construct(
58 $format, $dateFormat, $allowInlineLineBreaks,
59 $ignoreEmptyContextAndExtra
61 $this->includeStacktraces( $includeStacktraces );
64 /**
65 * @inheritDoc
67 public function format( array $record ): string {
68 // Drop the 'private' flag from the context
69 unset( $record['context']['private'] );
71 // Handle throwables specially: pretty format and remove from context
72 // Will be output for a '%exception%' placeholder in format
73 $prettyException = '';
74 if ( isset( $record['context']['exception'] ) &&
75 str_contains( $this->format, '%exception%' )
76 ) {
77 $e = $record['context']['exception'];
78 unset( $record['context']['exception'] );
80 if ( $e instanceof Throwable ) {
81 $prettyException = $this->normalizeException( $e );
82 } elseif ( is_array( $e ) ) {
83 $prettyException = $this->normalizeExceptionArray( $e );
84 } else {
85 $prettyException = $this->stringify( $e );
89 $output = parent::format( $record );
91 if ( str_contains( $output, '%exception%' ) ) {
92 $output = str_replace( '%exception%', $prettyException, $output );
94 return $output;
97 /**
98 * Convert a Throwable to a string.
100 * @param Throwable $e
101 * @param int $depth
102 * @return string
104 protected function normalizeException( Throwable $e, int $depth = 0 ): string {
105 // Can't use typehint. Must match Monolog\Formatter\LineFormatter::normalizeException($e)
106 return $this->normalizeExceptionArray( $this->exceptionAsArray( $e ) );
110 * Convert a throwable to an array of structured data.
112 * @param Throwable $e
113 * @return array
115 protected function exceptionAsArray( Throwable $e ) {
116 $out = [
117 'class' => get_class( $e ),
118 'message' => $e->getMessage(),
119 'code' => $e->getCode(),
120 'file' => $e->getFile(),
121 'line' => $e->getLine(),
122 'trace' => MWExceptionHandler::redactTrace( $e->getTrace() ),
125 $prev = $e->getPrevious();
126 if ( $prev ) {
127 $out['previous'] = $this->exceptionAsArray( $prev );
130 return $out;
134 * Convert an array of Throwable data to a string.
136 * @param array $e
137 * @return string
139 protected function normalizeExceptionArray( array $e ) {
140 $defaults = [
141 'class' => 'Unknown',
142 'file' => 'unknown',
143 'line' => null,
144 'message' => 'unknown',
145 'trace' => [],
147 $e = array_merge( $defaults, $e );
149 // @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal class is always set
150 $which = is_a( $e['class'], Error::class, true ) ? 'Error' : 'Exception';
151 $str = "\n[$which {$e['class']}] (" .
152 "{$e['file']}:{$e['line']}) {$e['message']}";
154 if ( $this->includeStacktraces && $e['trace'] ) {
155 $str .= "\n" .
156 // @phan-suppress-next-line PhanTypeMismatchArgumentNullable trace is always set
157 MWExceptionHandler::prettyPrintTrace( $e['trace'], ' ' );
160 if ( isset( $e['previous'] ) ) {
161 $prev = $e['previous'];
162 while ( $prev ) {
163 $prev = array_merge( $defaults, $prev );
164 $which = is_a( $prev['class'], Error::class, true ) ? 'Error' : 'Exception';
165 $str .= "\nCaused by: [$which {$prev['class']}] (" .
166 "{$prev['file']}:{$prev['line']}) {$prev['message']}";
168 if ( $this->includeStacktraces && $prev['trace'] ) {
169 $str .= "\n" .
170 MWExceptionHandler::prettyPrintTrace(
171 $prev['trace'], ' '
175 $prev = $prev['previous'] ?? null;
178 return $str;