Merge "Special:Newpages feed now shows first revision instead of latest revision"
[lhc/web/wiklou.git] / includes / debug / logger / monolog / LineFormatter.php
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.
7 *
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.
12 *
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
17 *
18 * @file
19 */
20
21 namespace MediaWiki\Logger\Monolog;
22
23 use Exception;
24 use Monolog\Formatter\LineFormatter as MonologLineFormatter;
25 use MWExceptionHandler;
26
27 /**
28 * Formats incoming records into a one-line string.
29 *
30 * An 'exeception' in the log record's context will be treated specially.
31 * It will be output for an '%exception%' placeholder in the format and
32 * excluded from '%context%' output if the '%exception%' placeholder is
33 * present.
34 *
35 * Exceptions that are logged with this formatter will optional have their
36 * stack traces appended. If that is done, MWExceptionHandler::redactedTrace()
37 * will be used to redact the trace information.
38 *
39 * @since 1.26
40 * @copyright © 2015 Wikimedia Foundation and contributors
41 */
42 class LineFormatter extends MonologLineFormatter {
43
44 /**
45 * @param string $format The format of the message
46 * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
47 * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
48 * @param bool $ignoreEmptyContextAndExtra
49 * @param bool $includeStacktraces
50 */
51 public function __construct(
52 $format = null, $dateFormat = null, $allowInlineLineBreaks = false,
53 $ignoreEmptyContextAndExtra = false, $includeStacktraces = false
54 ) {
55 parent::__construct(
56 $format, $dateFormat, $allowInlineLineBreaks,
57 $ignoreEmptyContextAndExtra
58 );
59 $this->includeStacktraces( $includeStacktraces );
60 }
61
62 /**
63 * @inheritDoc
64 */
65 public function format( array $record ) {
66 // Drop the 'private' flag from the context
67 unset( $record['context']['private'] );
68
69 // Handle exceptions specially: pretty format and remove from context
70 // Will be output for a '%exception%' placeholder in format
71 $prettyException = '';
72 if ( isset( $record['context']['exception'] ) &&
73 strpos( $this->format, '%exception%' ) !== false
74 ) {
75 $e = $record['context']['exception'];
76 unset( $record['context']['exception'] );
77
78 if ( $e instanceof Exception ) {
79 $prettyException = $this->normalizeException( $e );
80 } elseif ( is_array( $e ) ) {
81 $prettyException = $this->normalizeExceptionArray( $e );
82 } else {
83 $prettyException = $this->stringify( $e );
84 }
85 }
86
87 $output = parent::format( $record );
88
89 if ( strpos( $output, '%exception%' ) !== false ) {
90 $output = str_replace( '%exception%', $prettyException, $output );
91 }
92 return $output;
93 }
94
95 /**
96 * Convert an Exception to a string.
97 *
98 * @param Exception $e
99 * @return string
100 */
101 protected function normalizeException( $e ) {
102 return $this->normalizeExceptionArray( $this->exceptionAsArray( $e ) );
103 }
104
105 /**
106 * Convert an exception to an array of structured data.
107 *
108 * @param Exception $e
109 * @return array
110 */
111 protected function exceptionAsArray( Exception $e ) {
112 $out = [
113 'class' => get_class( $e ),
114 'message' => $e->getMessage(),
115 'code' => $e->getCode(),
116 'file' => $e->getFile(),
117 'line' => $e->getLine(),
118 'trace' => MWExceptionHandler::redactTrace( $e->getTrace() ),
119 ];
120
121 $prev = $e->getPrevious();
122 if ( $prev ) {
123 $out['previous'] = $this->exceptionAsArray( $prev );
124 }
125
126 return $out;
127 }
128
129 /**
130 * Convert an array of Exception data to a string.
131 *
132 * @param array $e
133 * @return string
134 */
135 protected function normalizeExceptionArray( array $e ) {
136 $defaults = [
137 'class' => 'Unknown',
138 'file' => 'unknown',
139 'line' => null,
140 'message' => 'unknown',
141 'trace' => [],
142 ];
143 $e = array_merge( $defaults, $e );
144
145 $str = "\n[Exception {$e['class']}] (" .
146 "{$e['file']}:{$e['line']}) {$e['message']}";
147
148 if ( $this->includeStacktraces && $e['trace'] ) {
149 $str .= "\n" .
150 MWExceptionHandler::prettyPrintTrace( $e['trace'], ' ' );
151 }
152
153 if ( isset( $e['previous'] ) ) {
154 $prev = $e['previous'];
155 while ( $prev ) {
156 $prev = array_merge( $defaults, $prev );
157 $str .= "\nCaused by: [Exception {$prev['class']}] (" .
158 "{$prev['file']}:{$prev['line']}) {$prev['message']}";
159
160 if ( $this->includeStacktraces && $prev['trace'] ) {
161 $str .= "\n" .
162 MWExceptionHandler::prettyPrintTrace(
163 $prev['trace'], ' '
164 );
165 }
166
167 $prev = isset( $prev['previous'] ) ? $prev['previous'] : null;
168 }
169 }
170 return $str;
171 }
172 }