feature #40761 [MonologBridge] Reset loggers on workers (l-vo)

This PR was merged into the 5.3-dev branch.

Discussion
----------

[MonologBridge] Reset loggers on workers

| Q             | A
| ------------- | ---
| Branch?       | 5.x
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       |
| License       | MIT
| Doc PR        |

This PR tries to solve some problems with buffered handlers (FingerCrossed) in workers.

Let's consider the default configuration (`stop_buffering: true`):
- When the threshold is crossed, all logs are flushed. Logs for the current message but also logs of previous messages in the buffer. Although buffer is limited `buffer_size`, it's a shame to keep logs of previous messages.
- When the threshold is crossed, buffering is disabled. So finger crossed configuration is not used anymore, all the logs are flushed as soon as they are written.

Then with (`stop_buffering: false`) (why isn't this the default configuration ?)
- It's a bit better since buffering isn't disabled when the threshold is crossed
- Like with `stop_buffering: true`, logs of previous messages are kept in memory

In a similar way of `DoctrineClearEntityManagerWorkerSubscriber`, this PR adds a `ResetLoggersWorkerSubscribber` to reset resettable loggers.

Integration in Monolog bundle: symfony/monolog-bundle#403

Commits
-------

1d2f7f1f87 [Messenger] Reset loggers on workers
This commit is contained in:
Fabien Potencier 2021-04-13 08:43:12 +02:00
commit ff0cf61278
4 changed files with 141 additions and 1 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
5.3
---
* Add `ResetLoggersWorkerSubscriber` to reset buffered logs in messenger workers
5.2.0
-----

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Monolog\Messenger;
use Monolog\ResettableInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
/**
* Reset loggers between messages being handled to release buffered handler logs.
*
* @author Laurent VOULLEMIER <laurent.voullemier@gmail.com>
*/
class ResetLoggersWorkerSubscriber implements EventSubscriberInterface
{
private $loggers;
public function __construct(iterable $loggers)
{
$this->loggers = $loggers;
}
public static function getSubscribedEvents(): array
{
return [
WorkerMessageHandledEvent::class => 'resetLoggers',
WorkerMessageFailedEvent::class => 'resetLoggers',
];
}
public function resetLoggers(): void
{
foreach ($this->loggers as $logger) {
if ($logger instanceof ResettableInterface) {
$logger->reset();
}
}
}
}

View File

@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bridge\Monolog\Tests\Messenger;
use Monolog\Handler\BufferHandler;
use Monolog\Handler\TestHandler;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Monolog\Logger;
use Symfony\Bridge\Monolog\Messenger\ResetLoggersWorkerSubscriber;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerRunningEvent;
use Symfony\Component\Messenger\Handler\HandlersLocator;
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
use Symfony\Component\Messenger\Worker;
class ResetLoggersWorkerSubscriberTest extends TestCase
{
public function testLogsAreFlushed()
{
$loggerTestHandler = new TestHandler();
$loggerTestHandler->setSkipReset(true);
$logger = new Logger('', [new BufferHandler($loggerTestHandler)]);
$message = new class() {
};
$handler = static function (object $message) use ($logger): void {
$logger->info('Message of class {class} is being handled', ['class' => \get_class($message)]);
};
$handlersMiddleware = new HandleMessageMiddleware(new HandlersLocator([
\get_class($message) => [$handler],
]));
$eventDispatcher = new EventDispatcher();
$eventDispatcher->addSubscriber(new ResetLoggersWorkerSubscriber([$logger]));
$eventDispatcher->addListener(WorkerRunningEvent::class, static function (WorkerRunningEvent $event): void {
$event->getWorker()->stop(); // Limit the worker to one loop
});
$bus = new MessageBus([$handlersMiddleware]);
$worker = new Worker([$this->createReceiver($message)], $bus, $eventDispatcher);
$worker->run();
$this->assertCount(1, $loggerTestHandler->getRecords());
}
private function createReceiver(object $message): ReceiverInterface
{
return new class($message) implements ReceiverInterface {
private $message;
public function __construct(object $message)
{
$this->message = $message;
}
public function get(): iterable
{
return [new Envelope($this->message)];
}
public function ack(Envelope $envelope): void
{
}
public function reject(Envelope $envelope): void
{
}
};
}
}

View File

@ -28,7 +28,8 @@
"symfony/security-core": "^4.4|^5.0",
"symfony/var-dumper": "^4.4|^5.0",
"symfony/mailer": "^4.4|^5.0",
"symfony/mime": "^4.4|^5.0"
"symfony/mime": "^4.4|^5.0",
"symfony/messenger": "^4.4|^5.0"
},
"conflict": {
"symfony/console": "<4.4",