MediaWiki REL1_33
LegacyLogger.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Logger;
22
23use DateTimeZone;
24use Exception;
28use Psr\Log\AbstractLogger;
29use Psr\Log\LogLevel;
31
49class LegacyLogger extends AbstractLogger {
50
54 protected $channel;
55
62 protected static $levelMapping = [
63 LogLevel::DEBUG => 100,
64 LogLevel::INFO => 200,
65 LogLevel::NOTICE => 250,
66 LogLevel::WARNING => 300,
67 LogLevel::ERROR => 400,
68 LogLevel::CRITICAL => 500,
69 LogLevel::ALERT => 550,
70 LogLevel::EMERGENCY => 600,
71 ];
72
76 protected static $dbChannels = [
77 'DBQuery' => true,
78 'DBConnection' => true
79 ];
80
84 public function __construct( $channel ) {
85 $this->channel = $channel;
86 }
87
96 public function log( $level, $message, array $context = [] ) {
97 global $wgDBerrorLog;
98
99 if ( is_string( $level ) ) {
100 $level = self::$levelMapping[$level];
101 }
102 if ( $this->channel === 'DBQuery'
103 && isset( $context['method'] )
104 && isset( $context['master'] )
105 && isset( $context['runtime'] )
106 ) {
107 // Also give the query information to the MWDebug tools
108 $enabled = MWDebug::query(
109 $message,
110 $context['method'],
111 $context['master'],
112 $context['runtime']
113 );
114 if ( $enabled ) {
115 // If we the toolbar was enabled, return early so that we don't
116 // also log the query to the main debug output.
117 return;
118 }
119 }
120
121 // If this is a DB-related error, and the site has $wgDBerrorLog
122 // configured, rewrite the channel as wfLogDBError instead.
123 // Likewise, if the site does not use $wgDBerrorLog, it should
124 // configurable like any other channel via $wgDebugLogGroups
125 // or $wgMWLoggerDefaultSpi.
126 if ( isset( self::$dbChannels[$this->channel] )
127 && $level >= self::$levelMapping[LogLevel::ERROR]
129 ) {
130 // Format and write DB errors to the legacy locations
131 $effectiveChannel = 'wfLogDBError';
132 } else {
133 $effectiveChannel = $this->channel;
134 }
135
136 if ( self::shouldEmit( $effectiveChannel, $message, $level, $context ) ) {
137 $text = self::format( $effectiveChannel, $message, $context );
138 $destination = self::destination( $effectiveChannel, $message, $context );
139 self::emit( $text, $destination );
140 }
141 if ( !isset( $context['private'] ) || !$context['private'] ) {
142 // Add to debug toolbar if not marked as "private"
143 MWDebug::debugMsg( $message, [ 'channel' => $this->channel ] + $context );
144 }
145 }
146
157 public static function shouldEmit( $channel, $message, $level, $context ) {
159
160 if ( is_string( $level ) ) {
161 $level = self::$levelMapping[$level];
162 }
163
164 if ( $channel === 'wfLogDBError' ) {
165 // wfLogDBError messages are emitted if a database log location is
166 // specfied.
167 $shouldEmit = (bool)$wgDBerrorLog;
168
169 } elseif ( $channel === 'wfDebug' ) {
170 // wfDebug messages are emitted if a catch all logging file has
171 // been specified. Checked explicitly so that 'private' flagged
172 // messages are not discarded by unset $wgDebugLogGroups channel
173 // handling below.
174 $shouldEmit = $wgDebugLogFile != '';
175
176 } elseif ( isset( $wgDebugLogGroups[$channel] ) ) {
177 $logConfig = $wgDebugLogGroups[$channel];
178
179 if ( is_array( $logConfig ) ) {
180 $shouldEmit = true;
181 if ( isset( $logConfig['sample'] ) ) {
182 // Emit randomly with a 1 in 'sample' chance for each message.
183 $shouldEmit = mt_rand( 1, $logConfig['sample'] ) === 1;
184 }
185
186 if ( isset( $logConfig['level'] ) ) {
187 $shouldEmit = $level >= self::$levelMapping[$logConfig['level']];
188 }
189 } else {
190 // Emit unless the config value is explictly false.
191 $shouldEmit = $logConfig !== false;
192 }
193
194 } elseif ( isset( $context['private'] ) && $context['private'] ) {
195 // Don't emit if the message didn't match previous checks based on
196 // the channel and the event is marked as private. This check
197 // discards messages sent via wfDebugLog() with dest == 'private'
198 // and no explicit wgDebugLogGroups configuration.
199 $shouldEmit = false;
200 } else {
201 // Default return value is the same as the historic wfDebug
202 // method: emit if $wgDebugLogFile has been set.
203 $shouldEmit = $wgDebugLogFile != '';
204 }
205
206 return $shouldEmit;
207 }
208
221 public static function format( $channel, $message, $context ) {
223
224 if ( $channel === 'wfDebug' ) {
225 $text = self::formatAsWfDebug( $channel, $message, $context );
226
227 } elseif ( $channel === 'wfLogDBError' ) {
228 $text = self::formatAsWfLogDBError( $channel, $message, $context );
229
230 } elseif ( $channel === 'profileoutput' ) {
231 // Legacy wfLogProfilingData formatitng
232 $forward = '';
233 if ( isset( $context['forwarded_for'] ) ) {
234 $forward = " forwarded for {$context['forwarded_for']}";
235 }
236 if ( isset( $context['client_ip'] ) ) {
237 $forward .= " client IP {$context['client_ip']}";
238 }
239 if ( isset( $context['from'] ) ) {
240 $forward .= " from {$context['from']}";
241 }
242 if ( $forward ) {
243 $forward = "\t(proxied via {$context['proxy']}{$forward})";
244 }
245 if ( $context['anon'] ) {
246 $forward .= ' anon';
247 }
248 if ( !isset( $context['url'] ) ) {
249 $context['url'] = 'n/a';
250 }
251
252 $log = sprintf( "%s\t%04.3f\t%s%s\n",
253 gmdate( 'YmdHis' ), $context['elapsed'], $context['url'], $forward );
254
256 $channel, $log . $context['output'], $context );
257
258 } elseif ( !isset( $wgDebugLogGroups[$channel] ) ) {
259 $text = self::formatAsWfDebug(
260 $channel, "[{$channel}] {$message}", $context );
261
262 } else {
263 // Default formatting is wfDebugLog's historic style
264 $text = self::formatAsWfDebugLog( $channel, $message, $context );
265 }
266
267 // Append stacktrace of exception if available
268 if ( $wgLogExceptionBacktrace && isset( $context['exception'] ) ) {
269 $e = $context['exception'];
270 $backtrace = false;
271
272 if ( $e instanceof Exception ) {
274
275 } elseif ( is_array( $e ) && isset( $e['trace'] ) ) {
276 // Exception has already been unpacked as structured data
277 $backtrace = $e['trace'];
278 }
279
280 if ( $backtrace ) {
281 $text .= MWExceptionHandler::prettyPrintTrace( $backtrace ) .
282 "\n";
283 }
284 }
285
286 return self::interpolate( $text, $context );
287 }
288
297 protected static function formatAsWfDebug( $channel, $message, $context ) {
298 $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $message );
299 if ( isset( $context['seconds_elapsed'] ) ) {
300 // Prepend elapsed request time and real memory usage with two
301 // trailing spaces.
302 $text = "{$context['seconds_elapsed']} {$context['memory_used']} {$text}";
303 }
304 if ( isset( $context['prefix'] ) ) {
305 $text = "{$context['prefix']}{$text}";
306 }
307 return "{$text}\n";
308 }
309
318 protected static function formatAsWfLogDBError( $channel, $message, $context ) {
319 global $wgDBerrorLogTZ;
320 static $cachedTimezone = null;
321
322 if ( !$cachedTimezone ) {
323 $cachedTimezone = new DateTimeZone( $wgDBerrorLogTZ );
324 }
325
326 $d = date_create( 'now', $cachedTimezone );
327 $date = $d->format( 'D M j G:i:s T Y' );
328
329 $host = wfHostname();
331
332 $text = "{$date}\t{$host}\t{$wiki}\t{$message}\n";
333 return $text;
334 }
335
344 protected static function formatAsWfDebugLog( $channel, $message, $context ) {
345 $time = wfTimestamp( TS_DB );
347 $host = wfHostname();
348 $text = "{$time} {$host} {$wiki}: {$message}\n";
349 return $text;
350 }
351
359 public static function interpolate( $message, array $context ) {
360 if ( strpos( $message, '{' ) !== false ) {
361 $replace = [];
362 foreach ( $context as $key => $val ) {
363 $replace['{' . $key . '}'] = self::flatten( $val );
364 }
365 $message = strtr( $message, $replace );
366 }
367 return $message;
368 }
369
377 protected static function flatten( $item ) {
378 if ( $item === null ) {
379 return '[Null]';
380 }
381
382 if ( is_bool( $item ) ) {
383 return $item ? 'true' : 'false';
384 }
385
386 if ( is_float( $item ) ) {
387 if ( is_infinite( $item ) ) {
388 return ( $item > 0 ? '' : '-' ) . 'INF';
389 }
390 if ( is_nan( $item ) ) {
391 return 'NaN';
392 }
393 return (string)$item;
394 }
395
396 if ( is_scalar( $item ) ) {
397 return (string)$item;
398 }
399
400 if ( is_array( $item ) ) {
401 return '[Array(' . count( $item ) . ')]';
402 }
403
404 if ( $item instanceof \DateTime ) {
405 return $item->format( 'c' );
406 }
407
408 if ( $item instanceof Exception ) {
409 return '[Exception ' . get_class( $item ) . '( ' .
410 $item->getFile() . ':' . $item->getLine() . ') ' .
411 $item->getMessage() . ']';
412 }
413
414 if ( is_object( $item ) ) {
415 if ( method_exists( $item, '__toString' ) ) {
416 return (string)$item;
417 }
418
419 return '[Object ' . get_class( $item ) . ']';
420 }
421
422 if ( is_resource( $item ) ) {
423 return '[Resource ' . get_resource_type( $item ) . ']';
424 }
425
426 return '[Unknown ' . gettype( $item ) . ']';
427 }
428
439 protected static function destination( $channel, $message, $context ) {
441
442 // Default destination is the debug log file as historically used by
443 // the wfDebug function.
444 $destination = $wgDebugLogFile;
445
446 if ( isset( $context['destination'] ) ) {
447 // Use destination explicitly provided in context
448 $destination = $context['destination'];
449
450 } elseif ( $channel === 'wfDebug' ) {
451 $destination = $wgDebugLogFile;
452
453 } elseif ( $channel === 'wfLogDBError' ) {
454 $destination = $wgDBerrorLog;
455
456 } elseif ( isset( $wgDebugLogGroups[$channel] ) ) {
457 $logConfig = $wgDebugLogGroups[$channel];
458
459 if ( is_array( $logConfig ) ) {
460 $destination = $logConfig['destination'];
461 } else {
462 $destination = strval( $logConfig );
463 }
464 }
465
466 return $destination;
467 }
468
478 public static function emit( $text, $file ) {
479 if ( substr( $file, 0, 4 ) == 'udp:' ) {
480 $transport = UDPTransport::newFromString( $file );
481 $transport->emit( $text );
482 } else {
483 \Wikimedia\suppressWarnings();
484 $exists = file_exists( $file );
485 $size = $exists ? filesize( $file ) : false;
486 if ( !$exists ||
487 ( $size !== false && $size + strlen( $text ) < 0x7fffffff )
488 ) {
489 file_put_contents( $file, $text, FILE_APPEND );
490 }
491 \Wikimedia\restoreWarnings();
492 }
493 }
494
495}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgLogExceptionBacktrace
If true, send the exception backtrace to the error log.
$wgDBerrorLogTZ
Timezone to use in the error log.
$wgDBerrorLog
File to log database errors to.
$wgDebugLogGroups
Map of string log group names to log destinations.
$wgDebugLogFile
Filename for debug logging.
wfHostname()
Fetch server name for use in error reporting etc.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
New debugger system that outputs a toolbar on page view.
Definition MWDebug.php:33
static query( $sql, $function, $isMaster, $runTime)
Begins profiling on a database query.
Definition MWDebug.php:354
static debugMsg( $str, $context=[])
This is a method to pass messages from wfDebug to the pretty debugger.
Definition MWDebug.php:323
Handler class for MWExceptions.
static prettyPrintTrace(array $trace, $pad='')
Generate a string representation of a stacktrace.
static getRedactedTrace( $e)
Return a copy of an exception's backtrace as an array.
PSR-3 logger that mimics the historic implementation of MediaWiki's former wfErrorLog logging impleme...
static flatten( $item)
Convert a logging context element to a string suitable for interpolation.
static formatAsWfDebugLog( $channel, $message, $context)
Format a message as `wfDebugLog() would have formatted it.
static shouldEmit( $channel, $message, $level, $context)
Determine if the given message should be emitted or not.
log( $level, $message, array $context=[])
Logs with an arbitrary level.
static formatAsWfLogDBError( $channel, $message, $context)
Format a message as wfLogDBError() would have formatted it.
static interpolate( $message, array $context)
Interpolate placeholders in logging message.
static destination( $channel, $message, $context)
Select the appropriate log output destination for the given log event.
static emit( $text, $file)
Log to a file without getting "file size exceeded" signals.
static format( $channel, $message, $context)
Format a message.
static formatAsWfDebug( $channel, $message, $context)
Format a message as wfDebug() would have formatted it.
static $levelMapping
Convert \Psr\Log\LogLevel constants into int for sane comparisons These are the same values that Monl...
A generic class to send a message over UDP.
static newFromString( $info)
Helper tools for dealing with other locally-hosted wikis.
Definition WikiMap.php:29
static getWikiIdFromDbDomain( $domain)
Get the wiki ID of a database domain.
Definition WikiMap.php:259
static getCurrentWikiDbDomain()
Definition WikiMap.php:292
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition hooks.txt:1802
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition hooks.txt:2848
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition hooks.txt:2004
returning false will NOT prevent logging $e
Definition hooks.txt:2175
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Definition router.php:42