From 4694fc2f807ff2eb7f7c38d394f993c051f6c4a9 Mon Sep 17 00:00:00 2001
From: HypeMC <2445045+HypeMC@users.noreply.github.com>
Date: Tue, 10 May 2022 14:32:29 +0200
Subject: [PATCH] Add ability to configure the PsrLogMessageProcessor (#417)
---
DependencyInjection/Configuration.php | 15 ++++-
DependencyInjection/MonologExtension.php | 57 ++++++++++++++-----
Resources/config/schema/monolog-1.0.xsd | 9 ++-
.../DependencyInjection/ConfigurationTest.php | 35 ++++++++++++
.../FixtureMonologExtensionTest.php | 50 ++++++++++++++++
.../process_psr_3_messages_with_arguments.xml | 16 ++++++
...ocess_psr_3_messages_without_arguments.xml | 13 +++++
.../process_psr_3_messages_with_arguments.yml | 10 ++++
...ocess_psr_3_messages_without_arguments.yml | 6 ++
9 files changed, 194 insertions(+), 17 deletions(-)
create mode 100644 Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_with_arguments.xml
create mode 100644 Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_without_arguments.xml
create mode 100644 Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_with_arguments.yml
create mode 100644 Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_without_arguments.yml
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 339f524b..4a7b36a5 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -11,12 +11,12 @@
namespace Symfony\Bundle\MonologBundle\DependencyInjection;
+use Monolog\Logger;
use Symfony\Component\Config\Definition\BaseNode;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
-use Monolog\Logger;
/**
* This class contains the configuration information for the bundle
@@ -426,7 +426,18 @@ public function getConfigTreeBuilder()
->scalarNode('app_name')->defaultNull()->end()
->booleanNode('fill_extra_context')->defaultFalse()->end() // sentry
->booleanNode('include_stacktraces')->defaultFalse()->end()
- ->booleanNode('process_psr_3_messages')->defaultNull()->end()
+ ->arrayNode('process_psr_3_messages')
+ ->addDefaultsIfNotSet()
+ ->beforeNormalization()
+ ->ifTrue(static function ($v) { return !\is_array($v); })
+ ->then(static function ($v) { return ['enabled' => $v]; })
+ ->end()
+ ->children()
+ ->booleanNode('enabled')->defaultNull()->end()
+ ->scalarNode('date_format')->end()
+ ->booleanNode('remove_used_context_fields')->end()
+ ->end()
+ ->end()
->scalarNode('path')->defaultValue('%kernel.logs_dir%/%kernel.environment%.log')->end() // stream and rotating
->scalarNode('file_permission') // stream and rotating
->defaultNull()
diff --git a/DependencyInjection/MonologExtension.php b/DependencyInjection/MonologExtension.php
index d793d1cc..cb884faf 100644
--- a/DependencyInjection/MonologExtension.php
+++ b/DependencyInjection/MonologExtension.php
@@ -13,9 +13,10 @@
use Monolog\Attribute\AsMonologProcessor;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
+use Monolog\Handler\HandlerInterface;
use Monolog\Logger;
use Monolog\Processor\ProcessorInterface;
-use Monolog\Handler\HandlerInterface;
+use Monolog\Processor\PsrLogMessageProcessor;
use Monolog\ResettableInterface;
use Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy;
use Symfony\Bridge\Monolog\Processor\SwitchUserTokenProcessor;
@@ -203,21 +204,13 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler
$definition->setConfigurator(['Symfony\\Bundle\\MonologBundle\\MonologBundle', 'includeStacktraces']);
}
- if (null === $handler['process_psr_3_messages']) {
- $handler['process_psr_3_messages'] = !isset($handler['handler']) && !$handler['members'];
+ if (null === $handler['process_psr_3_messages']['enabled']) {
+ $handler['process_psr_3_messages']['enabled'] = !isset($handler['handler']) && !$handler['members'];
}
- if ($handler['process_psr_3_messages']) {
- if (method_exists($handlerClass, 'pushProcessor')) {
- $processorId = 'monolog.processor.psr_log_message';
- if (!$container->hasDefinition($processorId)) {
- $processor = new Definition('Monolog\\Processor\\PsrLogMessageProcessor');
- $processor->setPublic(false);
- $container->setDefinition($processorId, $processor);
- }
-
- $definition->addMethodCall('pushProcessor', [new Reference($processorId)]);
- }
+ if ($handler['process_psr_3_messages']['enabled'] && method_exists($handlerClass, 'pushProcessor')) {
+ $processorId = $this->buildPsrLogMessageProcessor($container, $handler['process_psr_3_messages']);
+ $definition->addMethodCall('pushProcessor', [new Reference($processorId)]);
}
switch ($handler['type']) {
@@ -1040,4 +1033,40 @@ private function getHandlerClassByType($handlerType)
return $typeToClassMapping[$handlerType];
}
+
+ private function buildPsrLogMessageProcessor(ContainerBuilder $container, array $processorOptions): string
+ {
+ static $hasConstructorArguments;
+
+ if (!isset($hasConstructorArguments)) {
+ $reflectionConstructor = (new \ReflectionClass(PsrLogMessageProcessor::class))->getConstructor();
+ $hasConstructorArguments = null !== $reflectionConstructor && $reflectionConstructor->getNumberOfParameters() > 0;
+ unset($reflectionConstructor);
+ }
+
+ $processorId = 'monolog.processor.psr_log_message';
+ $processorArguments = [];
+
+ unset($processorOptions['enabled']);
+
+ if (!empty($processorOptions)) {
+ if (!$hasConstructorArguments) {
+ throw new \RuntimeException('Monolog 1.26 or higher is required for the "date_format" and "remove_used_context_fields" options to be used.');
+ }
+ $processorArguments = [
+ $processorOptions['date_format'] ?? null,
+ $processorOptions['remove_used_context_fields'] ?? false,
+ ];
+ $processorId .= '.'.ContainerBuilder::hash($processorArguments);
+ }
+
+ if (!$container->hasDefinition($processorId)) {
+ $processor = new Definition(PsrLogMessageProcessor::class);
+ $processor->setPublic(false);
+ $processor->setArguments($processorArguments);
+ $container->setDefinition($processorId, $processor);
+ }
+
+ return $processorId;
+ }
}
diff --git a/Resources/config/schema/monolog-1.0.xsd b/Resources/config/schema/monolog-1.0.xsd
index eb00e68a..b00e969e 100644
--- a/Resources/config/schema/monolog-1.0.xsd
+++ b/Resources/config/schema/monolog-1.0.xsd
@@ -29,6 +29,7 @@
+
@@ -192,7 +193,13 @@
-
+
+
+
+
+
+
+
diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php
index 7548af94..2d537eee 100644
--- a/Tests/DependencyInjection/ConfigurationTest.php
+++ b/Tests/DependencyInjection/ConfigurationTest.php
@@ -489,6 +489,41 @@ public function testConsoleFormatterOptionsRename()
$this->assertArrayNotHasKey('console_formater_options', $config['handlers']['both2']);
}
+ /**
+ * @dataProvider processPsr3MessagesProvider
+ */
+ public function testWithProcessPsr3Messages(array $configuration, array $processedConfiguration): void
+ {
+ $configs = [
+ [
+ 'handlers' => [
+ 'main' => ['type' => 'stream'] + $configuration,
+ ],
+ ],
+ ];
+
+ $config = $this->process($configs);
+
+ $this->assertEquals($processedConfiguration, $config['handlers']['main']['process_psr_3_messages']);
+ }
+
+ public function processPsr3MessagesProvider(): iterable
+ {
+ yield 'Not specified' => [[], ['enabled' => null]];
+ yield 'Null' => [['process_psr_3_messages' => null], ['enabled' => true]];
+ yield 'True' => [['process_psr_3_messages' => true], ['enabled' => true]];
+ yield 'False' => [['process_psr_3_messages' => false], ['enabled' => false]];
+
+ yield 'Date format' => [
+ ['process_psr_3_messages' => ['date_format' => 'Y']],
+ ['date_format' => 'Y', 'enabled' => null],
+ ];
+ yield 'Enabled false & remove used' => [
+ ['process_psr_3_messages' => ['enabled' => false, 'remove_used_context_fields' => true]],
+ ['enabled' => false, 'remove_used_context_fields' => true],
+ ];
+ }
+
/**
* Processes an array of configurations and returns a compiled version.
*
diff --git a/Tests/DependencyInjection/FixtureMonologExtensionTest.php b/Tests/DependencyInjection/FixtureMonologExtensionTest.php
index fa73d732..5515ff21 100644
--- a/Tests/DependencyInjection/FixtureMonologExtensionTest.php
+++ b/Tests/DependencyInjection/FixtureMonologExtensionTest.php
@@ -12,6 +12,7 @@
namespace Symfony\Bundle\MonologBundle\Tests\DependencyInjection;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
+use Monolog\Processor\PsrLogMessageProcessor;
use Symfony\Bridge\Monolog\Processor\SwitchUserTokenProcessor;
use Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\LoggerChannelPass;
use Symfony\Bundle\MonologBundle\DependencyInjection\MonologExtension;
@@ -280,6 +281,55 @@ public function testPsr3MessageProcessingDisabled()
$this->assertNotContainsEquals(['pushProcessor', [new Reference('monolog.processor.psr_log_message')]], $methodCalls, 'The PSR-3 processor should not be enabled');
}
+ public function testPsrLogMessageProcessorHasConstructorArguments(): void
+ {
+ $reflectionConstructor = (new \ReflectionClass(PsrLogMessageProcessor::class))->getConstructor();
+ if (null === $reflectionConstructor || $reflectionConstructor->getNumberOfParameters() <= 0) {
+ $this->markTestSkipped('Monolog >= 1.26 is needed.');
+ }
+
+ $container = $this->getContainer('process_psr_3_messages_with_arguments');
+
+ $processors = [
+ 'monolog.processor.psr_log_message' => ['name' => 'without_arguments', 'arguments' => []],
+ 'monolog.processor.psr_log_message.'.ContainerBuilder::hash($arguments = ['Y', false]) => [
+ 'name' => 'with_arguments',
+ 'arguments' => $arguments,
+ ],
+ ];
+ foreach ($processors as $processorId => $settings) {
+ $this->assertTrue($container->hasDefinition($processorId));
+ $processor = $container->getDefinition($processorId);
+ $this->assertDICConstructorArguments($processor, $settings['arguments']);
+
+ $this->assertTrue($container->hasDefinition($handlerId = 'monolog.handler.'.$settings['name']));
+ $handler = $container->getDefinition($handlerId);
+ $this->assertDICDefinitionMethodCallAt(0, $handler, 'pushProcessor', [new Reference($processorId)]);
+ }
+ }
+
+ public function testPsrLogMessageProcessorDoesNotHaveConstructorArguments(): void
+ {
+ $reflectionConstructor = (new \ReflectionClass(PsrLogMessageProcessor::class))->getConstructor();
+ if (null !== $reflectionConstructor && $reflectionConstructor->getNumberOfParameters() > 0) {
+ $this->markTestSkipped('Monolog < 1.26 is needed.');
+ }
+
+ $container = $this->getContainer('process_psr_3_messages_without_arguments');
+
+ $this->assertTrue($container->hasDefinition($processorId = 'monolog.processor.psr_log_message'));
+ $processor = $container->getDefinition($processorId);
+ $this->assertDICConstructorArguments($processor, []);
+
+ $this->assertTrue($container->hasDefinition($handlerId = 'monolog.handler.without_arguments'));
+ $handler = $container->getDefinition($handlerId);
+ $this->assertDICDefinitionMethodCallAt(0, $handler, 'pushProcessor', [new Reference($processorId)]);
+
+ $this->expectException(\RuntimeException::class);
+ $this->expectExceptionMessage('Monolog 1.26 or higher is required for the "date_format" and "remove_used_context_fields" options to be used.');
+ $this->getContainer('process_psr_3_messages_with_arguments');
+ }
+
public function testNativeMailer()
{
$container = $this->getContainer('native_mailer');
diff --git a/Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_with_arguments.xml b/Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_with_arguments.xml
new file mode 100644
index 00000000..693f7fe8
--- /dev/null
+++ b/Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_with_arguments.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_without_arguments.xml b/Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_without_arguments.xml
new file mode 100644
index 00000000..288eb750
--- /dev/null
+++ b/Tests/DependencyInjection/Fixtures/xml/process_psr_3_messages_without_arguments.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_with_arguments.yml b/Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_with_arguments.yml
new file mode 100644
index 00000000..d98cb8a0
--- /dev/null
+++ b/Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_with_arguments.yml
@@ -0,0 +1,10 @@
+monolog:
+ handlers:
+ with_arguments:
+ type: stream
+ process_psr_3_messages:
+ date_format: 'Y'
+ without_arguments:
+ type: stream
+ process_psr_3_messages:
+ enabled: true
diff --git a/Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_without_arguments.yml b/Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_without_arguments.yml
new file mode 100644
index 00000000..b04e190c
--- /dev/null
+++ b/Tests/DependencyInjection/Fixtures/yml/process_psr_3_messages_without_arguments.yml
@@ -0,0 +1,6 @@
+monolog:
+ handlers:
+ without_arguments:
+ type: stream
+ process_psr_3_messages:
+ enabled: true