Skip to content

Commit d3cf193

Browse files
committed
Merge branch '3.x' into 4.x
* 3.x: Remove :void return types from tests Add priority field to processor tags Conflicts: - src/DependencyInjection/Compiler/AddProcessorsPass.php - tests/DependencyInjection/ConfigurationTest.php - tests/DependencyInjection/MonologExtensionTest.php
2 parents 43da9a3 + 4fb8f83 commit d3cf193

File tree

6 files changed

+128
-37
lines changed

6 files changed

+128
-37
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* Fix `host` configuration for `elastic_search` handler
1717
* Add `hosts` configuration for `elastica` handler
1818
* Add `enabled` option to `handlers` configuration
19+
* Add `priority` field to `processor` tag
1920

2021
## 3.10.0 (2023-11-06)
2122

config/monolog.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
->parent('monolog.logger_prototype')
3737
->args(['index_0' => 'app'])
3838
->call('useMicrosecondTimestamps', [param('monolog.use_microseconds')])
39+
->tag('monolog.channel_logger')
3940

4041
->set('monolog.logger_prototype', Logger::class)
4142
->args([abstract_arg('channel')])

src/DependencyInjection/Compiler/AddProcessorsPass.php

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
1415
use Symfony\Component\DependencyInjection\ChildDefinition;
1516
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
1618
use Symfony\Component\DependencyInjection\ContainerBuilder;
17-
use Symfony\Component\DependencyInjection\Reference;
1819

1920
/**
2021
* Registers processors in Monolog loggers or handlers.
@@ -25,48 +26,68 @@
2526
*/
2627
class AddProcessorsPass implements CompilerPassInterface
2728
{
29+
use PriorityTaggedServiceTrait;
30+
2831
public function process(ContainerBuilder $container): void
2932
{
3033
if (!$container->hasDefinition('monolog.logger')) {
3134
return;
3235
}
3336

37+
$indexedTags = [];
38+
$i = 1;
39+
3440
foreach ($container->findTaggedServiceIds('monolog.processor') as $id => $tags) {
3541
if (array_any($tags, $closure = function (array $tag) { return (bool) $tag; })) {
3642
$tags = array_filter($tags, $closure);
3743
}
3844

39-
foreach ($tags as $tag) {
40-
if (!empty($tag['channel']) && !empty($tag['handler'])) {
41-
throw new \InvalidArgumentException(\sprintf('you cannot specify both the "handler" and "channel" attributes for the "monolog.processor" tag on service "%s".', $id));
42-
}
45+
foreach ($tags as &$tag) {
46+
$indexedTags[$tag['index'] = $i++] = $tag;
47+
}
48+
unset($tag);
49+
$definition = $container->getDefinition($id);
50+
$definition->setTags(array_merge($definition->getTags(), ['monolog.processor' => $tags]));
51+
}
4352

44-
if (!empty($tag['handler'])) {
45-
$definition = $container->findDefinition(\sprintf('monolog.handler.%s', $tag['handler']));
46-
$parentDef = $definition;
47-
while (!$parentDef->getClass() && $parentDef instanceof ChildDefinition) {
48-
$parentDef = $container->findDefinition($parentDef->getParent());
49-
}
50-
$class = $container->getParameterBag()->resolveValue($parentDef->getClass());
51-
if (!method_exists($class, 'pushProcessor')) {
52-
throw new \InvalidArgumentException(\sprintf('The "%s" handler does not accept processors.', $tag['handler']));
53-
}
54-
} elseif (!empty($tag['channel'])) {
55-
if ('app' === $tag['channel']) {
56-
$definition = $container->getDefinition('monolog.logger');
57-
} else {
58-
$definition = $container->getDefinition(\sprintf('monolog.logger.%s', $tag['channel']));
59-
}
60-
} else {
61-
$definition = $container->getDefinition('monolog.logger_prototype');
62-
}
53+
$taggedIteratorArgument = new TaggedIteratorArgument('monolog.processor', 'index', null, true);
54+
// array_reverse is used because ProcessableHandlerTrait::pushProcessor prepends processors to the beginning of the stack
55+
foreach (array_reverse($this->findAndSortTaggedServices($taggedIteratorArgument, $container), true) as $index => $reference) {
56+
$tag = $indexedTags[$index];
6357

64-
if (!empty($tag['method'])) {
65-
$processor = [new Reference($id), $tag['method']];
66-
} else {
67-
// If no method is defined, fallback to use __invoke
68-
$processor = new Reference($id);
58+
if (!empty($tag['channel']) && !empty($tag['handler'])) {
59+
throw new \InvalidArgumentException(\sprintf('You cannot specify both the "handler" and "channel" attributes for the "monolog.processor" tag on service "%s".', $reference));
60+
}
61+
62+
if (!empty($tag['handler'])) {
63+
$parentDef = $container->findDefinition(\sprintf('monolog.handler.%s', $tag['handler']));
64+
$definitions = [$parentDef];
65+
while (!$parentDef->getClass() && $parentDef instanceof ChildDefinition) {
66+
$parentDef = $container->findDefinition($parentDef->getParent());
67+
}
68+
$class = $container->getParameterBag()->resolveValue($parentDef->getClass());
69+
if (!method_exists($class, 'pushProcessor')) {
70+
throw new \InvalidArgumentException(\sprintf('The "%s" handler does not accept processors.', $tag['handler']));
71+
}
72+
} elseif (!empty($tag['channel'])) {
73+
$loggerId = 'app' === $tag['channel'] ? 'monolog.logger' : \sprintf('monolog.logger.%s', $tag['channel']);
74+
$definitions = [$container->getDefinition($loggerId)];
75+
} elseif ($loggerIds = $container->findTaggedServiceIds('monolog.channel_logger')) {
76+
$definitions = [];
77+
foreach ($loggerIds as $loggerId => $tags) {
78+
$definitions[] = $container->getDefinition($loggerId);
6979
}
80+
} else {
81+
$definitions = [$container->getDefinition('monolog.logger_prototype')];
82+
}
83+
84+
if (!empty($tag['method'])) {
85+
$processor = [$reference, $tag['method']];
86+
} else {
87+
// If no method is defined, fallback to use __invoke
88+
$processor = $reference;
89+
}
90+
foreach ($definitions as $definition) {
7091
$definition->addMethodCall('pushProcessor', [$processor]);
7192
}
7293
}

src/DependencyInjection/Compiler/LoggerChannelPass.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ protected function createLogger(string $channel, string $loggerId, ContainerBuil
126126
if (!\in_array($channel, $createdLoggers, true)) {
127127
$logger = new ChildDefinition('monolog.logger_prototype');
128128
$logger->replaceArgument(0, $channel);
129+
$logger->addTag('monolog.channel_logger');
129130
$container->setDefinition($loggerId, $logger);
130131
$createdLoggers[] = $channel;
131132
}

tests/DependencyInjection/Compiler/AddProcessorsPassTest.php

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Component\DependencyInjection\Definition;
2525
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
2626
use Symfony\Component\DependencyInjection\Reference;
27+
use Symfony\Component\DependencyInjection\TypedReference;
2728

2829
class AddProcessorsPassTest extends TestCase
2930
{
@@ -34,12 +35,40 @@ public function testHandlerProcessors()
3435
$service = $container->getDefinition('monolog.handler.test');
3536
$calls = $service->getMethodCalls();
3637
$this->assertCount(1, $calls);
37-
$this->assertEquals(['pushProcessor', [new Reference('test')]], $calls[0]);
38+
$this->assertEquals(['pushProcessor', [new TypedReference('test', 'TestClass')]], $calls[0]);
3839

3940
$service = $container->getDefinition('handler_test');
4041
$calls = $service->getMethodCalls();
4142
$this->assertCount(1, $calls);
42-
$this->assertEquals(['pushProcessor', [new Reference('test2')]], $calls[0]);
43+
$this->assertEquals(['pushProcessor', [new TypedReference('test2', 'TestClass')]], $calls[0]);
44+
45+
$service = $container->getDefinition('monolog.handler.priority_test');
46+
$calls = $service->getMethodCalls();
47+
$this->assertCount(5, $calls);
48+
$this->assertEquals(['pushProcessor', [new TypedReference('processor-10', 'TestClass')]], $calls[0]);
49+
$this->assertEquals(['pushProcessor', [new TypedReference('processor+10', 'TestClass')]], $calls[1]);
50+
$this->assertEquals(['pushProcessor', [new TypedReference('processor+20', 'TestClass')]], $calls[2]);
51+
$this->assertEquals(['pushProcessor', [new TypedReference('processor+20', 'TestClass')]], $calls[2]);
52+
$this->assertEquals(['pushProcessor', [new TypedReference('processor+25+35', 'TestClass')]], $calls[3]);
53+
$this->assertEquals(['pushProcessor', [new TypedReference('processor+35+25', 'TestClass')]], $calls[4]);
54+
55+
$service = $container->getDefinition('monolog.handler.priority_test_2');
56+
$calls = $service->getMethodCalls();
57+
$this->assertCount(2, $calls);
58+
$this->assertEquals(['pushProcessor', [new TypedReference('processor+35+25', 'TestClass')]], $calls[0]);
59+
$this->assertEquals(['pushProcessor', [new TypedReference('processor+25+35', 'TestClass')]], $calls[1]);
60+
61+
$service = $container->getDefinition('monolog.logger');
62+
$calls = $service->getMethodCalls();
63+
$this->assertCount(2, $calls);
64+
$this->assertEquals(['useMicrosecondTimestamps', ['%monolog.use_microseconds%']], $calls[0]);
65+
$this->assertEquals(['pushProcessor', [new TypedReference('processor_all_channels+0', 'TestClass')]], $calls[1]);
66+
67+
$service = $container->getDefinition('monolog.logger.test');
68+
$calls = $service->getMethodCalls();
69+
$this->assertCount(2, $calls);
70+
$this->assertEquals(['pushProcessor', [new TypedReference('processor_test_channel-25', 'TestClass')]], $calls[0]);
71+
$this->assertEquals(['pushProcessor', [new TypedReference('processor_all_channels+0', 'TestClass')]], $calls[1]);
4372
}
4473

4574
public function testFailureOnHandlerWithoutPushProcessor()
@@ -88,7 +117,7 @@ public static function provideEmptyTagsData(): iterable
88117
{
89118
yield 'with empty tag' => [
90119
[[]],
91-
[['pushProcessor', [new Reference('TestClass')]], ['useMicrosecondTimestamps', ['%monolog.use_microseconds%']]],
120+
[['useMicrosecondTimestamps', ['%monolog.use_microseconds%']], ['pushProcessor', [new Reference('TestClass')]]],
92121
[['pushProcessor', [new Reference('TestClass')]]],
93122
];
94123

@@ -106,7 +135,7 @@ public static function provideEmptyTagsData(): iterable
106135

107136
yield 'with method and no channel' => [
108137
[[], ['method' => 'foo']],
109-
[['pushProcessor', [[new Reference('TestClass'), 'foo']]], ['useMicrosecondTimestamps', ['%monolog.use_microseconds%']]],
138+
[['useMicrosecondTimestamps', ['%monolog.use_microseconds%']], ['pushProcessor', [[new Reference('TestClass'), 'foo']]]],
110139
[['pushProcessor', [[new Reference('TestClass'), 'foo']]]],
111140
];
112141
}
@@ -117,24 +146,62 @@ protected function getContainer(): ContainerBuilder
117146
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../../config'));
118147
$loader->load('monolog.php');
119148

149+
$container->setParameter('monolog.additional_channels', ['test']);
150+
$container->setParameter('monolog.handlers_to_channels', []);
151+
120152
$definition = $container->getDefinition('monolog.logger_prototype');
121153
$container->setParameter('monolog.handler.console.class', ConsoleHandler::class);
122154
$container->setDefinition('monolog.handler.test', new Definition('%monolog.handler.console.class%', [100, false]));
123155
$container->setDefinition('handler_test', new Definition('%monolog.handler.console.class%', [100, false]));
156+
$container->setDefinition('monolog.handler.priority_test', new Definition('%monolog.handler.console.class%', [100, false]));
157+
$container->setDefinition('monolog.handler.priority_test_2', new Definition('%monolog.handler.console.class%', [100, false]));
124158
$container->setAlias('monolog.handler.test2', 'handler_test');
125159
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.test')]);
126160
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.test2')]);
161+
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.priority_test')]);
162+
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.priority_test_2')]);
127163

128-
$service = new Definition('TestClass', ['false', new Reference('logger')]);
164+
$service = new Definition('TestClass');
129165
$service->addTag('monolog.processor', ['handler' => 'test']);
130166
$container->setDefinition('test', $service);
131167

132-
$service = new Definition('TestClass', ['false', new Reference('logger')]);
168+
$service = new Definition('TestClass');
133169
$service->addTag('monolog.processor', ['handler' => 'test2']);
134170
$container->setDefinition('test2', $service);
135171

172+
$service = new Definition('TestClass');
173+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 10]);
174+
$container->setDefinition('processor+10', $service);
175+
176+
$service = new Definition('TestClass');
177+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => -10]);
178+
$container->setDefinition('processor-10', $service);
179+
180+
$service = new Definition('TestClass');
181+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 20]);
182+
$container->setDefinition('processor+20', $service);
183+
184+
$service = new Definition('TestClass');
185+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 35]);
186+
$service->addTag('monolog.processor', ['handler' => 'priority_test_2', 'priority' => 25]);
187+
$container->setDefinition('processor+35+25', $service);
188+
189+
$service = new Definition('TestClass');
190+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 25]);
191+
$service->addTag('monolog.processor', ['handler' => 'priority_test_2', 'priority' => 35]);
192+
$container->setDefinition('processor+25+35', $service);
193+
194+
$service = new Definition('TestClass');
195+
$service->addTag('monolog.processor', ['priority' => 0]);
196+
$container->setDefinition('processor_all_channels+0', $service);
197+
198+
$service = new Definition('TestClass');
199+
$service->addTag('monolog.processor', ['channel' => 'test', 'priority' => -25]);
200+
$container->setDefinition('processor_test_channel-25', $service);
201+
136202
$container->getCompilerPassConfig()->setOptimizationPasses([]);
137203
$container->getCompilerPassConfig()->setRemovingPasses([]);
204+
$container->addCompilerPass(new LoggerChannelPass());
138205
$container->addCompilerPass(new AddProcessorsPass());
139206
$container->compile();
140207

tests/DependencyInjection/MonologExtensionTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -541,15 +541,15 @@ public function testElasticsearchAndElasticaHandlers()
541541

542542
// Elasticsearch handler should receive the elasticsearch.client as first argument
543543
$esHandler = $container->getDefinition('monolog.handler.es_handler');
544-
$this->assertSame(ElasticsearchHandler::class,$esHandler->getClass());
544+
$this->assertSame(ElasticsearchHandler::class, $esHandler->getClass());
545545
$esClient = $esHandler->getArgument(0);
546546
$this->assertInstanceOf(Definition::class, $esClient);
547547
$this->assertStringEndsWith('Elasticsearch\Client', $esClient->getClass());
548548
$this->assertSame(['hosts' => ['es:9200']], $esClient->getArgument(0));
549549

550550
// Elastica handler should receive the elastica.client as first argument
551551
$elasticaHandler = $container->getDefinition('monolog.handler.elastica_handler');
552-
$this->assertSame(ElasticaHandler::class,$elasticaHandler->getClass());
552+
$this->assertSame(ElasticaHandler::class, $elasticaHandler->getClass());
553553
$elasticaClient = $elasticaHandler->getArgument(0);
554554
$this->assertInstanceOf(Definition::class, $elasticaClient);
555555
$this->assertSame('Elastica\Client', $elasticaClient->getClass());

0 commit comments

Comments
 (0)