diff --git a/README.mdown b/README.mdown index 1225bebf..1ad01692 100644 --- a/README.mdown +++ b/README.mdown @@ -186,6 +186,8 @@ Handlers sent to all the handlers it is configured with. - _FilterHandler_: This handler only lets records of the given levels through to the wrapped handler. +- _SamplingHandler_: Wraps around another handler and lets you sample records + if you only want to store some of them. - _TestHandler_: Used for testing, it records everything that is sent to it and has accessors to read out the information. - _WhatFailureGroupHandler_: This handler extends the _GroupHandler_ ignoring diff --git a/src/Monolog/Handler/SamplingHandler.php b/src/Monolog/Handler/SamplingHandler.php new file mode 100644 index 00000000..6059dd77 --- /dev/null +++ b/src/Monolog/Handler/SamplingHandler.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Sampling handler + * + * 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 occurrence. 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 the Law of large numbers, it will + * tend to be close to this ratio with a large number of attempts. + * + * @author Bryan Davis + * @author Kunal Mehta + */ +class SamplingHandler extends AbstractHandler +{ + /** + * @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) + { + parent::__construct(); + $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 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/tests/Monolog/Handler/SamplingHandlerTest.php b/tests/Monolog/Handler/SamplingHandlerTest.php new file mode 100644 index 00000000..5e4557cf --- /dev/null +++ b/tests/Monolog/Handler/SamplingHandlerTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @covers Monolog\Handler\SamplingHandler::handle + */ +class SamplingHandlerTest extends TestCase +{ + public function testHandle() + { + $testHandler = new TestHandler(); + $handler = new SamplingHandler($testHandler, 2); + for ($i=0; $i<10000; $i++) + { + $handler->handle($this->getRecord()); + } + $count = count($testHandler->getRecords()); + // $count should be half of 10k, so between 4k and 6k + $this->assertLessThan(6000, $count); + $this->assertGreaterThan(4000, $count); + } +}