Skip to content

Commit c6b64c3

Browse files
committed
feature symfony#485 Add priority field to processor tags (HypeMC)
This PR was merged into the 3.x branch. Discussion ---------- Add priority field to processor tags | Q | A | ------------- | --- | Branch? | 3.x | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Continuation of symfony#455. ```yaml Foo\Bar\Log\Processors\FirstProcessor: tags: - { name: monolog.processor, channel: http } Foo\Bar\Log\Processors\SecondProcessor: tags: - { name: monolog.processor, channel: http, priority: -10 } # last Foo\Bar\Log\Processors\ThirdProcessor: tags: - { name: monolog.processor, channel: http, priority: -20 } # very last ``` ```php #[AsMonologProcessor(priority: -10)] class SecondProcessor {} #[AsMonologProcessor(priority: -20)] class ThirdProcessor {} ``` **Probably better to review with [whitespace changes ignored](https://github.com/symfony/monolog-bundle/pull/485/files?w=1).** Commits ------- 4165954 Add priority field to processor tags
2 parents a716f93 + 4165954 commit c6b64c3

File tree

5 files changed

+126
-35
lines changed

5 files changed

+126
-35
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
@@ -38,6 +38,7 @@
3838
->parent('monolog.logger_prototype')
3939
->args(['index_0' => 'app'])
4040
->call('useMicrosecondTimestamps', [param('monolog.use_microseconds')])
41+
->tag('monolog.channel_logger')
4142

4243
->set('monolog.logger_prototype', Logger::class)
4344
->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)
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
@@ -138,6 +138,7 @@ protected function createLogger(string $channel, string $loggerId, ContainerBuil
138138
if (!\in_array($channel, $this->channels)) {
139139
$logger = new ChildDefinition('monolog.logger_prototype');
140140
$logger->replaceArgument(0, $channel);
141+
$logger->addTag('monolog.channel_logger');
141142
$container->setDefinition($loggerId, $logger);
142143
$this->channels[] = $channel;
143144
}

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()
@@ -97,7 +126,7 @@ public static function provideEmptyTagsData(): iterable
97126
{
98127
yield 'with empty tag' => [
99128
[[]],
100-
[['pushProcessor', [new Reference('TestClass')]], ['useMicrosecondTimestamps', ['%monolog.use_microseconds%']]],
129+
[['useMicrosecondTimestamps', ['%monolog.use_microseconds%']], ['pushProcessor', [new Reference('TestClass')]]],
101130
[['pushProcessor', [new Reference('TestClass')]]],
102131
];
103132

@@ -115,7 +144,7 @@ public static function provideEmptyTagsData(): iterable
115144

116145
yield 'with method and no channel' => [
117146
[[], ['method' => 'foo']],
118-
[['pushProcessor', [[new Reference('TestClass'), 'foo']]], ['useMicrosecondTimestamps', ['%monolog.use_microseconds%']]],
147+
[['useMicrosecondTimestamps', ['%monolog.use_microseconds%']], ['pushProcessor', [[new Reference('TestClass'), 'foo']]]],
119148
[['pushProcessor', [[new Reference('TestClass'), 'foo']]]],
120149
];
121150
}
@@ -126,24 +155,62 @@ protected function getContainer()
126155
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../../config'));
127156
$loader->load('monolog.php');
128157

158+
$container->setParameter('monolog.additional_channels', ['test']);
159+
$container->setParameter('monolog.handlers_to_channels', []);
160+
129161
$definition = $container->getDefinition('monolog.logger_prototype');
130162
$container->setParameter('monolog.handler.console.class', ConsoleHandler::class);
131163
$container->setDefinition('monolog.handler.test', new Definition('%monolog.handler.console.class%', [100, false]));
132164
$container->setDefinition('handler_test', new Definition('%monolog.handler.console.class%', [100, false]));
165+
$container->setDefinition('monolog.handler.priority_test', new Definition('%monolog.handler.console.class%', [100, false]));
166+
$container->setDefinition('monolog.handler.priority_test_2', new Definition('%monolog.handler.console.class%', [100, false]));
133167
$container->setAlias('monolog.handler.test2', 'handler_test');
134168
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.test')]);
135169
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.test2')]);
170+
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.priority_test')]);
171+
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.priority_test_2')]);
136172

137-
$service = new Definition('TestClass', ['false', new Reference('logger')]);
173+
$service = new Definition('TestClass');
138174
$service->addTag('monolog.processor', ['handler' => 'test']);
139175
$container->setDefinition('test', $service);
140176

141-
$service = new Definition('TestClass', ['false', new Reference('logger')]);
177+
$service = new Definition('TestClass');
142178
$service->addTag('monolog.processor', ['handler' => 'test2']);
143179
$container->setDefinition('test2', $service);
144180

181+
$service = new Definition('TestClass');
182+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 10]);
183+
$container->setDefinition('processor+10', $service);
184+
185+
$service = new Definition('TestClass');
186+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => -10]);
187+
$container->setDefinition('processor-10', $service);
188+
189+
$service = new Definition('TestClass');
190+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 20]);
191+
$container->setDefinition('processor+20', $service);
192+
193+
$service = new Definition('TestClass');
194+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 35]);
195+
$service->addTag('monolog.processor', ['handler' => 'priority_test_2', 'priority' => 25]);
196+
$container->setDefinition('processor+35+25', $service);
197+
198+
$service = new Definition('TestClass');
199+
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 25]);
200+
$service->addTag('monolog.processor', ['handler' => 'priority_test_2', 'priority' => 35]);
201+
$container->setDefinition('processor+25+35', $service);
202+
203+
$service = new Definition('TestClass');
204+
$service->addTag('monolog.processor', ['priority' => 0]);
205+
$container->setDefinition('processor_all_channels+0', $service);
206+
207+
$service = new Definition('TestClass');
208+
$service->addTag('monolog.processor', ['channel' => 'test', 'priority' => -25]);
209+
$container->setDefinition('processor_test_channel-25', $service);
210+
145211
$container->getCompilerPassConfig()->setOptimizationPasses([]);
146212
$container->getCompilerPassConfig()->setRemovingPasses([]);
213+
$container->addCompilerPass(new LoggerChannelPass());
147214
$container->addCompilerPass(new AddProcessorsPass());
148215
$container->compile();
149216

0 commit comments

Comments
 (0)