mirror of
https://github.com/Seldaek/monolog.git
synced 2025-08-05 04:37:38 +02:00
Merge pull request #1083 from krisbuist/threshold-handler
Add an OverflowHandler
This commit is contained in:
@@ -128,6 +128,10 @@
|
|||||||
has accessors to read out the information.
|
has accessors to read out the information.
|
||||||
- [_HandlerWrapper_](../src/Monolog/Handler/HandlerWrapper.php): A simple handler wrapper you can inherit from to create
|
- [_HandlerWrapper_](../src/Monolog/Handler/HandlerWrapper.php): A simple handler wrapper you can inherit from to create
|
||||||
your own wrappers easily.
|
your own wrappers easily.
|
||||||
|
- [_OverflowHandler_](../src/Monolog/Handler/OverflowHandler.php): This handler will buffer all the log messages it
|
||||||
|
receives, up until a configured threshold of number of messages of a certain lever is reached, after it will pass all
|
||||||
|
log messages to the wrapped handler. Useful for applying in batch processing when you're only interested in significant
|
||||||
|
failures instead of minor, single erroneous events.
|
||||||
|
|
||||||
## Formatters
|
## Formatters
|
||||||
|
|
||||||
|
127
src/Monolog/Handler/OverflowHandler.php
Normal file
127
src/Monolog/Handler/OverflowHandler.php
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Monolog package.
|
||||||
|
*
|
||||||
|
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to only pass log messages when a certain threshold of number of messages is reached.
|
||||||
|
*
|
||||||
|
* This can be useful in cases of processing a batch of data, but you're for example only interested
|
||||||
|
* in case it fails catastrophically instead of a warning for 1 or 2 events. Worse things can happen, right?
|
||||||
|
*
|
||||||
|
* Usage example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* $log = new Logger('application');
|
||||||
|
* $handler = new SomeHandler(...)
|
||||||
|
*
|
||||||
|
* // Pass all warnings to the handler when more than 10 & all error messages when more then 5
|
||||||
|
* $overflow = new OverflowHandler($handler, [Logger::WARNING => 10, Logger::ERROR => 5]);
|
||||||
|
*
|
||||||
|
* $log->pushHandler($overflow);
|
||||||
|
*```
|
||||||
|
*
|
||||||
|
* @author Kris Buist <krisbuist@gmail.com>
|
||||||
|
*/
|
||||||
|
class OverflowHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
/** @var HandlerInterface */
|
||||||
|
private $handler;
|
||||||
|
|
||||||
|
/** @var int[] */
|
||||||
|
private $thresholdMap = [
|
||||||
|
Logger::DEBUG => 0,
|
||||||
|
Logger::INFO => 0,
|
||||||
|
Logger::NOTICE => 0,
|
||||||
|
Logger::WARNING => 0,
|
||||||
|
Logger::ERROR => 0,
|
||||||
|
Logger::CRITICAL => 0,
|
||||||
|
Logger::ALERT => 0,
|
||||||
|
Logger::EMERGENCY => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer of all messages passed to the handler before the threshold was reached
|
||||||
|
*
|
||||||
|
* @var mixed[][]
|
||||||
|
*/
|
||||||
|
private $buffer = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HandlerInterface $handler
|
||||||
|
* @param int[] $thresholdMap Dictionary of logger level => threshold
|
||||||
|
* @param int $level
|
||||||
|
* @param bool $bubble
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
HandlerInterface $handler,
|
||||||
|
array $thresholdMap = [],
|
||||||
|
int $level = Logger::DEBUG,
|
||||||
|
bool $bubble = true
|
||||||
|
) {
|
||||||
|
$this->handler = $handler;
|
||||||
|
foreach ($thresholdMap as $thresholdLevel => $threshold) {
|
||||||
|
$this->thresholdMap[$thresholdLevel] = $threshold;
|
||||||
|
}
|
||||||
|
parent::__construct($level, $bubble);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a record.
|
||||||
|
*
|
||||||
|
* All records may be passed to this method, and the handler should discard
|
||||||
|
* those that it does not want to handle.
|
||||||
|
*
|
||||||
|
* The return value of this function controls the bubbling process of the handler stack.
|
||||||
|
* Unless the bubbling is interrupted (by returning true), the Logger class will keep on
|
||||||
|
* calling further handlers in the stack with a given log record.
|
||||||
|
*
|
||||||
|
* @param array $record The record to handle
|
||||||
|
*
|
||||||
|
* @return Boolean true means that this handler handled the record, and that bubbling is not permitted.
|
||||||
|
* false means the record was either not processed or that this handler allows bubbling.
|
||||||
|
*/
|
||||||
|
public function handle(array $record): bool
|
||||||
|
{
|
||||||
|
if ($record['level'] < $this->level) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$level = $record['level'];
|
||||||
|
|
||||||
|
if (!isset($this->thresholdMap[$level])) {
|
||||||
|
$this->thresholdMap[$level] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->thresholdMap[$level] > 0) {
|
||||||
|
// The overflow threshold is not yet reached, so we're buffering the record and lowering the threshold by 1
|
||||||
|
$this->thresholdMap[$level]--;
|
||||||
|
$this->buffer[$level][] = $record;
|
||||||
|
return false === $this->bubble;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->thresholdMap[$level] == 0) {
|
||||||
|
// This current message is breaking the threshold. Flush the buffer and continue handling the current record
|
||||||
|
foreach ($this->buffer[$level] ?? [] as $buffered) {
|
||||||
|
$this->handler->handle($buffered);
|
||||||
|
}
|
||||||
|
$this->thresholdMap[$level]--;
|
||||||
|
unset($this->buffer[$level]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->handler->handle($record);
|
||||||
|
|
||||||
|
return false === $this->bubble;
|
||||||
|
}
|
||||||
|
}
|
108
tests/Monolog/Handler/OverflowHandlerTest.php
Normal file
108
tests/Monolog/Handler/OverflowHandlerTest.php
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Monolog package.
|
||||||
|
*
|
||||||
|
* (c) Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
use Monolog\Test\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kris Buist <krisbuist@gmail.com>
|
||||||
|
* @covers \Monolog\Handler\OverflowHandler
|
||||||
|
*/
|
||||||
|
class OverflowHandlerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testNotPassingRecordsBeneathLogLevel()
|
||||||
|
{
|
||||||
|
$testHandler = new TestHandler();
|
||||||
|
$handler = new OverflowHandler($testHandler, [], Logger::INFO);
|
||||||
|
$handler->handle($this->getRecord(Logger::DEBUG));
|
||||||
|
$this->assertFalse($testHandler->hasDebugRecords());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPassThroughWithoutThreshold()
|
||||||
|
{
|
||||||
|
$testHandler = new TestHandler();
|
||||||
|
$handler = new OverflowHandler($testHandler, [], Logger::INFO);
|
||||||
|
|
||||||
|
$handler->handle($this->getRecord(Logger::INFO, 'Info 1'));
|
||||||
|
$handler->handle($this->getRecord(Logger::INFO, 'Info 2'));
|
||||||
|
$handler->handle($this->getRecord(Logger::WARNING, 'Warning 1'));
|
||||||
|
|
||||||
|
$this->assertTrue($testHandler->hasInfoThatContains('Info 1'));
|
||||||
|
$this->assertTrue($testHandler->hasInfoThatContains('Info 2'));
|
||||||
|
$this->assertTrue($testHandler->hasWarningThatContains('Warning 1'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function testHoldingMessagesBeneathThreshold()
|
||||||
|
{
|
||||||
|
$testHandler = new TestHandler();
|
||||||
|
$handler = new OverflowHandler($testHandler, [Logger::INFO => 3]);
|
||||||
|
|
||||||
|
$handler->handle($this->getRecord(Logger::DEBUG, 'debug 1'));
|
||||||
|
$handler->handle($this->getRecord(Logger::DEBUG, 'debug 2'));
|
||||||
|
|
||||||
|
foreach (range(1, 3) as $i) {
|
||||||
|
$handler->handle($this->getRecord(Logger::INFO, 'info ' . $i));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertTrue($testHandler->hasDebugThatContains('debug 1'));
|
||||||
|
$this->assertTrue($testHandler->hasDebugThatContains('debug 2'));
|
||||||
|
$this->assertFalse($testHandler->hasInfoRecords());
|
||||||
|
|
||||||
|
$handler->handle($this->getRecord(Logger::INFO, 'info 4'));
|
||||||
|
|
||||||
|
foreach (range(1, 4) as $i) {
|
||||||
|
$this->assertTrue($testHandler->hasInfoThatContains('info ' . $i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function testCombinedThresholds()
|
||||||
|
{
|
||||||
|
$testHandler = new TestHandler();
|
||||||
|
$handler = new OverflowHandler($testHandler, [Logger::INFO => 5, Logger::WARNING => 10]);
|
||||||
|
|
||||||
|
$handler->handle($this->getRecord(Logger::DEBUG));
|
||||||
|
|
||||||
|
foreach (range(1, 5) as $i) {
|
||||||
|
$handler->handle($this->getRecord(Logger::INFO, 'info ' . $i));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (range(1, 10) as $i) {
|
||||||
|
$handler->handle($this->getRecord(Logger::WARNING, 'warning ' . $i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only 1 DEBUG records
|
||||||
|
$this->assertCount(1, $testHandler->getRecords());
|
||||||
|
|
||||||
|
$handler->handle($this->getRecord(Logger::INFO, 'info final'));
|
||||||
|
|
||||||
|
// 1 DEBUG + 5 buffered INFO + 1 new INFO
|
||||||
|
$this->assertCount(7, $testHandler->getRecords());
|
||||||
|
|
||||||
|
$handler->handle($this->getRecord(Logger::WARNING, 'warning final'));
|
||||||
|
|
||||||
|
// 1 DEBUG + 6 INFO + 10 buffered WARNING + 1 new WARNING
|
||||||
|
$this->assertCount(18, $testHandler->getRecords());
|
||||||
|
|
||||||
|
$handler->handle($this->getRecord(Logger::INFO, 'Another info'));
|
||||||
|
$handler->handle($this->getRecord(Logger::WARNING, 'Anther warning'));
|
||||||
|
|
||||||
|
// 1 DEBUG + 6 INFO + 11 WARNING + 1 new INFO + 1 new WARNING
|
||||||
|
$this->assertCount(20, $testHandler->getRecords());
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user