]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/debug/logger/monolog/LogstashFormatter.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.git] / includes / debug / logger / monolog / LogstashFormatter.php
1 <?php
2
3 namespace MediaWiki\Logger\Monolog;
4
5 /**
6  * LogstashFormatter squashes the base message array and the context and extras subarrays into one.
7  * This can result in unfortunately named context fields overwriting other data (T145133).
8  * This class modifies the standard LogstashFormatter to rename such fields and flag the message.
9  * Also changes exception JSON-ification which is done poorly by the standard class.
10  *
11  * Compatible with Monolog 1.x only.
12  *
13  * @since 1.29
14  */
15 class LogstashFormatter extends \Monolog\Formatter\LogstashFormatter {
16         /** @var array Keys which should not be used in log context */
17         protected $reservedKeys = [
18                 // from LogstashFormatter
19                 'message', 'channel', 'level', 'type',
20                 // from WebProcessor
21                 'url', 'ip', 'http_method', 'server', 'referrer',
22                 // from WikiProcessor
23                 'host', 'wiki', 'reqId', 'mwversion',
24                 // from config magic
25                 'normalized_message',
26         ];
27
28         /**
29          * Prevent key conflicts
30          * @param array $record
31          * @return array
32          */
33         protected function formatV0( array $record ) {
34                 if ( $this->contextPrefix ) {
35                         return parent::formatV0( $record );
36                 }
37
38                 $context = !empty( $record['context'] ) ? $record['context'] : [];
39                 $record['context'] = [];
40                 $formatted = parent::formatV0( $record );
41
42                 $formatted['@fields'] = $this->fixKeyConflicts( $formatted['@fields'], $context );
43                 return $formatted;
44         }
45
46         /**
47          * Prevent key conflicts
48          * @param array $record
49          * @return array
50          */
51         protected function formatV1( array $record ) {
52                 if ( $this->contextPrefix ) {
53                         return parent::formatV1( $record );
54                 }
55
56                 $context = !empty( $record['context'] ) ? $record['context'] : [];
57                 $record['context'] = [];
58                 $formatted = parent::formatV1( $record );
59
60                 $formatted = $this->fixKeyConflicts( $formatted, $context );
61                 return $formatted;
62         }
63
64         /**
65          * Check whether some context field would overwrite another message key. If so, rename
66          * and flag.
67          * @param array $fields Fields to be sent to logstash
68          * @param array $context Copy of the original $record['context']
69          * @return array Updated version of $fields
70          */
71         protected function fixKeyConflicts( array $fields, array $context ) {
72                 foreach ( $context as $key => $val ) {
73                         if (
74                                 in_array( $key, $this->reservedKeys, true ) &&
75                                 isset( $fields[$key] ) && $fields[$key] !== $val
76                         ) {
77                                 $fields['logstash_formatter_key_conflict'][] = $key;
78                                 $key = 'c_' . $key;
79                         }
80                         $fields[$key] = $val;
81                 }
82                 return $fields;
83         }
84
85         /**
86          * Use a more user-friendly trace format than NormalizerFormatter
87          * @param \Exception|\Throwable $e
88          * @return array
89          */
90         protected function normalizeException( $e ) {
91                 if ( !$e instanceof \Exception && !$e instanceof \Throwable ) {
92                         throw new \InvalidArgumentException( 'Exception/Throwable expected, got '
93                                 . gettype( $e ) . ' / ' . get_class( $e ) );
94                 }
95
96                 $data = [
97                         'class' => get_class( $e ),
98                         'message' => $e->getMessage(),
99                         'code' => $e->getCode(),
100                         'file' => $e->getFile() . ':' . $e->getLine(),
101                         'trace' => \MWExceptionHandler::getRedactedTraceAsString( $e ),
102                 ];
103
104                 $previous = $e->getPrevious();
105                 if ( $previous ) {
106                         $data['previous'] = $this->normalizeException( $previous );
107                 }
108
109                 return $data;
110         }
111 }