Skip to content

Commit bed54df

Browse files
wimulkemanWim Ulkemantaylorotwell
authored
[8.x] support action_level configuration (#40305)
* chore (logging): support action_level configuration Enable the usage of the action_level configuration for log channels. When the action_level configuration is used the choosen handler by the developer will be wrapped in a Fingerscrossed handler. The Fingerscrossed handler will buffer all log records that are at or above the provided level configuration. Only when a log record is provided at or above the action_level all the buffered records will be flushed to the log itself. This methodology helps developers to gather debug information about a process while only logging that debug information if the code hits for example a critical error. @see https://github.com/Seldaek/monolog/blob/main/src/Monolog/Handler/FingersCrossedHandler.php @see https://symfony.com/doc/current/logging.html#handlers-that-modify-log-entries * fix (test): support older Monolog version Support checking the wrapped handler in older Monolog versions (pre 1.x). In the older versions the getHandler method was not yet available. The handler was only set as a protected property. * formatting Co-authored-by: Wim Ulkeman <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
1 parent 926cde9 commit bed54df

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed

src/Illuminate/Log/LogManager.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use InvalidArgumentException;
88
use Monolog\Formatter\LineFormatter;
99
use Monolog\Handler\ErrorLogHandler;
10+
use Monolog\Handler\FingersCrossedHandler;
1011
use Monolog\Handler\FormattableHandlerInterface;
1112
use Monolog\Handler\HandlerInterface;
1213
use Monolog\Handler\RotatingFileHandler;
@@ -416,6 +417,10 @@ protected function prepareHandlers(array $handlers)
416417
*/
417418
protected function prepareHandler(HandlerInterface $handler, array $config = [])
418419
{
420+
if (isset($config['action_level'])) {
421+
$handler = new FingersCrossedHandler($handler, $this->actionLevel($config));
422+
}
423+
419424
if (Monolog::API !== 1 && (Monolog::API !== 2 || ! $handler instanceof FormattableHandlerInterface)) {
420425
return $handler;
421426
}

src/Illuminate/Log/ParsesLogConfiguration.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ protected function level(array $config)
4949
throw new InvalidArgumentException('Invalid log level.');
5050
}
5151

52+
/**
53+
* Parse the action level from the given configuration.
54+
*
55+
* @param array $config
56+
* @return int
57+
*/
58+
protected function actionLevel(array $config)
59+
{
60+
$level = $config['action_level'] ?? 'debug';
61+
62+
if (isset($this->levels[$level])) {
63+
return $this->levels[$level];
64+
}
65+
66+
throw new InvalidArgumentException('Invalid log action level.');
67+
}
68+
5269
/**
5370
* Extract the log channel from the given configuration.
5471
*

tests/Log/LogManagerTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Monolog\Formatter\HtmlFormatter;
88
use Monolog\Formatter\LineFormatter;
99
use Monolog\Formatter\NormalizerFormatter;
10+
use Monolog\Handler\FingersCrossedHandler;
1011
use Monolog\Handler\LogEntriesHandler;
1112
use Monolog\Handler\NewRelicHandler;
1213
use Monolog\Handler\NullHandler;
@@ -432,4 +433,52 @@ public function __invoke()
432433

433434
$this->assertSame(storage_path('logs/custom.log'), $url->getValue($handler));
434435
}
436+
437+
public function testWrappingHandlerInFingersCrossedWhenActionLevelIsUsed()
438+
{
439+
$config = $this->app['config'];
440+
441+
$config->set('logging.channels.fingerscrossed', [
442+
'driver' => 'monolog',
443+
'handler' => StreamHandler::class,
444+
'level' => 'debug',
445+
'action_level' => 'critical',
446+
'with' => [
447+
'stream' => 'php://stderr',
448+
'bubble' => false,
449+
],
450+
]);
451+
452+
$manager = new LogManager($this->app);
453+
454+
// create logger with handler specified from configuration
455+
$logger = $manager->channel('fingerscrossed');
456+
$handlers = $logger->getLogger()->getHandlers();
457+
458+
$this->assertInstanceOf(Logger::class, $logger);
459+
$this->assertCount(1, $handlers);
460+
461+
$expectedFingersCrossedHandler = $handlers[0];
462+
$this->assertInstanceOf(FingersCrossedHandler::class, $expectedFingersCrossedHandler, );
463+
464+
$activationStrategyProp = new ReflectionProperty(get_class($expectedFingersCrossedHandler), 'activationStrategy');
465+
$activationStrategyProp->setAccessible(true);
466+
$activationStrategyValue = $activationStrategyProp->getValue($expectedFingersCrossedHandler);
467+
468+
$actionLevelProp = new ReflectionProperty(get_class($activationStrategyValue), 'actionLevel');
469+
$actionLevelProp->setAccessible(true);
470+
$actionLevelValue = $actionLevelProp->getValue($activationStrategyValue);
471+
472+
$this->assertEquals(Monolog::CRITICAL, $actionLevelValue);
473+
474+
if (method_exists($expectedFingersCrossedHandler, 'getHandler')) {
475+
$expectedStreamHandler = $expectedFingersCrossedHandler->getHandler();
476+
} else {
477+
$handlerProp = new ReflectionProperty(get_class($expectedFingersCrossedHandler), 'handler');
478+
$handlerProp->setAccessible(true);
479+
$expectedStreamHandler = $handlerProp->getValue($expectedFingersCrossedHandler);
480+
}
481+
$this->assertInstanceOf(StreamHandler::class, $expectedStreamHandler);
482+
$this->assertEquals(Monolog::DEBUG, $expectedStreamHandler->getLevel());
483+
}
435484
}

0 commit comments

Comments
 (0)