From 1bad1d020fe04332eb30be8f03a7e9ecd17158cc Mon Sep 17 00:00:00 2001 From: Bryan Davis Date: Sat, 20 Dec 2014 14:15:59 -0700 Subject: [PATCH] monolog: MWLoggerMonologSamplingHandler Introduce the MWLoggerMonologSamplingHandler which can be used to probabilistically sample the log event stream. Bug: T85067 Change-Id: Icd14fc8c44ca9eef0f3f5cc4f1d1d8b68d517f07 --- autoload.php | 1 + includes/debug/logger/Logger.php | 25 +++- .../debug/logger/monolog/SamplingHandler.php | 122 ++++++++++++++++++ includes/debug/logger/monolog/Spi.php | 6 +- 4 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 includes/debug/logger/monolog/SamplingHandler.php diff --git a/autoload.php b/autoload.php index d4a152a023..50727bcd93 100644 --- a/autoload.php +++ b/autoload.php @@ -684,6 +684,7 @@ $wgAutoloadLocalClasses = array( 'MWLoggerMonologHandler' => __DIR__ . '/includes/debug/logger/monolog/Handler.php', 'MWLoggerMonologLegacyFormatter' => __DIR__ . '/includes/debug/logger/monolog/LegacyFormatter.php', 'MWLoggerMonologProcessor' => __DIR__ . '/includes/debug/logger/monolog/Processor.php', + 'MWLoggerMonologSamplingHandler' => __DIR__ . '/includes/debug/logger/monolog/SamplingHandler.php', 'MWLoggerMonologSpi' => __DIR__ . '/includes/debug/logger/monolog/Spi.php', 'MWLoggerNullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php', 'MWLoggerSpi' => __DIR__ . '/includes/debug/logger/Spi.php', diff --git a/includes/debug/logger/Logger.php b/includes/debug/logger/Logger.php index c54d2412cc..960faefd92 100644 --- a/includes/debug/logger/Logger.php +++ b/includes/debug/logger/Logger.php @@ -199,12 +199,18 @@ class MWLogger implements \Psr\Log\LoggerInterface { /** - * Get a named logger instance from the currently configured logger factory. + * Get the registered service provider. * - * @param string $channel Logger channel (name) - * @return MWLogger + * 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 getInstance( $channel ) { + public static function getProvider() { if ( self::$spi === null ) { global $wgMWLoggerDefaultSpi; $provider = ObjectFactory::getObjectFromSpec( @@ -212,8 +218,17 @@ class MWLogger implements \Psr\Log\LoggerInterface { ); self::registerProvider( $provider ); } + return self::$spi; + } - return self::$spi->getLogger( $channel ); + /** + * Get a named logger instance from the currently configured logger factory. + * + * @param string $channel Logger channel (name) + * @return MWLogger + */ + public static function getInstance( $channel ) { + return self::getProvider()->getLogger( $channel ); } } diff --git a/includes/debug/logger/monolog/SamplingHandler.php b/includes/debug/logger/monolog/SamplingHandler.php new file mode 100644 index 0000000000..d69da40130 --- /dev/null +++ b/includes/debug/logger/monolog/SamplingHandler.php @@ -0,0 +1,122 @@ + 'MWLoggerMonologSpi', + * 'args' => array( array( + * 'handlers' => array( + * 'some-handler' => array( ... ), + * 'sampled-some-handler' => array( + * 'class' => 'MWLoggerMonologSamplingHandler', + * 'args' => array( + * function() { + * return MWLogger::getProvider()->getHandler( 'some-handler'); + * }, + * 2, // emit logs with a 1:2 chance + * ), + * ), + * ), + * ) ), + * ); + * @endcode + * + * A sampled event stream can be useful for logging high frequency events in + * a production environment where you only need an idea of what is happening + * and are not concerned with capturing every occurence. Since the decision to + * handle or not handle a particular event is determined randomly, the + * resulting sampled log is not guaranteed to contain 1/N of the events that + * occurred in the application but based on [[Law of large numbers]] it will + * tend to be close to this ratio with a large number of attempts. + * + * @since 1.25 + * @author Bryan Davis + * @copyright © 2014 Bryan Davis and Wikimedia Foundation. + */ +class MWLoggerMonologSamplingHandler implements HandlerInterface { + + /** + * @var HandlerInterface $delegate + */ + protected $delegate; + + /** + * @var int $factor + */ + protected $factor; + + /** + * @param HandlerInterface $handler Wrapped handler + * @param int $factor Sample factor + */ + public function __construct( HandlerInterface $handler, $factor ) { + $this->delegate = $handler; + $this->factor = $factor; + } + + public function isHandling( array $record ) { + return $this->delegate->isHandling( $record ); + } + + public function handle( array $record ) { + if ( $this->isHandling( $record ) + && mt_rand( 1, $this->factor ) === 1 + ) { + return $this->delegate->handle( $record ); + } + return false; + } + + public function handleBatch( array $records ) { + foreach ( $records as $record ) { + $this->handle( $record ); + } + } + + public function pushProcessor( $callback ) { + $this->delegate->pushProcessor( $callback ); + return $this; + } + + public function popProcessor() { + return $this->delegate->popProcessor(); + } + + public function setFormatter( FormatterInterface $formatter ) { + $this->delegate->setFormatter( $formatter ); + return $this; + } + + public function getFormatter() { + return $this->delegate->getFormatter(); + } + +} diff --git a/includes/debug/logger/monolog/Spi.php b/includes/debug/logger/monolog/Spi.php index 4ad568763c..121dbe4522 100644 --- a/includes/debug/logger/monolog/Spi.php +++ b/includes/debug/logger/monolog/Spi.php @@ -199,7 +199,7 @@ class MWLoggerMonologSpi implements MWLoggerSpi { * @param string $name Processor name * @return callable */ - protected function getProcessor( $name ) { + public function getProcessor( $name ) { if ( !isset( $this->singletons['processors'][$name] ) ) { $spec = $this->config['processors'][$name]; $processor = ObjectFactory::getObjectFromSpec( $spec ); @@ -214,7 +214,7 @@ class MWLoggerMonologSpi implements MWLoggerSpi { * @param string $name Processor name * @return \Monolog\Handler\HandlerInterface */ - protected function getHandler( $name ) { + public function getHandler( $name ) { if ( !isset( $this->singletons['handlers'][$name] ) ) { $spec = $this->config['handlers'][$name]; $handler = ObjectFactory::getObjectFromSpec( $spec ); @@ -234,7 +234,7 @@ class MWLoggerMonologSpi implements MWLoggerSpi { * @param string $name Formatter name * @return \Monolog\Formatter\FormatterInterface */ - protected function getFormatter( $name ) { + public function getFormatter( $name ) { if ( !isset( $this->singletons['formatters'][$name] ) ) { $spec = $this->config['formatters'][$name]; $formatter = ObjectFactory::getObjectFromSpec( $spec ); -- 2.20.1