From: Bryan Davis Date: Mon, 23 Mar 2015 00:53:24 +0000 (-0600) Subject: Move MWLogger classes to MediaWiki\Logger namespace X-Git-Tag: 1.31.0-rc.0~11851 X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/categories/modifier.php?a=commitdiff_plain;h=1195e11a8a088ff35f506983c0496cb8af595e54;p=lhc%2Fweb%2Fwiklou.git Move MWLogger classes to MediaWiki\Logger namespace Move the MWLogger PSR-3 logging related classes into the MediaWiki\Logger namespace. Create shim classes to ease migration of existing MWLoggerFactory usage to the namespaced classes. Bug: T93406 Change-Id: I359cc81fbd2dcf8937742311dcc7d3dee08747b0 --- diff --git a/RELEASE-NOTES-1.25 b/RELEASE-NOTES-1.25 index 7b035acc89..1ec8b930b4 100644 --- a/RELEASE-NOTES-1.25 +++ b/RELEASE-NOTES-1.25 @@ -132,7 +132,8 @@ production. * The following libraries are now required: ** psr/log This library provides the interfaces set by the PSR-3 standard (http://www.php-fig.org/psr/psr-3/) - which are used by MediaWiki internally via the MWLoggerFactory class. + which are used by MediaWiki internally via the + MediaWiki\Logger\LoggerFactory class. See the structured logging RfC (https://www.mediawiki.org/wiki/Requests_for_comment/Structured_logging) for more background information. ** cssjanus/cssjanus diff --git a/api.php b/api.php index 7788a36c2f..92127474fc 100644 --- a/api.php +++ b/api.php @@ -30,6 +30,8 @@ * @file */ +use MediaWiki\Logger\LegacyLogger; + // So extensions (and other code) can check whether they're running in API mode define( 'MW_API', true ); @@ -124,7 +126,7 @@ if ( $wgAPIRequestLog ) { } else { $items[] = "failed in ApiBeforeMain"; } - MWLoggerLegacyLogger::emit( implode( ',', $items ) . "\n", $wgAPIRequestLog ); + LegacyLogger::emit( implode( ',', $items ) . "\n", $wgAPIRequestLog ); wfDebug( "Logged API request to $wgAPIRequestLog\n" ); } diff --git a/autoload.php b/autoload.php index 33967381f3..d646a0e8bb 100644 --- a/autoload.php +++ b/autoload.php @@ -694,17 +694,17 @@ $wgAutoloadLocalClasses = array( 'MWFunction' => __DIR__ . '/includes/utils/MWFunction.php', 'MWHookException' => __DIR__ . '/includes/Hooks.php', 'MWHttpRequest' => __DIR__ . '/includes/HttpFunctions.php', - 'MWLogger' => __DIR__ . '/includes/debug/logger/Logger.php', - 'MWLoggerFactory' => __DIR__ . '/includes/debug/logger/Factory.php', - 'MWLoggerLegacyLogger' => __DIR__ . '/includes/debug/logger/legacy/Logger.php', - 'MWLoggerLegacySpi' => __DIR__ . '/includes/debug/logger/legacy/Spi.php', - 'MWLoggerMonologHandler' => __DIR__ . '/includes/debug/logger/monolog/Handler.php', - 'MWLoggerMonologLegacyFormatter' => __DIR__ . '/includes/debug/logger/monolog/LegacyFormatter.php', - 'MWLoggerMonologProcessor' => __DIR__ . '/includes/debug/logger/monolog/Processor.php', - 'MWLoggerMonologSpi' => __DIR__ . '/includes/debug/logger/monolog/Spi.php', - 'MWLoggerMonologSyslogHandler' => __DIR__ . '/includes/debug/logger/monolog/SyslogHandler.php', - 'MWLoggerNullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php', - 'MWLoggerSpi' => __DIR__ . '/includes/debug/logger/Spi.php', + 'MWLogger' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerFactory' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerLegacyLogger' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerLegacySpi' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerMonologHandler' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerMonologLegacyFormatter' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerMonologProcessor' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerMonologSpi' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerMonologSyslogHandler' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerNullSpi' => __DIR__ . '/includes/debug/logger/Shims.php', + 'MWLoggerSpi' => __DIR__ . '/includes/debug/logger/Shims.php', 'MWMemcached' => __DIR__ . '/includes/objectcache/MemcachedClient.php', 'MWMessagePack' => __DIR__ . '/includes/libs/MWMessagePack.php', 'MWNamespace' => __DIR__ . '/includes/MWNamespace.php', @@ -737,6 +737,16 @@ $wgAutoloadLocalClasses = array( 'MediaWikiSite' => __DIR__ . '/includes/site/MediaWikiSite.php', 'MediaWikiTitleCodec' => __DIR__ . '/includes/title/MediaWikiTitleCodec.php', 'MediaWikiVersionFetcher' => __DIR__ . '/includes/MediaWikiVersionFetcher.php', + 'MediaWiki\\Logger\\LegacyLogger' => __DIR__ . '/includes/debug/logger/LegacyLogger.php', + 'MediaWiki\\Logger\\LegacySpi' => __DIR__ . '/includes/debug/logger/LegacySpi.php', + 'MediaWiki\\Logger\\LoggerFactory' => __DIR__ . '/includes/debug/logger/LoggerFactory.php', + 'MediaWiki\\Logger\\MonologSpi' => __DIR__ . '/includes/debug/logger/MonologSpi.php', + 'MediaWiki\\Logger\\Monolog\\LegacyFormatter' => __DIR__ . '/includes/debug/logger/monolog/LegacyFormatter.php', + 'MediaWiki\\Logger\\Monolog\\LegacyHandler' => __DIR__ . '/includes/debug/logger/monolog/LegacyHandler.php', + 'MediaWiki\\Logger\\Monolog\\SyslogHandler' => __DIR__ . '/includes/debug/logger/monolog/SyslogHandler.php', + 'MediaWiki\\Logger\\Monolog\\WikiProcessor' => __DIR__ . '/includes/debug/logger/monolog/WikiProcessor.php', + 'MediaWiki\\Logger\\NullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php', + 'MediaWiki\\Logger\\Spi' => __DIR__ . '/includes/debug/logger/Spi.php', 'MemCachedClientforWiki' => __DIR__ . '/includes/objectcache/MemcachedClient.php', 'MemcLockManager' => __DIR__ . '/includes/filebackend/lockmanager/MemcLockManager.php', 'MemcachedBagOStuff' => __DIR__ . '/includes/objectcache/MemcachedBagOStuff.php', diff --git a/docs/logger.txt b/docs/logger.txt new file mode 100644 index 0000000000..89e620aff0 --- /dev/null +++ b/docs/logger.txt @@ -0,0 +1,71 @@ +MediaWiki\Logger\LoggerFactory implements a PSR-3 [0] compatible message +logging system. + +Named Psr\Log\LoggerInterface instances can be obtained from the +MediaWiki\Logger\LoggerFactory::getInstance() static method. +MediaWiki\Logger\LoggerFactory expects a class implementing the +MediaWiki\Logger\Spi interface to act as a factory for new +Psr\Log\LoggerInterface instances. + +The "Spi" in MediaWiki\Logger\Spi stands for "service provider interface". An +SPI is an API intended to be implemented or extended by a third party. This +software design pattern is intended to enable framework extension and +replaceable components. It is specifically used in the +MediaWiki\Logger\LoggerFactory service to allow alternate PSR-3 logging +implementations to be easily integrated with MediaWiki. + +The service provider interface allows the backend logging library to be +implemented in multiple ways. The $wgMWLoggerDefaultSpi global provides the +classname of the default MediaWiki\Logger\Spi implementation to be loaded at +runtime. This can either be the name of a class implementing the +MediaWiki\Logger\Spi with a zero argument constructor or a callable that will +return an MediaWiki\Logger\Spi instance. Alternately the +MediaWiki\Logger\LoggerFactory::registerProvider() static method can be called +to inject an MediaWiki\Logger\Spi instance into the LoggerFactory and bypass +the use of the default configuration variable. + +The MediaWiki\Logger\LegacySpi class implements a service provider to generate +MediaWiki\Logger\LegacyLogger instances. The MediaWiki\Logger\LegacyLogger +class implements the PSR-3 logger interface and provides output and +configuration equivalent to the historic logging output of wfDebug, +wfDebugLog, wfLogDBError and wfErrorLog. The MediaWiki\Logger\LegacySpi class +is the default service provider configured in DefaultSettings.php. It's usage +should be transparent for users who are not ready or do not wish to switch to +a alternate logging platform. + +The MediaWiki\Logger\MonologSpi class implements a service provider to +generate Psr\Log\LoggerInterface instances that use the Monolog [1] logging +library. See the PHP docs (or source) for MediaWiki\Logger\MonologSpi for +details on the configuration of this provider. The default configuration +installs a null handler that will silently discard all logging events. The +documentation provided by the class describes a more feature rich logging +configuration. + +== Classes == +; MediaWiki\Logger\LoggerFactory +: Factory for Psr\Log\LoggerInterface loggers +; MediaWiki\Logger\Spi +: Service provider interface for MediaWiki\Logger\LoggerFactory +; MediaWiki\Logger\NullSpi +: MediaWiki\Logger\Spi for creating instances that discard all log events +; MediaWiki\Logger\LegacySpi +: Service provider for creating MediaWiki\Logger\LegacyLogger instances +; MediaWiki\Logger\LegacyLogger +: PSR-3 logger that mimics the historical output and configuration of wfDebug, + wfErrorLog and other global logging functions. +; MediaWiki\Logger\MonologSpi +: MediaWiki\Logger\Spi for creating instances backed by the monolog logging library +; MediaWiki\Logger\Monolog\LegacyHandler +: Monolog handler that replicates the udp2log and file logging + functionality of wfErrorLog() +; MediaWiki\Logger\Monolog\WikiProcessor +: Monolog log processer that adds host: wfHostname() and wiki: wfWikiID() + to all records + +== Globals == +; $wgMWLoggerDefaultSpi +: Specification for creating the default service provider interface to use + with MediaWiki\Logger\LoggerFactory + +[0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md +[1]: https://github.com/Seldaek/monolog diff --git a/docs/mwlogger.txt b/docs/mwlogger.txt deleted file mode 100644 index ecc3626db0..0000000000 --- a/docs/mwlogger.txt +++ /dev/null @@ -1,71 +0,0 @@ -MWLoggerFactory implements a PSR-3 [0] compatible message logging system. - -Named Psr\Log\LoggerInterface instances can be obtained from the -MWLoggerFactory::getInstance() static method. MWLoggerFactory expects a class -implementing the MWLoggerSpi interface to act as a factory for new -Psr\Log\LoggerInterface instances. - -The "Spi" in MWLoggerSpi stands for "service provider interface". A SPI is -an API intended to be implemented or extended by a third party. This software -design pattern is intended to enable framework extension and replaceable -components. It is specifically used in the MWLoggerFactory service to allow -alternate PSR-3 logging implementations to be easily integrated with -MediaWiki. - -The MWLoggerFactory::getInstance() static method is the means by which most -code acquires a Psr\Log\LoggerInterface instance. This in turn delegates -creation of Psr\Log\LoggerInterface instances to a class implementing the -MWLoggerSpi service provider interface. - -The service provider interface allows the backend logging library to be -implemented in multiple ways. The $wgMWLoggerDefaultSpi global provides the -classname of the default MWLoggerSpi implementation to be loaded at runtime. -This can either be the name of a class implementing the MWLoggerSpi with -a zero argument constructor or a callable that will return an MWLoggerSpi -instance. Alternately the MWLoggerFactory::registerProvider method can be -called to inject an MWLoggerSpi instance into MWLoggerFactory and bypass the -use of this configuration variable. - -The MWLoggerLegacySpi class implements a service provider to generate -MWLoggerLegacyLogger instances. The MWLoggerLegacyLogger class implements the -PSR-3 logger interface and provides output and configuration equivalent to the -historic logging output of wfDebug, wfDebugLog, wfLogDBError and wfErrorLog. -The MWLoggerLegacySpi class is the default service provider configured in -DefaultSettings.php. It's usage should be transparent for users who are not -ready or do not wish to switch to a alternate logging platform. - -The MWLoggerMonologSpi class implements a service provider to generate -Psr\Log\LoggerInterface instances that use the Monolog [1] logging library. -See the PHP docs (or source) for MWLoggerMonologSpi for details on the -configuration of this provider. The default configuration installs a null -handler that will silently discard all logging events. The documentation -provided by the class describes a more feature rich logging configuration. - -== Classes == -; MWLoggerFactory -: Factory for Psr\Log\LoggerInterface loggers -; MWLoggerSpi -: Service provider interface for MWLoggerFactory -; MWLoggerNullSpi -: MWLoggerSpi for creating instances that discard all log events -; MWLoggerLegacySpi -: Service provider for creating MWLoggerLegacyLogger instances -; MWLoggerLegacyLogger -: PSR-3 logger that mimics the historical output and configuration of wfDebug, - wfErrorLog and other global logging functions. -; MWLoggerMonologSpi -: MWLoggerSpi for creating instances backed by the monolog logging library -; MwLoggerMonologHandler -: Monolog handler that replicates the udp2log and file logging - functionality of wfErrorLog() -; MwLoggerMonologProcessor -: Monolog log processer that adds host: wfHostname() and wiki: wfWikiID() - to all records - -== Globals == -; $wgMWLoggerDefaultSpi -: Specification for creating the default service provider interface to use - with MWLoggerFactory - -[0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md -[1]: https://github.com/Seldaek/monolog diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 845c7f51ce..a5d66059ec 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -5303,16 +5303,16 @@ $wgDebugLogGroups = array(); * * The value should be an array suitable for use with * ObjectFactory::getObjectFromSpec(). The created object is expected to - * implement the MWLoggerSpi interface. See ObjectFactory for additional + * implement the MediaWiki\Logger\Spi interface. See ObjectFactory for additional * details. * - * Alternately the MWLoggerFactory::registerProvider method can be called to - * inject an MWLoggerSpi instance into MWLoggerFactory and bypass the use of - * this configuration variable entirely. + * Alternately the MediaWiki\Logger\LoggerFactory::registerProvider method can + * be called to inject an MediaWiki\Logger\Spi instance into the LoggerFactory + * and bypass the use of this configuration variable entirely. * * @par To completely disable logging: * @code - * $wgMWLoggerDefaultSpi = array( 'class' => 'MWLoggerNullSpi' ); + * $wgMWLoggerDefaultSpi = array( 'class' => '\\MediaWiki\\Logger\\NullSpi' ); * @endcode * * @since 1.25 @@ -5320,7 +5320,7 @@ $wgDebugLogGroups = array(); * @see MwLogger */ $wgMWLoggerDefaultSpi = array( - 'class' => 'MWLoggerLegacySpi', + 'class' => '\\MediaWiki\\Logger\\LegacySpi', ); /** diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index b2a2e3db4f..7c4ebc27e2 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -26,6 +26,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { use Liuggio\StatsdClient\StatsdClient; use Liuggio\StatsdClient\Sender\SocketSender; +use MediaWiki\Logger\LoggerFactory; // Hide compatibility functions from Doxygen /// @cond @@ -1051,7 +1052,7 @@ function wfDebug( $text, $dest = 'all', array $context = array() ) { $context['prefix'] = $wgDebugLogPrefix; } - $logger = MWLoggerFactory::getInstance( 'wfDebug' ); + $logger = LoggerFactory::getInstance( 'wfDebug' ); $logger->debug( $text, $context ); } @@ -1151,7 +1152,7 @@ function wfDebugLog( $text = trim( $text ); - $logger = MWLoggerFactory::getInstance( $logGroup ); + $logger = LoggerFactory::getInstance( $logGroup ); $context['private'] = ( $dest === 'private' ); $logger->info( $text, $context ); } @@ -1165,7 +1166,7 @@ function wfDebugLog( * @param array $context Additional logging context data */ function wfLogDBError( $text, array $context = array() ) { - $logger = MWLoggerFactory::getInstance( 'wfLogDBError' ); + $logger = LoggerFactory::getInstance( 'wfLogDBError' ); $logger->error( trim( $text ), $context ); } @@ -1224,11 +1225,11 @@ function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) { * @param string $file Filename * @param array $context Additional logging context data * @throws MWException - * @deprecated since 1.25 Use MWLoggerLegacyLogger::emit or UDPTransport + * @deprecated since 1.25 Use MediaWiki\Logger\LegacyLogger::emit or UDPTransport */ function wfErrorLog( $text, $file, array $context = array() ) { wfDeprecated( __METHOD__, '1.25' ); - $logger = MWLoggerFactory::getInstance( 'wfErrorLog' ); + $logger = LoggerFactory::getInstance( 'wfErrorLog' ); $context['destination'] = $file; $logger->info( trim( $text ), $context ); } @@ -1303,7 +1304,7 @@ function wfLogProfilingData() { $ctx['output'] = $profiler->getOutput(); - $log = MWLoggerFactory::getInstance( 'profileoutput' ); + $log = LoggerFactory::getInstance( 'profileoutput' ); $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx ); } diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php index 3f2cb6286c..68d03c8a56 100644 --- a/includes/MediaWiki.php +++ b/includes/MediaWiki.php @@ -20,6 +20,8 @@ * @file */ +use MediaWiki\Logger\LoggerFactory; + /** * The MediaWiki class is the helper class for the index.php entry point. */ @@ -478,7 +480,7 @@ class MediaWiki { $wgTitle = $title; $trxProfiler = Profiler::instance()->getTransactionProfiler(); - $trxProfiler->setLogger( MWLoggerFactory::getInstance( 'DBPerformance' ) ); + $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) ); // Aside from rollback, master queries should not happen on GET requests. // Periodic or "in passing" updates on GET should use the job queue. @@ -618,7 +620,7 @@ class MediaWiki { $n = intval( $jobRunRate ); } - $runJobsLogger = MWLoggerFactory::getInstance( 'runJobs' ); + $runJobsLogger = LoggerFactory::getInstance( 'runJobs' ); if ( !$this->config->get( 'RunJobsAsync' ) ) { // Fall back to running the job here while the user waits diff --git a/includes/debug/logger/Factory.php b/includes/debug/logger/Factory.php deleted file mode 100644 index 2660b92adf..0000000000 --- a/includes/debug/logger/Factory.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @copyright © 2014 Bryan Davis and Wikimedia Foundation. - */ -class MWLoggerFactory { - - /** - * Service provider. - * @var MWLoggerSpi $spi - */ - private static $spi; - - - /** - * Register a service provider to create new \Psr\Log\LoggerInterface - * instances. - * - * @param MWLoggerSpi $provider Provider to register - */ - public static function registerProvider( MWLoggerSpi $provider ) { - self::$spi = $provider; - } - - - /** - * Get the registered service provider. - * - * If called before any service provider has been registered, it will - * attempt to use the $wgMWLoggerDefaultSpi global to bootstrap - * MWLoggerSpi registration. $wgMWLoggerDefaultSpi is expected to be an - * array usable by ObjectFactory::getObjectFromSpec() to create a class. - * - * @return MWLoggerSpi - * @see registerProvider() - * @see ObjectFactory::getObjectFromSpec() - */ - public static function getProvider() { - if ( self::$spi === null ) { - global $wgMWLoggerDefaultSpi; - $provider = ObjectFactory::getObjectFromSpec( - $wgMWLoggerDefaultSpi - ); - self::registerProvider( $provider ); - } - return self::$spi; - } - - - /** - * Get a named logger instance from the currently configured logger factory. - * - * @param string $channel Logger channel (name) - * @return \Psr\Log\LoggerInterface - */ - public static function getInstance( $channel ) { - if ( !interface_exists( '\Psr\Log\LoggerInterface' ) ) { - $message = <<PSR-3 logging library to be present. This library is not embedded directly in MediaWiki's git repository and must be installed separately by the end user. - -Please see mediawiki.org for help on installing the required components. -TXT; - echo $message; - trigger_error( $message, E_USER_ERROR ); - die( 1 ); - } - - return self::getProvider()->getLogger( $channel ); - } - - - /** - * Construction of utility class is not allowed. - */ - private function __construct() { - // no-op - } -} diff --git a/includes/debug/logger/LegacyLogger.php b/includes/debug/logger/LegacyLogger.php new file mode 100644 index 0000000000..9cf104aa48 --- /dev/null +++ b/includes/debug/logger/LegacyLogger.php @@ -0,0 +1,379 @@ + + * @copyright © 2014 Bryan Davis and Wikimedia Foundation. + */ +class LegacyLogger extends AbstractLogger { + + /** + * @var string $channel + */ + protected $channel; + + /** + * Convert Psr\Log\LogLevel constants into int for sane comparisons + * These are the same values that Monlog uses + * + * @var array + */ + protected static $levelMapping = array( + LogLevel::DEBUG => 100, + LogLevel::INFO => 200, + LogLevel::NOTICE => 250, + LogLevel::WARNING => 300, + LogLevel::ERROR => 400, + LogLevel::CRITICAL => 500, + LogLevel::ALERT => 550, + LogLevel::EMERGENCY => 600, + ); + + + /** + * @param string $channel + */ + public function __construct( $channel ) { + $this->channel = $channel; + } + + /** + * Logs with an arbitrary level. + * + * @param string|int $level + * @param string $message + * @param array $context + */ + public function log( $level, $message, array $context = array() ) { + if ( self::shouldEmit( $this->channel, $message, $level, $context ) ) { + $text = self::format( $this->channel, $message, $context ); + $destination = self::destination( $this->channel, $message, $context ); + self::emit( $text, $destination ); + } + // Add to debug toolbar + MWDebug::debugMsg( $message, array( 'channel' => $this->channel ) + $context ); + } + + + /** + * Determine if the given message should be emitted or not. + * + * @param string $channel + * @param string $message + * @param string|int $level Psr\Log\LogEvent constant or Monlog level int + * @param array $context + * @return bool True if message should be sent to disk/network, false + * otherwise + */ + public static function shouldEmit( $channel, $message, $level, $context ) { + global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups; + + if ( $channel === 'wfLogDBError' ) { + // wfLogDBError messages are emitted if a database log location is + // specfied. + $shouldEmit = (bool)$wgDBerrorLog; + + } elseif ( $channel === 'wfErrorLog' ) { + // All messages on the wfErrorLog channel should be emitted. + $shouldEmit = true; + + } elseif ( isset( $wgDebugLogGroups[$channel] ) ) { + $logConfig = $wgDebugLogGroups[$channel]; + + if ( is_array( $logConfig ) ) { + $shouldEmit = true; + if ( isset( $logConfig['sample'] ) ) { + // Emit randomly with a 1 in 'sample' chance for each message. + $shouldEmit = mt_rand( 1, $logConfig['sample'] ) === 1; + } + + if ( isset( $logConfig['level'] ) ) { + if ( is_string( $level ) ) { + $level = self::$levelMapping[$level]; + } + $shouldEmit = $level >= self::$levelMapping[$logConfig['level']]; + } + } else { + // Emit unless the config value is explictly false. + $shouldEmit = $logConfig !== false; + } + + } elseif ( isset( $context['private'] ) && $context['private'] ) { + // Don't emit if the message didn't match previous checks based on + // the channel and the event is marked as private. This check + // discards messages sent via wfDebugLog() with dest == 'private' + // and no explicit wgDebugLogGroups configuration. + $shouldEmit = false; + } else { + // Default return value is the same as the historic wfDebug + // method: emit if $wgDebugLogFile has been set. + $shouldEmit = $wgDebugLogFile != ''; + } + + return $shouldEmit; + } + + + /** + * Format a message. + * + * Messages to the 'wfDebug', 'wfLogDBError' and 'wfErrorLog' channels + * receive special fomatting to mimic the historic output of the functions + * of the same name. All other channel values are formatted based on the + * historic output of the `wfDebugLog()` global function. + * + * @param string $channel + * @param string $message + * @param array $context + * @return string + */ + public static function format( $channel, $message, $context ) { + global $wgDebugLogGroups; + + if ( $channel === 'wfDebug' ) { + $text = self::formatAsWfDebug( $channel, $message, $context ); + + } elseif ( $channel === 'wfLogDBError' ) { + $text = self::formatAsWfLogDBError( $channel, $message, $context ); + + } elseif ( $channel === 'wfErrorLog' ) { + $text = "{$message}\n"; + + } elseif ( $channel === 'profileoutput' ) { + // Legacy wfLogProfilingData formatitng + $forward = ''; + if ( isset( $context['forwarded_for'] )) { + $forward = " forwarded for {$context['forwarded_for']}"; + } + if ( isset( $context['client_ip'] ) ) { + $forward .= " client IP {$context['client_ip']}"; + } + if ( isset( $context['from'] ) ) { + $forward .= " from {$context['from']}"; + } + if ( $forward ) { + $forward = "\t(proxied via {$context['proxy']}{$forward})"; + } + if ( $context['anon'] ) { + $forward .= ' anon'; + } + if ( !isset( $context['url'] ) ) { + $context['url'] = 'n/a'; + } + + $log = sprintf( "%s\t%04.3f\t%s%s\n", + gmdate( 'YmdHis' ), $context['elapsed'], $context['url'], $forward ); + + $text = self::formatAsWfDebugLog( + $channel, $log . $context['output'], $context ); + + } elseif ( !isset( $wgDebugLogGroups[$channel] ) ) { + $text = self::formatAsWfDebug( + $channel, "[{$channel}] {$message}", $context ); + + } else { + // Default formatting is wfDebugLog's historic style + $text = self::formatAsWfDebugLog( $channel, $message, $context ); + } + + return self::interpolate( $text, $context ); + } + + + /** + * Format a message as `wfDebug()` would have formatted it. + * + * @param string $channel + * @param string $message + * @param array $context + * @return string + */ + protected static function formatAsWfDebug( $channel, $message, $context ) { + $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $message ); + if ( isset( $context['seconds_elapsed'] ) ) { + // Prepend elapsed request time and real memory usage with two + // trailing spaces. + $text = "{$context['seconds_elapsed']} {$context['memory_used']} {$text}"; + } + if ( isset( $context['prefix'] ) ) { + $text = "{$context['prefix']}{$text}"; + } + return "{$text}\n"; + } + + + /** + * Format a message as `wfLogDBError()` would have formatted it. + * + * @param string $channel + * @param string $message + * @param array $context + * @return string + */ + protected static function formatAsWfLogDBError( $channel, $message, $context ) { + global $wgDBerrorLogTZ; + static $cachedTimezone = null; + + if ( $wgDBerrorLogTZ && !$cachedTimezone ) { + $cachedTimezone = new DateTimeZone( $wgDBerrorLogTZ ); + } + + // Workaround for https://bugs.php.net/bug.php?id=52063 + // Can be removed when min PHP > 5.3.6 + if ( $cachedTimezone === null ) { + $d = date_create( 'now' ); + } else { + $d = date_create( 'now', $cachedTimezone ); + } + $date = $d->format( 'D M j G:i:s T Y' ); + + $host = wfHostname(); + $wiki = wfWikiID(); + + $text = "{$date}\t{$host}\t{$wiki}\t{$message}\n"; + return $text; + } + + + /** + * Format a message as `wfDebugLog() would have formatted it. + * + * @param string $channel + * @param string $message + * @param array $context + */ + protected static function formatAsWfDebugLog( $channel, $message, $context ) { + $time = wfTimestamp( TS_DB ); + $wiki = wfWikiID(); + $host = wfHostname(); + $text = "{$time} {$host} {$wiki}: {$message}\n"; + return $text; + } + + + /** + * Interpolate placeholders in logging message. + * + * @param string $message + * @param array $context + * @return string Interpolated message + */ + public static function interpolate( $message, array $context ) { + if ( strpos( $message, '{' ) !== false ) { + $replace = array(); + foreach ( $context as $key => $val ) { + $replace['{' . $key . '}'] = $val; + } + $message = strtr( $message, $replace ); + } + return $message; + } + + + /** + * Select the appropriate log output destination for the given log event. + * + * If the event context contains 'destination' + * + * @param string $channel + * @param string $message + * @param array $context + * @return string + */ + protected static function destination( $channel, $message, $context ) { + global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups; + + // Default destination is the debug log file as historically used by + // the wfDebug function. + $destination = $wgDebugLogFile; + + if ( isset( $context['destination'] ) ) { + // Use destination explicitly provided in context + $destination = $context['destination']; + + } elseif ( $channel === 'wfDebug' ) { + $destination = $wgDebugLogFile; + + } elseif ( $channel === 'wfLogDBError' ) { + $destination = $wgDBerrorLog; + + } elseif ( isset( $wgDebugLogGroups[$channel] ) ) { + $logConfig = $wgDebugLogGroups[$channel]; + + if ( is_array( $logConfig ) ) { + $destination = $logConfig['destination']; + } else { + $destination = strval( $logConfig ); + } + } + + return $destination; + } + + + /** + * Log to a file without getting "file size exceeded" signals. + * + * Can also log to UDP with the syntax udp://host:port/prefix. This will send + * lines to the specified port, prefixed by the specified prefix and a space. + * + * @param string $text + * @param string $file Filename + * @throws MWException + */ + public static function emit( $text, $file ) { + if ( substr( $file, 0, 4 ) == 'udp:' ) { + $transport = UDPTransport::newFromString( $file ); + $transport->emit( $text ); + } else { + wfSuppressWarnings(); + $exists = file_exists( $file ); + $size = $exists ? filesize( $file ) : false; + if ( !$exists || + ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) + ) { + file_put_contents( $file, $text, FILE_APPEND ); + } + wfRestoreWarnings(); + } + } + +} diff --git a/includes/debug/logger/LegacySpi.php b/includes/debug/logger/LegacySpi.php new file mode 100644 index 0000000000..1bf39e41b0 --- /dev/null +++ b/includes/debug/logger/LegacySpi.php @@ -0,0 +1,59 @@ + '\\MediaWiki\\Logger\\LegacySpi', + * ); + * @endcode + * + * @see \MediaWiki\Logger\LoggerFactory + * @since 1.25 + * @author Bryan Davis + * @copyright © 2014 Bryan Davis and Wikimedia Foundation. + */ +class LegacySpi implements Spi { + + /** + * @var array $singletons + */ + protected $singletons = array(); + + + /** + * Get a logger instance. + * + * @param string $channel Logging channel + * @return \Psr\Log\LoggerInterface Logger instance + */ + public function getLogger( $channel ) { + if ( !isset( $this->singletons[$channel] ) ) { + $this->singletons[$channel] = new LegacyLogger( $channel ); + } + return $this->singletons[$channel]; + } + +} diff --git a/includes/debug/logger/Logger.php b/includes/debug/logger/Logger.php deleted file mode 100644 index 27cf0cd073..0000000000 --- a/includes/debug/logger/Logger.php +++ /dev/null @@ -1,72 +0,0 @@ - + * @copyright © 2014 Bryan Davis and Wikimedia Foundation. + */ +class LoggerFactory { + + /** + * Service provider. + * @var Spi $spi + */ + private static $spi; + + + /** + * Register a service provider to create new \Psr\Log\LoggerInterface + * instances. + * + * @param Spi $provider Provider to register + */ + public static function registerProvider( Spi $provider ) { + self::$spi = $provider; + } + + + /** + * Get the registered service provider. + * + * If called before any service provider has been registered, it will + * attempt to use the $wgMWLoggerDefaultSpi global to bootstrap + * Spi registration. $wgMWLoggerDefaultSpi is expected to be an + * array usable by ObjectFactory::getObjectFromSpec() to create a class. + * + * @return Spi + * @see registerProvider() + * @see ObjectFactory::getObjectFromSpec() + */ + public static function getProvider() { + if ( self::$spi === null ) { + global $wgMWLoggerDefaultSpi; + $provider = ObjectFactory::getObjectFromSpec( + $wgMWLoggerDefaultSpi + ); + self::registerProvider( $provider ); + } + return self::$spi; + } + + + /** + * Get a named logger instance from the currently configured logger factory. + * + * @param string $channel Logger channel (name) + * @return \Psr\Log\LoggerInterface + */ + public static function getInstance( $channel ) { + if ( !interface_exists( '\Psr\Log\LoggerInterface' ) ) { + $message = ( + 'MediaWiki requires the PSR-3 logging ' . + "library to be present. This library is not embedded directly in MediaWiki's " . + "git repository and must be installed separately by the end user.\n\n" . + 'Please see mediawiki.org for help on installing ' . + 'the required components.' + ); + echo $message; + trigger_error( $message, E_USER_ERROR ); + die( 1 ); + } + + return self::getProvider()->getLogger( $channel ); + } + + + /** + * Construction of utility class is not allowed. + */ + private function __construct() { + // no-op + } +} diff --git a/includes/debug/logger/MonologSpi.php b/includes/debug/logger/MonologSpi.php new file mode 100644 index 0000000000..a07fdc4a8b --- /dev/null +++ b/includes/debug/logger/MonologSpi.php @@ -0,0 +1,251 @@ + '\\MediaWiki\\Logger\\MonologSpi', + * 'args' => array( array( + * 'loggers' => array( + * '@default' => array( + * 'processors' => array( 'wiki', 'psr', 'pid', 'uid', 'web' ), + * 'handlers' => array( 'stream' ), + * ), + * 'runJobs' => array( + * 'processors' => array( 'wiki', 'psr', 'pid' ), + * 'handlers' => array( 'stream' ), + * ) + * ), + * 'processors' => array( + * 'wiki' => array( + * 'class' => '\\MediaWiki\\Logger\\Monolog\\WikiProcessor', + * ), + * 'psr' => array( + * 'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor', + * ), + * 'pid' => array( + * 'class' => '\\Monolog\\Processor\\ProcessIdProcessor', + * ), + * 'uid' => array( + * 'class' => '\\Monolog\\Processor\\UidProcessor', + * ), + * 'web' => array( + * 'class' => '\\Monolog\\Processor\\WebProcessor', + * ), + * ), + * 'handlers' => array( + * 'stream' => array( + * 'class' => '\\Monolog\\Handler\\StreamHandler', + * 'args' => array( 'path/to/your.log' ), + * 'formatter' => 'line', + * ), + * 'redis' => array( + * 'class' => '\\Monolog\\Handler\\RedisHandler', + * 'args' => array( function() { + * $redis = new Redis(); + * $redis->connect( '127.0.0.1', 6379 ); + * return $redis; + * }, + * 'logstash' + * ), + * 'formatter' => 'logstash', + * ), + * 'udp2log' => array( + * 'class' => '\\MediaWiki\\Logger\\Monolog\\LegacyHandler', + * 'args' => array( + * 'udp://127.0.0.1:8420/mediawiki + * ), + * 'formatter' => 'line', + * ), + * ), + * 'formatters' => array( + * 'line' => array( + * 'class' => '\\Monolog\\Formatter\\LineFormatter', + * ), + * 'logstash' => array( + * 'class' => '\\Monolog\\Formatter\\LogstashFormatter', + * 'args' => array( 'mediawiki', php_uname( 'n' ), null, '', 1 ), + * ), + * ), + * ) ), + * ); + * @endcode + * + * @see https://github.com/Seldaek/monolog + * @since 1.25 + * @author Bryan Davis + * @copyright © 2014 Bryan Davis and Wikimedia Foundation. + */ +class MonologSpi implements Spi { + + /** + * @var array $singletons + */ + protected $singletons; + + /** + * Configuration for creating new loggers. + * @var array $config + */ + protected $config; + + + /** + * @param array $config Configuration data. + */ + public function __construct( array $config ) { + $this->config = $config; + $this->reset(); + } + + + /** + * Reset internal caches. + * + * This is public for use in unit tests. Under normal operation there should + * be no need to flush the caches. + */ + public function reset() { + $this->singletons = array( + 'loggers' => array(), + 'handlers' => array(), + 'formatters' => array(), + 'processors' => array(), + ); + } + + + /** + * Get a logger instance. + * + * Creates and caches a logger instance based on configuration found in the + * $wgMWLoggerMonologSpiConfig global. Subsequent request for the same channel + * name will return the cached instance. + * + * @param string $channel Logging channel + * @return \Psr\Log\LoggerInterface Logger instance + */ + public function getLogger( $channel ) { + if ( !isset( $this->singletons['loggers'][$channel] ) ) { + // Fallback to using the '@default' configuration if an explict + // configuration for the requested channel isn't found. + $spec = isset( $this->config['loggers'][$channel] ) ? + $this->config['loggers'][$channel] : + $this->config['loggers']['@default']; + + $monolog = $this->createLogger( $channel, $spec ); + $this->singletons['loggers'][$channel] = $monolog; + } + + return $this->singletons['loggers'][$channel]; + } + + + /** + * Create a logger. + * @param string $channel Logger channel + * @param array $spec Configuration + * @return \Monolog\Logger + */ + protected function createLogger( $channel, $spec ) { + $obj = new Logger( $channel ); + + if ( isset( $spec['processors'] ) ) { + foreach ( $spec['processors'] as $processor ) { + $obj->pushProcessor( $this->getProcessor( $processor ) ); + } + } + + if ( isset( $spec['handlers'] ) ) { + foreach ( $spec['handlers'] as $handler ) { + $obj->pushHandler( $this->getHandler( $handler ) ); + } + } + return $obj; + } + + + /** + * Create or return cached processor. + * @param string $name Processor name + * @return callable + */ + public function getProcessor( $name ) { + if ( !isset( $this->singletons['processors'][$name] ) ) { + $spec = $this->config['processors'][$name]; + $processor = ObjectFactory::getObjectFromSpec( $spec ); + $this->singletons['processors'][$name] = $processor; + } + return $this->singletons['processors'][$name]; + } + + + /** + * Create or return cached handler. + * @param string $name Processor name + * @return \Monolog\Handler\HandlerInterface + */ + public function getHandler( $name ) { + if ( !isset( $this->singletons['handlers'][$name] ) ) { + $spec = $this->config['handlers'][$name]; + $handler = ObjectFactory::getObjectFromSpec( $spec ); + if ( isset( $spec['formatter'] ) ) { + $handler->setFormatter( + $this->getFormatter( $spec['formatter'] ) + ); + } + $this->singletons['handlers'][$name] = $handler; + } + return $this->singletons['handlers'][$name]; + } + + + /** + * Create or return cached formatter. + * @param string $name Formatter name + * @return \Monolog\Formatter\FormatterInterface + */ + public function getFormatter( $name ) { + if ( !isset( $this->singletons['formatters'][$name] ) ) { + $spec = $this->config['formatters'][$name]; + $formatter = ObjectFactory::getObjectFromSpec( $spec ); + $this->singletons['formatters'][$name] = $formatter; + } + return $this->singletons['formatters'][$name]; + } +} diff --git a/includes/debug/logger/NullSpi.php b/includes/debug/logger/NullSpi.php index 617842cc8f..a82d2c4c22 100644 --- a/includes/debug/logger/NullSpi.php +++ b/includes/debug/logger/NullSpi.php @@ -18,24 +18,27 @@ * @file */ +namespace MediaWiki\Logger; + +use Psr\Log\NullLogger; /** - * MWLoggerFactory service provider that creates \Psr\Log\NullLogger + * LoggerFactory service provider that creates \Psr\Log\NullLogger * instances. A NullLogger silently discards all log events sent to it. * * Usage: * @code * $wgMWLoggerDefaultSpi = array( - * 'class' => 'MWLoggerNullSpi', + * 'class' => '\\MediaWiki\\Logger\\NullSpi', * ); * @endcode * - * @see MWLoggerFactory + * @see \MediaWiki\Logger\LoggerFactory * @since 1.25 * @author Bryan Davis * @copyright © 2014 Bryan Davis and Wikimedia Foundation. */ -class MWLoggerNullSpi implements MWLoggerSpi { +class NullSpi implements Spi { /** * @var \Psr\Log\NullLogger $singleton @@ -44,7 +47,7 @@ class MWLoggerNullSpi implements MWLoggerSpi { public function __construct() { - $this->singleton = new \Psr\Log\NullLogger(); + $this->singleton = new NullLogger(); } diff --git a/includes/debug/logger/Shims.php b/includes/debug/logger/Shims.php new file mode 100644 index 0000000000..c78b0dce4d --- /dev/null +++ b/includes/debug/logger/Shims.php @@ -0,0 +1,171 @@ + * @copyright © 2014 Bryan Davis and Wikimedia Foundation. */ -interface MWLoggerSpi { +interface Spi { /** * Get a logger instance. diff --git a/includes/debug/logger/legacy/Logger.php b/includes/debug/logger/legacy/Logger.php deleted file mode 100644 index 610635db87..0000000000 --- a/includes/debug/logger/legacy/Logger.php +++ /dev/null @@ -1,375 +0,0 @@ - - * @copyright © 2014 Bryan Davis and Wikimedia Foundation. - */ -use Psr\Log\AbstractLogger; -use Psr\Log\LogLevel; - -class MWLoggerLegacyLogger extends AbstractLogger { - - /** - * @var string $channel - */ - protected $channel; - - /** - * Convert Psr\Log\LogLevel constants into int for sane comparisons - * These are the same values that Monlog uses - * - * @var array - */ - protected static $levelMapping = array( - LogLevel::DEBUG => 100, - LogLevel::INFO => 200, - LogLevel::NOTICE => 250, - LogLevel::WARNING => 300, - LogLevel::ERROR => 400, - LogLevel::CRITICAL => 500, - LogLevel::ALERT => 550, - LogLevel::EMERGENCY => 600, - ); - - - /** - * @param string $channel - */ - public function __construct( $channel ) { - $this->channel = $channel; - } - - /** - * Logs with an arbitrary level. - * - * @param string|int $level - * @param string $message - * @param array $context - */ - public function log( $level, $message, array $context = array() ) { - if ( self::shouldEmit( $this->channel, $message, $level, $context ) ) { - $text = self::format( $this->channel, $message, $context ); - $destination = self::destination( $this->channel, $message, $context ); - self::emit( $text, $destination ); - } - // Add to debug toolbar - MWDebug::debugMsg( $message, array( 'channel' => $this->channel ) + $context ); - } - - - /** - * Determine if the given message should be emitted or not. - * - * @param string $channel - * @param string $message - * @param string|int $level Psr\Log\LogEvent constant or Monlog level int - * @param array $context - * @return bool True if message should be sent to disk/network, false - * otherwise - */ - public static function shouldEmit( $channel, $message, $level, $context ) { - global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups; - - if ( $channel === 'wfLogDBError' ) { - // wfLogDBError messages are emitted if a database log location is - // specfied. - $shouldEmit = (bool)$wgDBerrorLog; - - } elseif ( $channel === 'wfErrorLog' ) { - // All messages on the wfErrorLog channel should be emitted. - $shouldEmit = true; - - } elseif ( isset( $wgDebugLogGroups[$channel] ) ) { - $logConfig = $wgDebugLogGroups[$channel]; - - if ( is_array( $logConfig ) ) { - $shouldEmit = true; - if ( isset( $logConfig['sample'] ) ) { - // Emit randomly with a 1 in 'sample' chance for each message. - $shouldEmit = mt_rand( 1, $logConfig['sample'] ) === 1; - } - - if ( isset( $logConfig['level'] ) ) { - if ( is_string( $level ) ) { - $level = self::$levelMapping[$level]; - } - $shouldEmit = $level >= self::$levelMapping[$logConfig['level']]; - } - } else { - // Emit unless the config value is explictly false. - $shouldEmit = $logConfig !== false; - } - - } elseif ( isset( $context['private'] ) && $context['private'] ) { - // Don't emit if the message didn't match previous checks based on - // the channel and the event is marked as private. This check - // discards messages sent via wfDebugLog() with dest == 'private' - // and no explicit wgDebugLogGroups configuration. - $shouldEmit = false; - } else { - // Default return value is the same as the historic wfDebug - // method: emit if $wgDebugLogFile has been set. - $shouldEmit = $wgDebugLogFile != ''; - } - - return $shouldEmit; - } - - - /** - * Format a message. - * - * Messages to the 'wfDebug', 'wfLogDBError' and 'wfErrorLog' channels - * receive special fomatting to mimic the historic output of the functions - * of the same name. All other channel values are formatted based on the - * historic output of the `wfDebugLog()` global function. - * - * @param string $channel - * @param string $message - * @param array $context - * @return string - */ - public static function format( $channel, $message, $context ) { - global $wgDebugLogGroups; - - if ( $channel === 'wfDebug' ) { - $text = self::formatAsWfDebug( $channel, $message, $context ); - - } elseif ( $channel === 'wfLogDBError' ) { - $text = self::formatAsWfLogDBError( $channel, $message, $context ); - - } elseif ( $channel === 'wfErrorLog' ) { - $text = "{$message}\n"; - - } elseif ( $channel === 'profileoutput' ) { - // Legacy wfLogProfilingData formatitng - $forward = ''; - if ( isset( $context['forwarded_for'] )) { - $forward = " forwarded for {$context['forwarded_for']}"; - } - if ( isset( $context['client_ip'] ) ) { - $forward .= " client IP {$context['client_ip']}"; - } - if ( isset( $context['from'] ) ) { - $forward .= " from {$context['from']}"; - } - if ( $forward ) { - $forward = "\t(proxied via {$context['proxy']}{$forward})"; - } - if ( $context['anon'] ) { - $forward .= ' anon'; - } - if ( !isset( $context['url'] ) ) { - $context['url'] = 'n/a'; - } - - $log = sprintf( "%s\t%04.3f\t%s%s\n", - gmdate( 'YmdHis' ), $context['elapsed'], $context['url'], $forward ); - - $text = self::formatAsWfDebugLog( - $channel, $log . $context['output'], $context ); - - } elseif ( !isset( $wgDebugLogGroups[$channel] ) ) { - $text = self::formatAsWfDebug( - $channel, "[{$channel}] {$message}", $context ); - - } else { - // Default formatting is wfDebugLog's historic style - $text = self::formatAsWfDebugLog( $channel, $message, $context ); - } - - return self::interpolate( $text, $context ); - } - - - /** - * Format a message as `wfDebug()` would have formatted it. - * - * @param string $channel - * @param string $message - * @param array $context - * @return string - */ - protected static function formatAsWfDebug( $channel, $message, $context ) { - $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $message ); - if ( isset( $context['seconds_elapsed'] ) ) { - // Prepend elapsed request time and real memory usage with two - // trailing spaces. - $text = "{$context['seconds_elapsed']} {$context['memory_used']} {$text}"; - } - if ( isset( $context['prefix'] ) ) { - $text = "{$context['prefix']}{$text}"; - } - return "{$text}\n"; - } - - - /** - * Format a message as `wfLogDBError()` would have formatted it. - * - * @param string $channel - * @param string $message - * @param array $context - * @return string - */ - protected static function formatAsWfLogDBError( $channel, $message, $context ) { - global $wgDBerrorLogTZ; - static $cachedTimezone = null; - - if ( $wgDBerrorLogTZ && !$cachedTimezone ) { - $cachedTimezone = new DateTimeZone( $wgDBerrorLogTZ ); - } - - // Workaround for https://bugs.php.net/bug.php?id=52063 - // Can be removed when min PHP > 5.3.6 - if ( $cachedTimezone === null ) { - $d = date_create( 'now' ); - } else { - $d = date_create( 'now', $cachedTimezone ); - } - $date = $d->format( 'D M j G:i:s T Y' ); - - $host = wfHostname(); - $wiki = wfWikiID(); - - $text = "{$date}\t{$host}\t{$wiki}\t{$message}\n"; - return $text; - } - - - /** - * Format a message as `wfDebugLog() would have formatted it. - * - * @param string $channel - * @param string $message - * @param array $context - */ - protected static function formatAsWfDebugLog( $channel, $message, $context ) { - $time = wfTimestamp( TS_DB ); - $wiki = wfWikiID(); - $host = wfHostname(); - $text = "{$time} {$host} {$wiki}: {$message}\n"; - return $text; - } - - - /** - * Interpolate placeholders in logging message. - * - * @param string $message - * @param array $context - * @return string Interpolated message - */ - public static function interpolate( $message, array $context ) { - if ( strpos( $message, '{' ) !== false ) { - $replace = array(); - foreach ( $context as $key => $val ) { - $replace['{' . $key . '}'] = $val; - } - $message = strtr( $message, $replace ); - } - return $message; - } - - - /** - * Select the appropriate log output destination for the given log event. - * - * If the event context contains 'destination' - * - * @param string $channel - * @param string $message - * @param array $context - * @return string - */ - protected static function destination( $channel, $message, $context ) { - global $wgDebugLogFile, $wgDBerrorLog, $wgDebugLogGroups; - - // Default destination is the debug log file as historically used by - // the wfDebug function. - $destination = $wgDebugLogFile; - - if ( isset( $context['destination'] ) ) { - // Use destination explicitly provided in context - $destination = $context['destination']; - - } elseif ( $channel === 'wfDebug' ) { - $destination = $wgDebugLogFile; - - } elseif ( $channel === 'wfLogDBError' ) { - $destination = $wgDBerrorLog; - - } elseif ( isset( $wgDebugLogGroups[$channel] ) ) { - $logConfig = $wgDebugLogGroups[$channel]; - - if ( is_array( $logConfig ) ) { - $destination = $logConfig['destination']; - } else { - $destination = strval( $logConfig ); - } - } - - return $destination; - } - - - /** - * Log to a file without getting "file size exceeded" signals. - * - * Can also log to UDP with the syntax udp://host:port/prefix. This will send - * lines to the specified port, prefixed by the specified prefix and a space. - * - * @param string $text - * @param string $file Filename - * @throws MWException - */ - public static function emit( $text, $file ) { - if ( substr( $file, 0, 4 ) == 'udp:' ) { - $transport = UDPTransport::newFromString( $file ); - $transport->emit( $text ); - } else { - wfSuppressWarnings(); - $exists = file_exists( $file ); - $size = $exists ? filesize( $file ) : false; - if ( !$exists || - ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) - ) { - file_put_contents( $file, $text, FILE_APPEND ); - } - wfRestoreWarnings(); - } - } - -} diff --git a/includes/debug/logger/legacy/Spi.php b/includes/debug/logger/legacy/Spi.php deleted file mode 100644 index 79d4f24a73..0000000000 --- a/includes/debug/logger/legacy/Spi.php +++ /dev/null @@ -1,58 +0,0 @@ - 'MWLoggerLegacySpi', - * ); - * @endcode - * - * @see MWLoggerFactory - * @since 1.25 - * @author Bryan Davis - * @copyright © 2014 Bryan Davis and Wikimedia Foundation. - */ -class MWLoggerLegacySpi implements MWLoggerSpi { - - /** - * @var array $singletons - */ - protected $singletons = array(); - - - /** - * Get a logger instance. - * - * @param string $channel Logging channel - * @return \Psr\Log\LoggerInterface Logger instance - */ - public function getLogger( $channel ) { - if ( !isset( $this->singletons[$channel] ) ) { - $this->singletons[$channel] = new MWLoggerLegacyLogger( $channel ); - } - return $this->singletons[$channel]; - } - -} diff --git a/includes/debug/logger/monolog/Handler.php b/includes/debug/logger/monolog/Handler.php deleted file mode 100644 index 9e7678d21c..0000000000 --- a/includes/debug/logger/monolog/Handler.php +++ /dev/null @@ -1,236 +0,0 @@ - - * @copyright © 2013 Bryan Davis and Wikimedia Foundation. - */ -class MWLoggerMonologHandler extends \Monolog\Handler\AbstractProcessingHandler { - - /** - * Log sink descriptor - * @var string $uri - */ - protected $uri; - - /** - * Filter log events using legacy rules - * @var bool $useLegacyFilter - */ - protected $useLegacyFilter; - - /** - * Log sink - * @var resource $sink - */ - protected $sink; - - /** - * @var string $error - */ - protected $error; - - /** - * @var string $host - */ - protected $host; - - /** - * @var int $port - */ - protected $port; - - /** - * @var string $prefix - */ - protected $prefix; - - - /** - * @param string $stream Stream URI - * @param bool $useLegacyFilter Filter log events using legacy rules - * @param int $level Minimum logging level that will trigger handler - * @param bool $bubble Can handled meesages bubble up the handler stack? - */ - public function __construct( - $stream, - $useLegacyFilter = false, - $level = \Monolog\Logger::DEBUG, - $bubble = true - ) { - parent::__construct( $level, $bubble ); - $this->uri = $stream; - $this->useLegacyFilter = $useLegacyFilter; - } - - /** - * Open the log sink described by our stream URI. - */ - protected function openSink() { - if ( !$this->uri ) { - throw new LogicException( - 'Missing stream uri, the stream can not be opened.' ); - } - $this->error = null; - set_error_handler( array( $this, 'errorTrap' ) ); - - if ( substr( $this->uri, 0, 4 ) == 'udp:' ) { - $parsed = parse_url( $this->uri ); - if ( !isset( $parsed['host'] ) ) { - throw new UnexpectedValueException( sprintf( - 'Udp transport "%s" must specify a host', $this->uri - ) ); - } - if ( !isset( $parsed['port'] ) ) { - throw new UnexpectedValueException( sprintf( - 'Udp transport "%s" must specify a port', $this->uri - ) ); - } - - $this->host = $parsed['host']; - $this->port = $parsed['port']; - $this->prefix = ''; - - if ( isset( $parsed['path'] ) ) { - $this->prefix = ltrim( $parsed['path'], '/' ); - } - - if ( filter_var( $this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) { - $domain = AF_INET6; - - } else { - $domain = AF_INET; - } - - $this->sink = socket_create( $domain, SOCK_DGRAM, SOL_UDP ); - - } else { - $this->sink = fopen( $this->uri, 'a' ); - } - restore_error_handler(); - - if ( !is_resource( $this->sink ) ) { - $this->sink = null; - throw new UnexpectedValueException( sprintf( - 'The stream or file "%s" could not be opened: %s', - $this->uri, $this->error - ) ); - } - } - - - /** - * Custom error handler. - * @param int $code Error number - * @param string $msg Error message - */ - protected function errorTrap( $code, $msg ) { - $this->error = $msg; - } - - - /** - * Should we use UDP to send messages to the sink? - * @return bool - */ - protected function useUdp() { - return $this->host !== null; - } - - - protected function write( array $record ) { - if ( $this->useLegacyFilter && - !MWLoggerLegacyLogger::shouldEmit( - $record['channel'], $record['message'], - $record['level'], $record - ) ) { - // Do not write record if we are enforcing legacy rules and they - // do not pass this message. This used to be done in isHandling(), - // but Monolog 1.12.0 made a breaking change that removed access - // to the needed channel and context information. - return; - } - - if ( $this->sink === null ) { - $this->openSink(); - } - - $text = (string)$record['formatted']; - if ( $this->useUdp() ) { - - // Clean it up for the multiplexer - if ( $this->prefix !== '' ) { - $leader = ( $this->prefix === '{channel}' ) ? - $record['channel'] : $this->prefix; - $text = preg_replace( '/^/m', "{$leader} ", $text ); - - // Limit to 64KB - if ( strlen( $text ) > 65506 ) { - $text = substr( $text, 0, 65506 ); - } - - if ( substr( $text, -1 ) != "\n" ) { - $text .= "\n"; - } - - } elseif ( strlen( $text ) > 65507 ) { - $text = substr( $text, 0, 65507 ); - } - - socket_sendto( - $this->sink, $text, strlen( $text ), 0, $this->host, $this->port ); - - } else { - fwrite( $this->sink, $text ); - } - } - - - public function close() { - if ( is_resource( $this->sink ) ) { - if ( $this->useUdp() ) { - socket_close( $this->sink ); - - } else { - fclose( $this->sink ); - } - } - $this->sink = null; - } - -} diff --git a/includes/debug/logger/monolog/LegacyFormatter.php b/includes/debug/logger/monolog/LegacyFormatter.php index 67acf57d4a..9ec15cb85b 100644 --- a/includes/debug/logger/monolog/LegacyFormatter.php +++ b/includes/debug/logger/monolog/LegacyFormatter.php @@ -18,17 +18,22 @@ * @file */ +namespace MediaWiki\Logger\Monolog; + +use MediaWiki\Logger\LegacyLogger; +use Monolog\Formatter\NormalizerFormatter; + /** * Log message formatter that mimics the legacy log message formatting of * `wfDebug`, `wfDebugLog`, `wfLogDBError` and `wfErrorLog` global functions by - * delegating the formatting to MWLoggerLegacyLogger. + * delegating the formatting to \MediaWiki\Logger\LegacyLogger. * * @since 1.25 * @author Bryan Davis * @copyright © 2013 Bryan Davis and Wikimedia Foundation. - * @see MWLoggerLegacyLogger + * @see \MediaWiki\Logger\LegacyLogger */ -class MWLoggerMonologLegacyFormatter extends \Monolog\Formatter\NormalizerFormatter { +class LegacyFormatter extends NormalizerFormatter { public function __construct() { parent::__construct( 'c' ); @@ -36,7 +41,7 @@ class MWLoggerMonologLegacyFormatter extends \Monolog\Formatter\NormalizerFormat public function format( array $record ) { $normalized = parent::format( $record ); - return MWLoggerLegacyLogger::format( + return LegacyLogger::format( $normalized['channel'], $normalized['message'], $normalized ); } diff --git a/includes/debug/logger/monolog/LegacyHandler.php b/includes/debug/logger/monolog/LegacyHandler.php new file mode 100644 index 0000000000..8405819d86 --- /dev/null +++ b/includes/debug/logger/monolog/LegacyHandler.php @@ -0,0 +1,243 @@ + + * @copyright © 2013 Bryan Davis and Wikimedia Foundation. + */ +class LegacyHandler extends AbstractProcessingHandler { + + /** + * Log sink descriptor + * @var string $uri + */ + protected $uri; + + /** + * Filter log events using legacy rules + * @var bool $useLegacyFilter + */ + protected $useLegacyFilter; + + /** + * Log sink + * @var resource $sink + */ + protected $sink; + + /** + * @var string $error + */ + protected $error; + + /** + * @var string $host + */ + protected $host; + + /** + * @var int $port + */ + protected $port; + + /** + * @var string $prefix + */ + protected $prefix; + + + /** + * @param string $stream Stream URI + * @param bool $useLegacyFilter Filter log events using legacy rules + * @param int $level Minimum logging level that will trigger handler + * @param bool $bubble Can handled meesages bubble up the handler stack? + */ + public function __construct( + $stream, + $useLegacyFilter = false, + $level = Logger::DEBUG, + $bubble = true + ) { + parent::__construct( $level, $bubble ); + $this->uri = $stream; + $this->useLegacyFilter = $useLegacyFilter; + } + + /** + * Open the log sink described by our stream URI. + */ + protected function openSink() { + if ( !$this->uri ) { + throw new LogicException( + 'Missing stream uri, the stream can not be opened.' ); + } + $this->error = null; + set_error_handler( array( $this, 'errorTrap' ) ); + + if ( substr( $this->uri, 0, 4 ) == 'udp:' ) { + $parsed = parse_url( $this->uri ); + if ( !isset( $parsed['host'] ) ) { + throw new UnexpectedValueException( sprintf( + 'Udp transport "%s" must specify a host', $this->uri + ) ); + } + if ( !isset( $parsed['port'] ) ) { + throw new UnexpectedValueException( sprintf( + 'Udp transport "%s" must specify a port', $this->uri + ) ); + } + + $this->host = $parsed['host']; + $this->port = $parsed['port']; + $this->prefix = ''; + + if ( isset( $parsed['path'] ) ) { + $this->prefix = ltrim( $parsed['path'], '/' ); + } + + if ( filter_var( $this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) { + $domain = AF_INET6; + + } else { + $domain = AF_INET; + } + + $this->sink = socket_create( $domain, SOCK_DGRAM, SOL_UDP ); + + } else { + $this->sink = fopen( $this->uri, 'a' ); + } + restore_error_handler(); + + if ( !is_resource( $this->sink ) ) { + $this->sink = null; + throw new UnexpectedValueException( sprintf( + 'The stream or file "%s" could not be opened: %s', + $this->uri, $this->error + ) ); + } + } + + + /** + * Custom error handler. + * @param int $code Error number + * @param string $msg Error message + */ + protected function errorTrap( $code, $msg ) { + $this->error = $msg; + } + + + /** + * Should we use UDP to send messages to the sink? + * @return bool + */ + protected function useUdp() { + return $this->host !== null; + } + + + protected function write( array $record ) { + if ( $this->useLegacyFilter && + !LegacyLogger::shouldEmit( + $record['channel'], $record['message'], + $record['level'], $record + ) ) { + // Do not write record if we are enforcing legacy rules and they + // do not pass this message. This used to be done in isHandling(), + // but Monolog 1.12.0 made a breaking change that removed access + // to the needed channel and context information. + return; + } + + if ( $this->sink === null ) { + $this->openSink(); + } + + $text = (string)$record['formatted']; + if ( $this->useUdp() ) { + + // Clean it up for the multiplexer + if ( $this->prefix !== '' ) { + $leader = ( $this->prefix === '{channel}' ) ? + $record['channel'] : $this->prefix; + $text = preg_replace( '/^/m', "{$leader} ", $text ); + + // Limit to 64KB + if ( strlen( $text ) > 65506 ) { + $text = substr( $text, 0, 65506 ); + } + + if ( substr( $text, -1 ) != "\n" ) { + $text .= "\n"; + } + + } elseif ( strlen( $text ) > 65507 ) { + $text = substr( $text, 0, 65507 ); + } + + socket_sendto( + $this->sink, $text, strlen( $text ), 0, $this->host, $this->port + ); + + } else { + fwrite( $this->sink, $text ); + } + } + + + public function close() { + if ( is_resource( $this->sink ) ) { + if ( $this->useUdp() ) { + socket_close( $this->sink ); + + } else { + fclose( $this->sink ); + } + } + $this->sink = null; + } +} diff --git a/includes/debug/logger/monolog/Processor.php b/includes/debug/logger/monolog/Processor.php deleted file mode 100644 index 4aa07f1b36..0000000000 --- a/includes/debug/logger/monolog/Processor.php +++ /dev/null @@ -1,45 +0,0 @@ - - * @copyright © 2013 Bryan Davis and Wikimedia Foundation. - */ -class MWLoggerMonologProcessor { - - /** - * @param array $record - * @return array - */ - public function __invoke( array $record ) { - $record['extra'] = array_merge( - $record['extra'], - array( - 'host' => wfHostname(), - 'wiki' => wfWikiID(), - ) - ); - return $record; - } - -} diff --git a/includes/debug/logger/monolog/Spi.php b/includes/debug/logger/monolog/Spi.php deleted file mode 100644 index 68acf1df26..0000000000 --- a/includes/debug/logger/monolog/Spi.php +++ /dev/null @@ -1,246 +0,0 @@ - 'MWLoggerMonologSpi', - * 'args' => array( array( - * 'loggers' => array( - * '@default' => array( - * 'processors' => array( 'wiki', 'psr', 'pid', 'uid', 'web' ), - * 'handlers' => array( 'stream' ), - * ), - * 'runJobs' => array( - * 'processors' => array( 'wiki', 'psr', 'pid' ), - * 'handlers' => array( 'stream' ), - * ) - * ), - * 'processors' => array( - * 'wiki' => array( - * 'class' => 'MWLoggerMonologProcessor', - * ), - * 'psr' => array( - * 'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor', - * ), - * 'pid' => array( - * 'class' => '\\Monolog\\Processor\\ProcessIdProcessor', - * ), - * 'uid' => array( - * 'class' => '\\Monolog\\Processor\\UidProcessor', - * ), - * 'web' => array( - * 'class' => '\\Monolog\\Processor\\WebProcessor', - * ), - * ), - * 'handlers' => array( - * 'stream' => array( - * 'class' => '\\Monolog\\Handler\\StreamHandler', - * 'args' => array( 'path/to/your.log' ), - * 'formatter' => 'line', - * ), - * 'redis' => array( - * 'class' => '\\Monolog\\Handler\\RedisHandler', - * 'args' => array( function() { - * $redis = new Redis(); - * $redis->connect( '127.0.0.1', 6379 ); - * return $redis; - * }, - * 'logstash' - * ), - * 'formatter' => 'logstash', - * ), - * 'udp2log' => array( - * 'class' => 'MWLoggerMonologHandler', - * 'args' => array( - * 'udp://127.0.0.1:8420/mediawiki - * ), - * 'formatter' => 'line', - * ), - * ), - * 'formatters' => array( - * 'line' => array( - * 'class' => '\\Monolog\\Formatter\\LineFormatter', - * ), - * 'logstash' => array( - * 'class' => '\\Monolog\\Formatter\\LogstashFormatter', - * 'args' => array( 'mediawiki', php_uname( 'n' ), null, '', 1 ), - * ), - * ), - * ) ), - * ); - * @endcode - * - * @see https://github.com/Seldaek/monolog - * @since 1.25 - * @author Bryan Davis - * @copyright © 2014 Bryan Davis and Wikimedia Foundation. - */ -class MWLoggerMonologSpi implements MWLoggerSpi { - - /** - * @var array $singletons - */ - protected $singletons; - - /** - * Configuration for creating new loggers. - * @var array $config - */ - protected $config; - - - /** - * @param array $config Configuration data. - */ - public function __construct( array $config ) { - $this->config = $config; - $this->reset(); - } - - - /** - * Reset internal caches. - * - * This is public for use in unit tests. Under normal operation there should - * be no need to flush the caches. - */ - public function reset() { - $this->singletons = array( - 'loggers' => array(), - 'handlers' => array(), - 'formatters' => array(), - 'processors' => array(), - ); - } - - - /** - * Get a logger instance. - * - * Creates and caches a logger instance based on configuration found in the - * $wgMWLoggerMonologSpiConfig global. Subsequent request for the same channel - * name will return the cached instance. - * - * @param string $channel Logging channel - * @return \Psr\Log\LoggerInterface Logger instance - */ - public function getLogger( $channel ) { - if ( !isset( $this->singletons['loggers'][$channel] ) ) { - // Fallback to using the '@default' configuration if an explict - // configuration for the requested channel isn't found. - $spec = isset( $this->config['loggers'][$channel] ) ? - $this->config['loggers'][$channel] : - $this->config['loggers']['@default']; - - $monolog = $this->createLogger( $channel, $spec ); - $this->singletons['loggers'][$channel] = $monolog; - } - - return $this->singletons['loggers'][$channel]; - } - - - /** - * Create a logger. - * @param string $channel Logger channel - * @param array $spec Configuration - * @return \Monolog\Logger - */ - protected function createLogger( $channel, $spec ) { - $obj = new \Monolog\Logger( $channel ); - - if ( isset( $spec['processors'] ) ) { - foreach ( $spec['processors'] as $processor ) { - $obj->pushProcessor( $this->getProcessor( $processor ) ); - } - } - - if ( isset( $spec['handlers'] ) ) { - foreach ( $spec['handlers'] as $handler ) { - $obj->pushHandler( $this->getHandler( $handler ) ); - } - } - return $obj; - } - - - /** - * Create or return cached processor. - * @param string $name Processor name - * @return callable - */ - public function getProcessor( $name ) { - if ( !isset( $this->singletons['processors'][$name] ) ) { - $spec = $this->config['processors'][$name]; - $processor = ObjectFactory::getObjectFromSpec( $spec ); - $this->singletons['processors'][$name] = $processor; - } - return $this->singletons['processors'][$name]; - } - - - /** - * Create or return cached handler. - * @param string $name Processor name - * @return \Monolog\Handler\HandlerInterface - */ - public function getHandler( $name ) { - if ( !isset( $this->singletons['handlers'][$name] ) ) { - $spec = $this->config['handlers'][$name]; - $handler = ObjectFactory::getObjectFromSpec( $spec ); - if ( isset( $spec['formatter'] ) ) { - $handler->setFormatter( - $this->getFormatter( $spec['formatter'] ) - ); - } - $this->singletons['handlers'][$name] = $handler; - } - return $this->singletons['handlers'][$name]; - } - - - /** - * Create or return cached formatter. - * @param string $name Formatter name - * @return \Monolog\Formatter\FormatterInterface - */ - public function getFormatter( $name ) { - if ( !isset( $this->singletons['formatters'][$name] ) ) { - $spec = $this->config['formatters'][$name]; - $formatter = ObjectFactory::getObjectFromSpec( $spec ); - $this->singletons['formatters'][$name] = $formatter; - } - return $this->singletons['formatters'][$name]; - } -} diff --git a/includes/debug/logger/monolog/SyslogHandler.php b/includes/debug/logger/monolog/SyslogHandler.php index 50c2fb5cd8..008efbc161 100644 --- a/includes/debug/logger/monolog/SyslogHandler.php +++ b/includes/debug/logger/monolog/SyslogHandler.php @@ -18,6 +18,8 @@ * @file */ +namespace MediaWiki\Logger\Monolog; + use Monolog\Handler\SyslogUdpHandler; use Monolog\Logger; @@ -44,7 +46,7 @@ use Monolog\Logger; * @author Bryan Davis * @copyright © 2015 Bryan Davis and Wikimedia Foundation. */ -class MWLoggerMonologSyslogHandler extends SyslogUdpHandler { +class SyslogHandler extends SyslogUdpHandler { /** * @var string $appname diff --git a/includes/debug/logger/monolog/WikiProcessor.php b/includes/debug/logger/monolog/WikiProcessor.php new file mode 100644 index 0000000000..a52f63664b --- /dev/null +++ b/includes/debug/logger/monolog/WikiProcessor.php @@ -0,0 +1,47 @@ + + * @copyright © 2013 Bryan Davis and Wikimedia Foundation. + */ +class WikiProcessor { + + /** + * @param array $record + * @return array + */ + public function __invoke( array $record ) { + $record['extra'] = array_merge( + $record['extra'], + array( + 'host' => wfHostname(), + 'wiki' => wfWikiID(), + ) + ); + return $record; + } + +} diff --git a/includes/exception/HttpError.php b/includes/exception/HttpError.php index 051556623b..b81c57314c 100644 --- a/includes/exception/HttpError.php +++ b/includes/exception/HttpError.php @@ -18,6 +18,8 @@ * @file */ +use MediaWiki\Logger\LoggerFactory; + /** * Show an error that looks like an HTTP server error. * Replacement for wfHttpError(). @@ -81,7 +83,7 @@ class HttpError extends MWException { } private function doLog() { - $logger = MWLoggerFactory::getInstance( 'HttpError' ); + $logger = LoggerFactory::getInstance( 'HttpError' ); $content = $this->content; if ( $content instanceof Message ) { diff --git a/includes/jobqueue/JobRunner.php b/includes/jobqueue/JobRunner.php index edb491159d..b8c5d6cf78 100644 --- a/includes/jobqueue/JobRunner.php +++ b/includes/jobqueue/JobRunner.php @@ -21,6 +21,7 @@ * @ingroup JobQueue */ +use MediaWiki\Logger\LoggerFactory; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; @@ -58,7 +59,7 @@ class JobRunner implements LoggerAwareInterface { */ public function __construct( LoggerInterface $logger = null ) { if ( $logger === null ) { - $logger = MWLoggerFactory::getInstance( 'runJobs' ); + $logger = LoggerFactory::getInstance( 'runJobs' ); } $this->setLogger( $logger ); } diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php index 1f2c9c046c..2e47e24ad9 100644 --- a/includes/objectcache/ObjectCache.php +++ b/includes/objectcache/ObjectCache.php @@ -21,6 +21,8 @@ * @ingroup Cache */ +use MediaWiki\Logger\LoggerFactory; + /** * Functions to get cache objects * @@ -82,11 +84,11 @@ class ObjectCache { */ static function newFromParams( $params ) { if ( isset( $params['loggroup'] ) ) { - $params['logger'] = MWLoggerFactory::getInstance( $params['loggroup'] ); + $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] ); } else { // For backwards-compatability with custom parameters, lets not // have all logging suddenly disappear - $params['logger'] = MWLoggerFactory::getInstance( 'objectcache' ); + $params['logger'] = LoggerFactory::getInstance( 'objectcache' ); } if ( isset( $params['factory'] ) ) { return call_user_func( $params['factory'], $params ); diff --git a/includes/specials/SpecialRunJobs.php b/includes/specials/SpecialRunJobs.php index 0d44d34358..8cf93670ba 100644 --- a/includes/specials/SpecialRunJobs.php +++ b/includes/specials/SpecialRunJobs.php @@ -22,6 +22,8 @@ * @author Aaron Schulz */ +use MediaWiki\Logger\LoggerFactory; + /** * Special page designed for running background tasks (internal use only) * @@ -89,7 +91,7 @@ class SpecialRunJobs extends UnlistedSpecialPage { // Do all of the specified tasks... if ( in_array( 'jobs', explode( '|', $params['tasks'] ) ) ) { - $runner = new JobRunner( MWLoggerFactory::getInstance( 'runJobs' ) ); + $runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) ); $response = $runner->run( array( 'type' => $params['type'], 'maxJobs' => $params['maxjobs'] ? $params['maxjobs'] : 1, diff --git a/maintenance/runJobs.php b/maintenance/runJobs.php index 6abfb66bf3..3864e3c607 100644 --- a/maintenance/runJobs.php +++ b/maintenance/runJobs.php @@ -23,6 +23,8 @@ require_once __DIR__ . '/Maintenance.php'; +use MediaWiki\Logger\LoggerFactory; + /** * Maintenance script that runs pending jobs. * @@ -68,7 +70,7 @@ class RunJobs extends Maintenance { $json = ( $this->getOption( 'result' ) === 'json' ); - $runner = new JobRunner( MWLoggerFactory::getInstance( 'runJobs' ) ); + $runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) ); if ( !$json ) { $runner->setDebugHandler( array( $this, 'debugInternal' ) ); } diff --git a/maintenance/storage/recompressTracked.php b/maintenance/storage/recompressTracked.php index eb7eca18a2..3562df6299 100644 --- a/maintenance/storage/recompressTracked.php +++ b/maintenance/storage/recompressTracked.php @@ -22,6 +22,8 @@ * @ingroup Maintenance ExternalStorage */ +use MediaWiki\Logger\LegacyLogger; + $optionsWithArgs = RecompressTracked::getOptionsWithArgs(); require __DIR__ . '/../commandLine.inc'; @@ -141,7 +143,7 @@ class RecompressTracked { $header .= "({$this->slaveId})"; } $header .= ' ' . wfWikiID(); - MWLoggerLegacyLogger::emit( sprintf( "%-50s %s\n", $header, $msg ), $file ); + LegacyLogger::emit( sprintf( "%-50s %s\n", $header, $msg ), $file ); } /** diff --git a/tests/phpunit/includes/debug/logging/LegacyLoggerTest.php b/tests/phpunit/includes/debug/logging/LegacyLoggerTest.php new file mode 100644 index 0000000000..415fa0451b --- /dev/null +++ b/tests/phpunit/includes/debug/logging/LegacyLoggerTest.php @@ -0,0 +1,122 @@ +assertEquals( + $expect, LegacyLogger::interpolate( $message, $context ) ); + } + + public function provideInterpolate() { + return array( + array( + 'no-op', + array(), + 'no-op', + ), + array( + 'Hello {world}!', + array( + 'world' => 'World', + ), + 'Hello World!', + ), + array( + '{greeting} {user}', + array( + 'greeting' => 'Goodnight', + 'user' => 'Moon', + ), + 'Goodnight Moon', + ), + array( + 'Oops {key_not_set}', + array(), + 'Oops {key_not_set}', + ), + array( + '{ not interpolated }', + array( + 'not interpolated' => 'This should NOT show up in the message', + ), + '{ not interpolated }', + ), + ); + } + + /** + * @covers LegacyLogger::shouldEmit + * @dataProvider provideShouldEmit + */ + public function testShouldEmit( $level, $config, $expected ) { + $this->setMwGlobals( 'wgDebugLogGroups', array( 'fakechannel' => $config ) ); + $this->assertEquals( + $expected, + LegacyLogger::shouldEmit( 'fakechannel', 'some message', $level, array() ) + ); + } + + public static function provideShouldEmit() { + $dest = array( 'destination' => 'foobar' ); + $tests = array( + array( + LogLevel::DEBUG, + $dest, + true + ), + array( + LogLevel::WARNING, + $dest + array( 'level' => LogLevel::INFO ), + true, + ), + array( + LogLevel::INFO, + $dest + array( 'level' => LogLevel::CRITICAL ), + false, + ), + ); + + if ( class_exists( '\Monolog\Logger' ) ) { + $tests[] = array( + \Monolog\Logger::INFO, + $dest + array( 'level' => LogLevel::INFO ), + true, + ); + $tests[] = array( + \Monolog\Logger::WARNING, + $dest + array( 'level' => LogLevel::EMERGENCY ), + false, + ); + } + + return $tests; + } + +} diff --git a/tests/phpunit/includes/debug/logging/legacy/LoggerTest.php b/tests/phpunit/includes/debug/logging/legacy/LoggerTest.php deleted file mode 100644 index 66e9be4805..0000000000 --- a/tests/phpunit/includes/debug/logging/legacy/LoggerTest.php +++ /dev/null @@ -1,118 +0,0 @@ -assertEquals( - $expect, MWLoggerLegacyLogger::interpolate( $message, $context ) ); - } - - public function provideInterpolate() { - return array( - array( - 'no-op', - array(), - 'no-op', - ), - array( - 'Hello {world}!', - array( - 'world' => 'World', - ), - 'Hello World!', - ), - array( - '{greeting} {user}', - array( - 'greeting' => 'Goodnight', - 'user' => 'Moon', - ), - 'Goodnight Moon', - ), - array( - 'Oops {key_not_set}', - array(), - 'Oops {key_not_set}', - ), - array( - '{ not interpolated }', - array( - 'not interpolated' => 'This should NOT show up in the message', - ), - '{ not interpolated }', - ), - ); - } - - /** - * @covers MWLoggerLegacyLogger::shouldEmit - * @dataProvider provideShouldEmit - */ - public function testShouldEmit( $level, $config, $expected ) { - $this->setMwGlobals( 'wgDebugLogGroups', array( 'fakechannel' => $config ) ); - $this->assertEquals( - $expected, - MWLoggerLegacyLogger::shouldEmit( 'fakechannel', 'some message', $level, array() ) - ); - } - - public static function provideShouldEmit() { - $dest = array( 'destination' => 'foobar' ); - $tests = array( - array( - LogLevel::DEBUG, - $dest, - true - ), - array( - LogLevel::WARNING, - $dest + array( 'level' => LogLevel::INFO ), - true, - ), - array( - LogLevel::INFO, - $dest + array( 'level' => LogLevel::CRITICAL ), - false, - ), - ); - - if ( class_exists( '\Monolog\Logger' ) ) { - $tests[] = array( - \Monolog\Logger::INFO, - $dest + array( 'level' => LogLevel::INFO ), - true, - ); - $tests[] = array( - \Monolog\Logger::WARNING, - $dest + array( 'level' => LogLevel::EMERGENCY ), - false, - ); - } - - return $tests; - } - -}