1
0
mirror of https://github.com/Seldaek/monolog.git synced 2025-08-01 19:00:20 +02:00

Added a new ResettableInterface and implemented it where possible.

When one use Monolog in a long process like an AMQP worker with a
`FingersCrossedHandler` or `BufferHandler` there is a drawback: as soon as there
is an AMQP message that generate a log >= error (for example), all next AMQP
messages will output logs, even if theses messages don't generate log where
level >= error.

In the same context there is a drawback for processor that add an UUID to the
logs. The UUID should change for each AMQP messages.

---

This patch address this issue with a new interface: `ResettableInterface` interface.
Side note: `reset()`, `flush()`, `clear()`,  are already used in Monolog. So
basically, one can use the `reset()` on the `Logger` and on some
`Handler`s / `Processor`s.

It's especially useful for

* the `FingersCrossedHandler`: it `close()` the buffer, then it `clear()` the buffer.
* the `BufferHandler`: it `flush()` the buffer, then it `clear()` the buffer.
* the `UidProcessor`: it renew the `uid`.
This commit is contained in:
Grégoire Pineau
2017-05-31 17:19:21 +02:00
parent c465e11445
commit 0625068bf0
16 changed files with 200 additions and 15 deletions

View File

@@ -19,6 +19,8 @@
* Fixed table row styling issues in HtmlFormatter * Fixed table row styling issues in HtmlFormatter
* Fixed RavenHandler dropping the message when logging exception * Fixed RavenHandler dropping the message when logging exception
* Fixed WhatFailureGroupHandler skipping processors when using handleBatch * Fixed WhatFailureGroupHandler skipping processors when using handleBatch
* Added a `ResettableInterface` in order to reset/reset/clear/flush handlers and processors
and implement it where possible
### 1.23.0 (2017-06-19) ### 1.23.0 (2017-06-19)

View File

@@ -11,16 +11,17 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\Formatter\LineFormatter; use Monolog\Formatter\LineFormatter;
use Monolog\Logger;
use Monolog\ResettableInterface;
/** /**
* Base Handler class providing the Handler structure * Base Handler class providing the Handler structure
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
abstract class AbstractHandler implements HandlerInterface abstract class AbstractHandler implements HandlerInterface, ResettableInterface
{ {
protected $level = Logger::DEBUG; protected $level = Logger::DEBUG;
protected $bubble = true; protected $bubble = true;
@@ -174,6 +175,17 @@ abstract class AbstractHandler implements HandlerInterface
} }
} }
public function reset()
{
$this->close();
foreach ($this->processors as $processor) {
if ($processor instanceof ResettableInterface) {
$processor->reset();
}
}
}
/** /**
* Gets the default formatter. * Gets the default formatter.
* *

View File

@@ -11,6 +11,8 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\ResettableInterface;
/** /**
* Base Handler class providing the Handler structure * Base Handler class providing the Handler structure
* *

View File

@@ -69,14 +69,19 @@ class BrowserConsoleHandler extends AbstractProcessingHandler
} elseif ($format === 'js') { } elseif ($format === 'js') {
static::writeOutput(static::generateScript()); static::writeOutput(static::generateScript());
} }
static::reset(); static::resetStatic();
} }
} }
public function reset()
{
self::resetStatic();
}
/** /**
* Forget all logged records * Forget all logged records
*/ */
public static function reset() public static function resetStatic()
{ {
static::$records = array(); static::$records = array();
} }

View File

@@ -12,6 +12,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface;
/** /**
* Buffers all records until closing the handler and then pass them as batch. * Buffers all records until closing the handler and then pass them as batch.
@@ -114,4 +115,13 @@ class BufferHandler extends AbstractHandler
$this->bufferSize = 0; $this->bufferSize = 0;
$this->buffer = array(); $this->buffer = array();
} }
public function reset()
{
parent::reset();
if ($this->handler instanceof ResettableInterface) {
$this->handler->reset();
}
}
} }

View File

@@ -14,6 +14,7 @@ namespace Monolog\Handler;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; use Monolog\Handler\FingersCrossed\ActivationStrategyInterface;
use Monolog\Logger; use Monolog\Logger;
use Monolog\ResettableInterface;
/** /**
* Buffers all records until a certain level is reached * Buffers all records until a certain level is reached
@@ -147,7 +148,14 @@ class FingersCrossedHandler extends AbstractHandler
*/ */
public function reset() public function reset()
{ {
parent::reset();
$this->buffer = array();
$this->buffering = true; $this->buffering = true;
if ($this->handler instanceof ResettableInterface) {
$this->handler->reset();
}
} }
/** /**

View File

@@ -12,6 +12,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
use Monolog\ResettableInterface;
/** /**
* Forwards records to multiple handlers * Forwards records to multiple handlers
@@ -90,6 +91,17 @@ class GroupHandler extends AbstractHandler
} }
} }
public function reset()
{
parent::reset();
foreach ($this->handlers as $handler) {
if ($handler instanceof ResettableInterface) {
$handler->reset();
}
}
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */

View File

@@ -11,6 +11,7 @@
namespace Monolog\Handler; namespace Monolog\Handler;
use Monolog\ResettableInterface;
use Monolog\Formatter\FormatterInterface; use Monolog\Formatter\FormatterInterface;
/** /**
@@ -30,7 +31,7 @@ use Monolog\Formatter\FormatterInterface;
* *
* @author Alexey Karapetov <alexey@karapetov.com> * @author Alexey Karapetov <alexey@karapetov.com>
*/ */
class HandlerWrapper implements HandlerInterface class HandlerWrapper implements HandlerInterface, ResettableInterface
{ {
/** /**
* @var HandlerInterface * @var HandlerInterface
@@ -105,4 +106,11 @@ class HandlerWrapper implements HandlerInterface
{ {
return $this->handler->getFormatter(); return $this->handler->getFormatter();
} }
public function reset()
{
if ($this->handler instanceof ResettableInterface) {
return $this->handler->reset();
}
}
} }

View File

@@ -25,7 +25,7 @@ use Exception;
* *
* @author Jordi Boggiano <j.boggiano@seld.be> * @author Jordi Boggiano <j.boggiano@seld.be>
*/ */
class Logger implements LoggerInterface class Logger implements LoggerInterface, ResettableInterface
{ {
/** /**
* Detailed debug information * Detailed debug information
@@ -354,6 +354,21 @@ class Logger implements LoggerInterface
return true; return true;
} }
public function reset()
{
foreach ($this->handlers as $handler) {
if ($handler instanceof ResettableInterface) {
$handler->reset();
}
}
foreach ($this->processors as $processor) {
if ($processor instanceof ResettableInterface) {
$processor->reset();
}
}
}
/** /**
* Adds a log record at the DEBUG level. * Adds a log record at the DEBUG level.
* *

View File

@@ -11,12 +11,14 @@
namespace Monolog\Processor; namespace Monolog\Processor;
use Monolog\ResettableInterface;
/** /**
* Adds a unique identifier into records * Adds a unique identifier into records
* *
* @author Simon Mönch <sm@webfactory.de> * @author Simon Mönch <sm@webfactory.de>
*/ */
class UidProcessor class UidProcessor implements ResettableInterface
{ {
private $uid; private $uid;
@@ -26,7 +28,8 @@ class UidProcessor
throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32');
} }
$this->uid = substr(hash('md5', uniqid('', true)), 0, $length);
$this->uid = $this->generateUid($length);
} }
public function __invoke(array $record) public function __invoke(array $record)
@@ -43,4 +46,14 @@ class UidProcessor
{ {
return $this->uid; return $this->uid;
} }
public function reset()
{
$this->uid = $this->generateUid(strlen($this->uid));
}
private function generateUid($length)
{
return substr(hash('md5', uniqid('', true)), 0, $length);
}
} }

View File

@@ -0,0 +1,25 @@
<?php
/*
* 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 or Processor implementing this interface will be reset when Logger::reset() is called.
*
* Resetting an Handler or a Processor usually means cleaning all buffers or
* resetting in its internal state.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
interface ResettableInterface
{
public function reset();
}

View File

@@ -21,7 +21,7 @@ class BrowserConsoleHandlerTest extends TestCase
{ {
protected function setUp() protected function setUp()
{ {
BrowserConsoleHandler::reset(); BrowserConsoleHandler::resetStatic();
} }
protected function generateScript() protected function generateScript()

View File

@@ -21,7 +21,7 @@ class ChromePHPHandlerTest extends TestCase
{ {
protected function setUp() protected function setUp()
{ {
TestChromePHPHandler::reset(); TestChromePHPHandler::resetStatic();
$_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0'; $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0';
} }
@@ -136,7 +136,7 @@ class TestChromePHPHandler extends ChromePHPHandler
{ {
protected $headers = array(); protected $headers = array();
public static function reset() public static function resetStatic()
{ {
self::$initialized = false; self::$initialized = false;
self::$overflowed = false; self::$overflowed = false;

View File

@@ -58,7 +58,7 @@ class FingersCrossedHandlerTest extends TestCase
* @covers Monolog\Handler\FingersCrossedHandler::activate * @covers Monolog\Handler\FingersCrossedHandler::activate
* @covers Monolog\Handler\FingersCrossedHandler::reset * @covers Monolog\Handler\FingersCrossedHandler::reset
*/ */
public function testHandleRestartBufferingAfterReset() public function testHandleResetBufferingAfterReset()
{ {
$test = new TestHandler(); $test = new TestHandler();
$handler = new FingersCrossedHandler($test); $handler = new FingersCrossedHandler($test);
@@ -76,7 +76,7 @@ class FingersCrossedHandlerTest extends TestCase
* @covers Monolog\Handler\FingersCrossedHandler::handle * @covers Monolog\Handler\FingersCrossedHandler::handle
* @covers Monolog\Handler\FingersCrossedHandler::activate * @covers Monolog\Handler\FingersCrossedHandler::activate
*/ */
public function testHandleRestartBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled() public function testHandleResetBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled()
{ {
$test = new TestHandler(); $test = new TestHandler();
$handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false); $handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false);

View File

@@ -21,7 +21,7 @@ class FirePHPHandlerTest extends TestCase
{ {
public function setUp() public function setUp()
{ {
TestFirePHPHandler::reset(); TestFirePHPHandler::resetStatic();
$_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0'; $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0';
} }
@@ -77,7 +77,7 @@ class TestFirePHPHandler extends FirePHPHandler
{ {
protected $headers = array(); protected $headers = array();
public static function reset() public static function resetStatic()
{ {
self::$initialized = false; self::$initialized = false;
self::$sendHeaders = true; self::$sendHeaders = true;

View File

@@ -614,4 +614,77 @@ class LoggerTest extends \PHPUnit_Framework_TestCase
$logger->pushHandler($handler); $logger->pushHandler($handler);
$logger->info('test'); $logger->info('test');
} }
public function testReset()
{
$logger = new Logger('app');
$testHandler = new Handler\TestHandler();
$bufferHandler = new Handler\BufferHandler($testHandler);
$groupHandler = new Handler\GroupHandler(array($bufferHandler));
$fingersCrossedHandler = new Handler\FingersCrossedHandler($groupHandler);
$logger->pushHandler($fingersCrossedHandler);
$processorUid1 = new Processor\UidProcessor(10);
$uid1 = $processorUid1->getUid();
$groupHandler->pushProcessor($processorUid1);
$processorUid2 = new Processor\UidProcessor(5);
$uid2 = $processorUid2->getUid();
$logger->pushProcessor($processorUid2);
$getProperty = function ($object, $property) {
$reflectionProperty = new \ReflectionProperty(get_class($object), $property);
$reflectionProperty->setAccessible(true);
return $reflectionProperty->getValue($object);
};
$that = $this;
$assertBufferOfBufferHandlerEmpty = function () use ($getProperty, $bufferHandler, $that) {
$that->assertEmpty($getProperty($bufferHandler, 'buffer'));
};
$assertBuffersEmpty = function() use ($assertBufferOfBufferHandlerEmpty, $getProperty, $fingersCrossedHandler, $that) {
$assertBufferOfBufferHandlerEmpty();
$that->assertEmpty($getProperty($fingersCrossedHandler, 'buffer'));
};
$logger->debug('debug');
$logger->reset();
$assertBuffersEmpty();
$this->assertFalse($testHandler->hasDebugRecords());
$this->assertFalse($testHandler->hasErrorRecords());
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
$logger->debug('debug');
$logger->error('error');
$logger->reset();
$assertBuffersEmpty();
$this->assertTrue($testHandler->hasDebugRecords());
$this->assertTrue($testHandler->hasErrorRecords());
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
$logger->info('info');
$this->assertNotEmpty($getProperty($fingersCrossedHandler, 'buffer'));
$assertBufferOfBufferHandlerEmpty();
$this->assertFalse($testHandler->hasInfoRecords());
$logger->reset();
$assertBuffersEmpty();
$this->assertFalse($testHandler->hasInfoRecords());
$this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
$this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
$logger->notice('notice');
$logger->emergency('emergency');
$logger->reset();
$assertBuffersEmpty();
$this->assertFalse($testHandler->hasInfoRecords());
$this->assertTrue($testHandler->hasNoticeRecords());
$this->assertTrue($testHandler->hasEmergencyRecords());
$this->assertNotSame($uid1, $processorUid1->getUid());
$this->assertNotSame($uid2, $processorUid2->getUid());
}
} }