From b2d356bbf25cc0129ce425196b741000c2cda113 Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 5 Apr 2011 09:41:27 +0200 Subject: [PATCH] Added a BufferHandler to allow batch processing of the records --- src/Monolog/Handler/AbstractHandler.php | 7 ++ src/Monolog/Handler/BufferHandler.php | 72 +++++++++++++++++++++ src/Monolog/Handler/HandlerInterface.php | 8 +++ tests/Monolog/Handler/BufferHandlerTest.php | 67 +++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 src/Monolog/Handler/BufferHandler.php create mode 100644 tests/Monolog/Handler/BufferHandlerTest.php diff --git a/src/Monolog/Handler/AbstractHandler.php b/src/Monolog/Handler/AbstractHandler.php index 8e2ab496..e5db2146 100644 --- a/src/Monolog/Handler/AbstractHandler.php +++ b/src/Monolog/Handler/AbstractHandler.php @@ -62,6 +62,13 @@ abstract class AbstractHandler implements HandlerInterface return false === $this->bubble; } + public function handleBatch(array $records) + { + foreach ($records as $record) { + $this->handle($record); + } + } + abstract public function write(array $record); public function close() diff --git a/src/Monolog/Handler/BufferHandler.php b/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 00000000..efc4b25c --- /dev/null +++ b/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + */ +class BufferHandler extends AbstractHandler +{ + protected $handler; + protected $bufferSize; + protected $buffer = array(); + + /** + * @param HandlerInterface $handler Handler. + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param Boolean $bubble + */ + public function __construct(HandlerInterface $handler, $bufferSize = 0, $bubble = false) + { + $this->handler = $handler; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + } + + /** + * Handles a record + * + * Records are buffered until closing the handler. + * + * @param array $record Records + * @return Boolean Whether the record was handled + */ + public function handle(array $record) + { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + + return false === $this->bubble; + } + + public function close() + { + $this->handler->handleBatch($this->buffer); + } + + /** + * Implemented to comply with the AbstractHandler requirements. Can not be called. + */ + public function write(array $record) + { + throw new \BadMethodCallException('This method should not be called directly on the FingersCrossedHandler.'); + } +} \ No newline at end of file diff --git a/src/Monolog/Handler/HandlerInterface.php b/src/Monolog/Handler/HandlerInterface.php index 7132f900..7833fa83 100644 --- a/src/Monolog/Handler/HandlerInterface.php +++ b/src/Monolog/Handler/HandlerInterface.php @@ -35,6 +35,14 @@ interface HandlerInterface */ function handle(array $record); + /** + * Handles a set of records. + * + * @param array $records The records to handle (an array of record arrays) + * @return Boolean Whether the handler stops the propagation in the stack or not. + */ + function handleBatch(array $records); + /** * Adds a processor in the stack. * diff --git a/tests/Monolog/Handler/BufferHandlerTest.php b/tests/Monolog/Handler/BufferHandlerTest.php new file mode 100644 index 00000000..369d1e7d --- /dev/null +++ b/tests/Monolog/Handler/BufferHandlerTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +class BufferHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testHandleBuffers() + { + $test = new TestHandler(); + $handler = new BufferHandler($test); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertFalse($test->hasDebugRecords()); + $this->assertFalse($test->hasInfoRecords()); + $handler->close(); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + } + + public function testDestructPropagatesRecords() + { + $test = new TestHandler(); + $handler = new BufferHandler($test); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->handle($this->getRecord(Logger::DEBUG)); + unset ($handler); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasDebugRecords()); + } + + public function testHandleBufferLimit() + { + $test = new TestHandler(); + $handler = new BufferHandler($test, 2); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->close(); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertFalse($test->hasDebugRecords()); + } + + protected function getRecord($level = Logger::WARNING) + { + return array( + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'log', + 'Record' => 'foo', + 'datetime' => new \DateTime, + 'extra' => array(), + ); + } +}