Move MWLogger classes to MediaWiki\Logger namespace
authorBryan Davis <bd808@wikimedia.org>
Mon, 23 Mar 2015 00:53:24 +0000 (18:53 -0600)
committerOri Livneh <ori@wikimedia.org>
Fri, 3 Apr 2015 18:32:24 +0000 (11:32 -0700)
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

34 files changed:
RELEASE-NOTES-1.25
api.php
autoload.php
docs/logger.txt [new file with mode: 0644]
docs/mwlogger.txt [deleted file]
includes/DefaultSettings.php
includes/GlobalFunctions.php
includes/MediaWiki.php
includes/debug/logger/Factory.php [deleted file]
includes/debug/logger/LegacyLogger.php [new file with mode: 0644]
includes/debug/logger/LegacySpi.php [new file with mode: 0644]
includes/debug/logger/Logger.php [deleted file]
includes/debug/logger/LoggerFactory.php [new file with mode: 0644]
includes/debug/logger/MonologSpi.php [new file with mode: 0644]
includes/debug/logger/NullSpi.php
includes/debug/logger/Shims.php [new file with mode: 0644]
includes/debug/logger/Spi.php
includes/debug/logger/legacy/Logger.php [deleted file]
includes/debug/logger/legacy/Spi.php [deleted file]
includes/debug/logger/monolog/Handler.php [deleted file]
includes/debug/logger/monolog/LegacyFormatter.php
includes/debug/logger/monolog/LegacyHandler.php [new file with mode: 0644]
includes/debug/logger/monolog/Processor.php [deleted file]
includes/debug/logger/monolog/Spi.php [deleted file]
includes/debug/logger/monolog/SyslogHandler.php
includes/debug/logger/monolog/WikiProcessor.php [new file with mode: 0644]
includes/exception/HttpError.php
includes/jobqueue/JobRunner.php
includes/objectcache/ObjectCache.php
includes/specials/SpecialRunJobs.php
maintenance/runJobs.php
maintenance/storage/recompressTracked.php
tests/phpunit/includes/debug/logging/LegacyLoggerTest.php [new file with mode: 0644]
tests/phpunit/includes/debug/logging/legacy/LoggerTest.php [deleted file]

index 7b035ac..1ec8b93 100644 (file)
@@ -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 7788a36..9212747 100644 (file)
--- 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" );
 }
 
index 3396738..d646a0e 100644 (file)
@@ -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 (file)
index 0000000..89e620a
--- /dev/null
@@ -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 (file)
index ecc3626..0000000
+++ /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
index 845c7f5..a5d6605 100644 (file)
@@ -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',
 );
 
 /**
index b2a2e3d..7c4ebc2 100644 (file)
@@ -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 );
 }
 
index 3f2cb62..68d03c8 100644 (file)
@@ -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 (file)
index 2660b92..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-
-/**
- * PSR-3 logger instance factory.
- *
- * Creation of \Psr\Log\LoggerInterface instances is managed via the
- * MWLoggerFactory::getInstance() static method which in turn delegates to the
- * currently registered service provider.
- *
- * A service provider is any class implementing the MWLoggerSpi interface.
- * There are two possible methods of registering a service provider. The
- * MWLoggerFactory::registerProvider() static method can be called at any time
- * to change the service provider. If MWLoggerFactory::getInstance() is 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.
- *
- * @see MWLoggerSpi
- * @since 1.25
- * @author Bryan Davis <bd808@wikimedia.org>
- * @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 = <<<TXT
-MediaWiki requires the <a href="https://github.com/php-fig/log">PSR-3 logging library</a> 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 <a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a> 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 (file)
index 0000000..9cf104a
--- /dev/null
@@ -0,0 +1,379 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Logger;
+
+use MWDebug;
+use Psr\Log\AbstractLogger;
+use Psr\Log\LogLevel;
+use UDPTransport;
+
+/**
+ * PSR-3 logger that mimics the historic implementation of MediaWiki's
+ * wfErrorLog logging implementation.
+ *
+ * This logger is configured by the following global configuration variables:
+ * - `$wgDebugLogFile`
+ * - `$wgDebugLogGroups`
+ * - `$wgDBerrorLog`
+ * - `$wgDBerrorLogTZ`
+ *
+ * See documentation in DefaultSettings.php for detailed explanations of each
+ * variable.
+ *
+ * @see \MediaWiki\Logger\LoggerFactory
+ * @since 1.25
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @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 (file)
index 0000000..1bf39e4
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Logger;
+
+/**
+ * LoggerFactory service provider that creates LegacyLogger instances.
+ *
+ * Usage:
+ * @code
+ * $wgMWLoggerDefaultSpi = array(
+ *   'class' => '\\MediaWiki\\Logger\\LegacySpi',
+ * );
+ * @endcode
+ *
+ * @see \MediaWiki\Logger\LoggerFactory
+ * @since 1.25
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @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 (file)
index 27cf0cd..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-
-/**
- * Backwards compatibility stub for usage from before the introduction of
- * MWLoggerFactory.
- *
- * @deprecated since 1.25 Use MWLoggerFactory
- * @todo This class should be removed before the 1.25 final release.
- */
-class MWLogger {
-
-       /**
-        * Register a service provider to create new \Psr\Log\LoggerInterface
-        * instances.
-        *
-        * @param MWLoggerSpi $provider Provider to register
-        * @deprecated since 1.25 Use MWLoggerFactory::registerProvider()
-        */
-       public static function registerProvider( MWLoggerSpi $provider ) {
-               MWLoggerFactory::registerProvider( $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()
-        * @deprecated since 1.25 Use MWLoggerFactory::getProvider()
-        */
-       public static function getProvider() {
-               return MWLoggerFactory::getProvider();
-       }
-
-
-       /**
-        * Get a named logger instance from the currently configured logger factory.
-        *
-        * @param string $channel Logger channel (name)
-        * @return \Psr\Log\LoggerInterface
-        * @deprecated since 1.25 Use MWLoggerFactory::getInstance()
-        */
-       public static function getInstance( $channel ) {
-               return MWLoggerFactory::getInstance( $channel );
-       }
-
-}
diff --git a/includes/debug/logger/LoggerFactory.php b/includes/debug/logger/LoggerFactory.php
new file mode 100644 (file)
index 0000000..b3078b9
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Logger;
+
+use ObjectFactory;
+
+/**
+ * PSR-3 logger instance factory.
+ *
+ * Creation of \Psr\Log\LoggerInterface instances is managed via the
+ * LoggerFactory::getInstance() static method which in turn delegates to the
+ * currently registered service provider.
+ *
+ * A service provider is any class implementing the Spi interface.
+ * There are two possible methods of registering a service provider. The
+ * LoggerFactory::registerProvider() static method can be called at any time
+ * to change the service provider. If LoggerFactory::getInstance() is 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.
+ *
+ * @see \MediaWiki\Logger\Spi
+ * @since 1.25
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @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 <a href="https://github.com/php-fig/log">PSR-3 logging ' .
+                               "library</a> 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 <a href="https://www.mediawiki.org/wiki/Download_from_Git' .
+                               '#Fetch_external_libraries">mediawiki.org</a> 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 (file)
index 0000000..a07fdc4
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Logger;
+
+use Monolog\Logger;
+use ObjectFactory;
+
+/**
+ * LoggerFactory service provider that creates loggers implemented by
+ * Monolog.
+ *
+ * Configured using an array of configuration data with the keys 'loggers',
+ * 'processors', 'handlers' and 'formatters'.
+ *
+ * The ['loggers']['@default'] configuration will be used to create loggers
+ * for any channel that isn't explicitly named in the 'loggers' configuration
+ * section.
+ *
+ * Configuration will most typically be provided in the $wgMWLoggerDefaultSpi
+ * global configuration variable used by LoggerFactory to construct its
+ * default SPI provider:
+ * @code
+ * $wgMWLoggerDefaultSpi = array(
+ *   'class' => '\\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 <bd808@wikimedia.org>
+ * @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];
+       }
+}
index 617842c..a82d2c4 100644 (file)
  * @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 <bd808@wikimedia.org>
  * @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 (file)
index 0000000..c78b0dc
--- /dev/null
@@ -0,0 +1,171 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * MediaWiki\Logger\LoggerFactory.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\LoggerFactory
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLogger {
+
+       /**
+        * Register a service provider to create new \Psr\Log\LoggerInterface
+        * instances.
+        *
+        * @param \MediaWiki\Logger\Spi $provider Provider to register
+        * @deprecated since 1.25 Use MediaWiki\Logger\LoggerFactory::registerProvider()
+        */
+       public static function registerProvider( \MediaWiki\Logger\Spi $provider ) {
+               \MediaWiki\Logger\LoggerFactory::registerProvider( $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 \MediaWiki\Logger\Spi
+        * @see registerProvider()
+        * @see ObjectFactory::getObjectFromSpec()
+        * @deprecated since 1.25 Use MediaWiki\Logger\LoggerFactory::getProvider()
+        */
+       public static function getProvider() {
+               return \MediaWiki\Logger\LoggerFactory::getProvider();
+       }
+
+
+       /**
+        * Get a named logger instance from the currently configured logger factory.
+        *
+        * @param string $channel Logger channel (name)
+        * @return \Psr\Log\LoggerInterface
+        * @deprecated since 1.25 Use MediaWiki\Logger\LoggerFactory::getInstance()
+        */
+       public static function getInstance( $channel ) {
+               return \MediaWiki\Logger\LoggerFactory::getInstance( $channel );
+       }
+
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\LoggerFactory
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerFactory extends \MediaWiki\Logger\LoggerFactory {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\LegacyLogger
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerLegacyLogger extends \MediaWiki\Logger\LegacyLogger {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\LegacySpi
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerLegacySpi extends \MediaWiki\Logger\LegacySpi {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\Monolog\LegacyHandler
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerMonologHandler extends \MediaWiki\Logger\Monolog\LegacyHandler {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\Monolog\LegacyFormatter
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerMonologLegacyFormatter extends \MediaWiki\Logger\Monolog\LegacyFormatter {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\Monolog\WikiProcessor
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerMonologProcessor extends \MediaWiki\Logger\Monolog\WikiProcessor {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\MonologSpi
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerMonologSpi extends \MediaWiki\Logger\MonologSpi {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\Monolog\SyslogHandler
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerMonologSyslogHandler extends \MediaWiki\Logger\Monolog\SyslogHandler {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\NullSpi
+ * @todo This class should be removed before the 1.25 final release.
+ */
+class MWLoggerNullSpi extends \MediaWiki\Logger\NullSpi {
+}
+
+/**
+ * Backwards compatibility stub for usage from before the introduction of
+ * the MediaWiki\Logger namespace.
+ *
+ * @deprecated since 1.25 Use \MediaWiki\Logger\Spi
+ * @todo This class should be removed before the 1.25 final release.
+ */
+interface MWLoggerSpi extends \MediaWiki\Logger\Spi {
+}
index cd9f17f..044789f 100644 (file)
@@ -18,6 +18,8 @@
  * @file
  */
 
+namespace MediaWiki\Logger;
+
 /**
  * Service provider interface for \Psr\Log\LoggerInterface implementation
  * libraries.
  * MediaWiki can be configured to use a class implementing this interface to
  * create new \Psr\Log\LoggerInterface instances via either the
  * $wgMWLoggerDefaultSpi global variable or code that constructs an instance
- * and registers it via the MWLoggerFactory::registerProvider() static method.
+ * and registers it via the LoggerFactory::registerProvider() static method.
  *
- * @see MWLoggerFactory
+ * @see \MediaWiki\Logger\LoggerFactory
  * @since 1.25
  * @author Bryan Davis <bd808@wikimedia.org>
  * @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 (file)
index 610635d..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * PSR-3 logger that mimics the historic implementation of MediaWiki's
- * wfErrorLog logging implementation.
- *
- * This logger is configured by the following global configuration variables:
- * - `$wgDebugLogFile`
- * - `$wgDebugLogGroups`
- * - `$wgDBerrorLog`
- * - `$wgDBerrorLogTZ`
- *
- * See documentation in DefaultSettings.php for detailed explanations of each
- * variable.
- *
- * @see MWLoggerFactory
- * @since 1.25
- * @author Bryan Davis <bd808@wikimedia.org>
- * @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 (file)
index 79d4f24..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * MWLoggerFactory service provider that creates MWLoggerLegacyLogger
- * instances.
- *
- * Usage:
- * @code
- * $wgMWLoggerDefaultSpi = array(
- *   'class' => 'MWLoggerLegacySpi',
- * );
- * @endcode
- *
- * @see MWLoggerFactory
- * @since 1.25
- * @author Bryan Davis <bd808@wikimedia.org>
- * @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 (file)
index 9e7678d..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-
-/**
- * Log handler that replicates the behavior of MediaWiki's wfErrorLog()
- * logging service. Log output can be directed to a local file, a PHP stream,
- * or a udp2log server.
- *
- * For udp2log output, the stream specification must have the form:
- * "udp://HOST:PORT[/PREFIX]"
- * where:
- * - HOST: IPv4, IPv6 or hostname
- * - PORT: server port
- * - PREFIX: optional (but recommended) prefix telling udp2log how to route
- * the log event. The special prefix "{channel}" will use the log event's
- * channel as the prefix value.
- *
- * When not targeting a udp2log stream this class will act as a drop-in
- * replacement for Monolog's StreamHandler.
- *
- * @since 1.25
- * @author Bryan Davis <bd808@wikimedia.org>
- * @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;
-       }
-
-}
index 67acf57..9ec15cb 100644 (file)
  * @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 <bd808@wikimedia.org>
  * @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 (file)
index 0000000..8405819
--- /dev/null
@@ -0,0 +1,243 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Logger\Monolog;
+
+use LogicException;
+use MediaWiki\Logger\LegacyLogger;
+use Monolog\Handler\AbstractProcessingHandler;
+use Monolog\Logger;
+use UnexpectedValueException;
+
+/**
+ * Log handler that replicates the behavior of MediaWiki's wfErrorLog()
+ * logging service. Log output can be directed to a local file, a PHP stream,
+ * or a udp2log server.
+ *
+ * For udp2log output, the stream specification must have the form:
+ * "udp://HOST:PORT[/PREFIX]"
+ * where:
+ * - HOST: IPv4, IPv6 or hostname
+ * - PORT: server port
+ * - PREFIX: optional (but recommended) prefix telling udp2log how to route
+ * the log event. The special prefix "{channel}" will use the log event's
+ * channel as the prefix value.
+ *
+ * When not targeting a udp2log stream this class will act as a drop-in
+ * replacement for Monolog's StreamHandler.
+ *
+ * @since 1.25
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @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 (file)
index 4aa07f1..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * Injects `wfHostname()` and `wfWikiID()` in all records.
- *
- * @since 1.25
- * @author Bryan Davis <bd808@wikimedia.org>
- * @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 (file)
index 68acf1d..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * MWLoggerFactory service provider that creates loggers implemented by
- * Monolog.
- *
- * Configured using an array of configuration data with the keys 'loggers',
- * 'processors', 'handlers' and 'formatters'.
- *
- * The ['loggers']['@default'] configuration will be used to create loggers
- * for any channel that isn't explicitly named in the 'loggers' configuration
- * section.
- *
- * Configuration will most typically be provided in the $wgMWLoggerDefaultSpi
- * global configuration variable used by MWLoggerFactory to construct its
- * default SPI provider:
- * @code
- * $wgMWLoggerDefaultSpi = array(
- *   'class' => '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 <bd808@wikimedia.org>
- * @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];
-       }
-}
index 50c2fb5..008efbc 100644 (file)
@@ -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 <bd808@wikimedia.org>
  * @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 (file)
index 0000000..a52f636
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Logger\Monolog;
+
+/**
+ * Injects `wfHostname()` and `wfWikiID()` in all records.
+ *
+ * @since 1.25
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @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;
+       }
+
+}
index 0515566..b81c573 100644 (file)
@@ -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 ) {
index edb4911..b8c5d6c 100644 (file)
@@ -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 );
        }
index 1f2c9c0..2e47e24 100644 (file)
@@ -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 );
index 0d44d34..8cf9367 100644 (file)
@@ -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,
index 6abfb66..3864e3c 100644 (file)
@@ -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' ) );
                }
index eb7eca1..3562df6 100644 (file)
@@ -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 (file)
index 0000000..415fa04
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+namespace MediaWiki\Logger;
+
+use MediaWikiTestCase;
+use Psr\Log\LogLevel;
+
+class LegacyLoggerTest extends MediaWikiTestCase {
+
+       /**
+        * @covers LegacyLogger::interpolate
+        * @dataProvider provideInterpolate
+        */
+       public function testInterpolate( $message, $context, $expect ) {
+               $this->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 (file)
index 66e9be4..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-use Psr\Log\LogLevel;
-
-class MWLoggerLegacyLoggerTest extends MediaWikiTestCase {
-
-       /**
-        * @covers MWLoggerLegacyLogger::interpolate
-        * @dataProvider provideInterpolate
-        */
-       public function testInterpolate( $message, $context, $expect ) {
-               $this->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;
-       }
-
-}