Skip to content

Commit 2d2c77d

Browse files
author
alexandresalome
committed
Possibility to define a custom mapping between channels and handlers
1 parent 289442e commit 2d2c77d

File tree

6 files changed

+301
-2
lines changed

6 files changed

+301
-2
lines changed

DependencyInjection/Compiler/LoggerChannelPass.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,27 @@ public function process(ContainerBuilder $container)
4545
}
4646
}
4747
}
48+
49+
$handlersToChannels = $container->getParameter('monolog.handlers_to_channels');
50+
51+
foreach ($handlersToChannels as $handler => $channels) {
52+
foreach ($this->processChannels($channels) as $channel) {
53+
$logger = $container->getDefinition('monolog.logger.'.$channel);
54+
$logger->addMethodCall('pushHandler', array(new Reference($handler)));
55+
}
56+
}
57+
}
58+
59+
protected function processChannels($configuration)
60+
{
61+
if (null === $configuration) {
62+
return $this->channels;
63+
}
64+
65+
if ('inclusive' === $configuration['type']) {
66+
return $configuration['elements'];
67+
}
68+
return array_diff($this->channels, $configuration['elements']);
4869
}
4970

5071
protected function createLogger($channel, $loggerId, ContainerBuilder $container)

DependencyInjection/Configuration.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1515
use Symfony\Component\Config\Definition\ConfigurationInterface;
16+
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1617

1718
/**
1819
* This class contains the configuration information for the bundle
@@ -84,6 +85,57 @@ public function getConfigTreeBuilder()
8485
->scalarNode('factory-method')->defaultNull()->end()
8586
->end()
8687
->end()
88+
->arrayNode('channels')
89+
->fixXmlConfig('channel', 'elements')
90+
->canBeUnset()
91+
->beforeNormalization()
92+
->always(function ($v) {
93+
94+
if (is_string($v)) {
95+
$v = array($v);
96+
}
97+
98+
if (null === $v || count($v) == 0) {
99+
return null;
100+
}
101+
102+
if (isset($v['type'])) {
103+
return $v;
104+
}
105+
106+
$isExclusive = null;
107+
$elements = array();
108+
foreach ($v as $element) {
109+
if (0 === strpos($element, '!')) {
110+
if (false === $isExclusive) {
111+
throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list.');
112+
}
113+
$elements[] = substr($element, 1);
114+
$isExclusive = true;
115+
} else {
116+
if (true === $isExclusive) {
117+
throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list');
118+
}
119+
$elements[] = $element;
120+
$isExclusive = false;
121+
}
122+
}
123+
124+
return array('type' => $isExclusive ? 'exclusive' : 'inclusive', 'elements' => $elements);
125+
})
126+
->end()
127+
->children()
128+
->scalarNode('type')
129+
->validate()
130+
->ifNotInArray(array('inclusive', 'exclusive'))
131+
->thenInvalid('The type of channels has to be inclusive or exclusive')
132+
->end()
133+
->end()
134+
->arrayNode('elements')
135+
->prototype('scalar')->end()
136+
->end()
137+
->end()
138+
->end()
87139
->scalarNode('formatter')->end()
88140
->end()
89141
->validate()

DependencyInjection/MonologExtension.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,13 @@ public function load(array $configs, ContainerBuilder $container)
4747
$logger = $container->getDefinition('monolog.logger_prototype');
4848

4949
$handlers = array();
50+
5051
foreach ($config['handlers'] as $name => $handler) {
51-
$handlers[] = array('id' => $this->buildHandler($container, $name, $handler), 'priority' => $handler['priority'] );
52+
$handlers[] = array(
53+
'id' => $this->buildHandler($container, $name, $handler),
54+
'priority' => $handler['priority'],
55+
'channels' => isset($handler['channels']) ? $handler['channels'] : null
56+
);
5257
}
5358

5459
$handlers = array_reverse($handlers);
@@ -59,11 +64,13 @@ public function load(array $configs, ContainerBuilder $container)
5964

6065
return $a['priority'] < $b['priority'] ? -1 : 1;
6166
});
67+
$handlersToChannels = array();
6268
foreach ($handlers as $handler) {
6369
if (!in_array($handler['id'], $this->nestedHandlers)) {
64-
$logger->addMethodCall('pushHandler', array(new Reference($handler['id'])));
70+
$handlersToChannels[$handler['id']] = $handler['channels'];
6571
}
6672
}
73+
$container->setParameter('monolog.handlers_to_channels', $handlersToChannels);
6774

6875
$this->addClassesToCompile(array(
6976
'Monolog\\Formatter\\FormatterInterface',

Resources/config/schema/monolog-1.0.xsd

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,11 @@
5959
<xsd:attribute name="id" type="xsd:string" />
6060
<xsd:attribute name="method" type="xsd:string" />
6161
</xsd:complexType>
62+
63+
<xsd:complexType name="channels">
64+
<xsd:sequence>
65+
<xsd:element name="type" type="xsd:string" minOccurs="1" maxOccurs="1" />
66+
<xsd:element name="channel" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
67+
</xsd:sequence>
68+
</xsd:complexType>
6269
</xsd:schema>

Tests/DependencyInjection/Compiler/LoggerChannelPassTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ public function testProcess()
2828

2929
$service = $container->getDefinition('test');
3030
$this->assertEquals('monolog.logger.test', (string) $service->getArgument(1), '->process replaces the logger by the new one');
31+
32+
33+
// pushHandlers for service "test"
34+
$expected = array(
35+
'test' => array('monolog.handler.a', 'monolog.handler.b', 'monolog.handler.c'),
36+
'foo' => array('monolog.handler.b'),
37+
'bar' => array('monolog.handler.b', 'monolog.handler.c'),
38+
);
39+
40+
foreach ($expected as $serviceName => $handlers) {
41+
$service = $container->getDefinition($serviceName);
42+
$channel = $container->getDefinition((string) $service->getArgument(1));
43+
44+
$calls = $channel->getMethodCalls();
45+
$this->assertCount(count($handlers), $calls);
46+
foreach ($handlers as $i => $handler) {
47+
list($methodName, $arguments) = $calls[$i];
48+
$this->assertEquals('pushHandler', $methodName);
49+
$this->assertCount(1, $arguments);
50+
$this->assertEquals($handler, (string) $arguments[0]);
51+
}
52+
}
3153
}
3254

3355
protected function getContainer()
@@ -43,6 +65,30 @@ protected function getContainer()
4365
$service->addTag('monolog.logger', array ('channel' => 'test'));
4466
$container->setDefinition('test', $service);
4567

68+
// Handlers
69+
$container->set('monolog.handler.a', new Definition('%monolog.handler.null.class%', array (100, false)));
70+
$container->set('monolog.handler.b', new Definition('%monolog.handler.null.class%', array (100, false)));
71+
$container->set('monolog.handler.c', new Definition('%monolog.handler.null.class%', array (100, false)));
72+
73+
// Channels
74+
foreach (array('test', 'foo', 'bar') as $name) {
75+
$service = new Definition('TestClass', array('false', new Reference('logger')));
76+
$service->addTag('monolog.logger', array ('channel' => $name));
77+
$container->setDefinition($name, $service);
78+
}
79+
80+
$container->setParameter('monolog.handlers_to_channels', array(
81+
'monolog.handler.a' => array(
82+
'type' => 'inclusive',
83+
'elements' => array('test')
84+
),
85+
'monolog.handler.b' => null,
86+
'monolog.handler.c' => array(
87+
'type' => 'exclusive',
88+
'elements' => array('foo')
89+
)
90+
));
91+
4692
$container->getCompilerPassConfig()->setOptimizationPasses(array());
4793
$container->getCompilerPassConfig()->setRemovingPasses(array());
4894
$container->addCompilerPass(new LoggerChannelPass());
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\MonologBundle\Tests\DependencyInjection;
13+
14+
use Symfony\Bundle\MonologBundle\DependencyInjection\Configuration;
15+
use Symfony\Component\Config\Definition\Processor;
16+
17+
/**
18+
* @author Alexandre Salomé <[email protected]>
19+
*/
20+
class ConfigurationTest extends \PHPUnit_Framework_TestCase
21+
{
22+
/**
23+
* Some basic tests to make sure the configuration is correctly processed in
24+
* the standard case.
25+
*/
26+
public function testProcessSimpleCase()
27+
{
28+
$configs = array(
29+
array(
30+
'handlers' => array('foobar' => array('type' => 'stream', 'path' => '/foo/bar'))
31+
)
32+
);
33+
34+
$config = $this->process($configs);
35+
36+
$this->assertArrayHasKey('handlers', $config);
37+
$this->assertArrayHasKey('foobar', $config['handlers']);
38+
$this->assertEquals('stream', $config['handlers']['foobar']['type']);
39+
$this->assertEquals('/foo/bar', $config['handlers']['foobar']['path']);
40+
}
41+
42+
public function provideProcessStringChannels()
43+
{
44+
return array(
45+
array('foo', 'foo', true),
46+
array('!foo', 'foo', false)
47+
);
48+
}
49+
50+
/**
51+
* @dataProvider provideProcessStringChannels
52+
*/
53+
public function testProcessStringChannels($string, $expectedString, $isInclusive)
54+
{
55+
$configs = array(
56+
array(
57+
'handlers' => array(
58+
'foobar' => array(
59+
'type' => 'stream',
60+
'path' => '/foo/bar',
61+
'channels' => $string
62+
)
63+
)
64+
)
65+
);
66+
67+
$config = $this->process($configs);
68+
69+
$this->assertEquals($isInclusive ? 'inclusive' : 'exclusive', $config['handlers']['foobar']['channels']['type']);
70+
$this->assertCount(1, $config['handlers']['foobar']['channels']['elements']);
71+
$this->assertEquals($expectedString, $config['handlers']['foobar']['channels']['elements'][0]);
72+
}
73+
74+
public function testArrays()
75+
{
76+
$configs = array(
77+
array(
78+
'handlers' => array(
79+
'foo' => array(
80+
'type' => 'stream',
81+
'path' => '/foo',
82+
'channels' => array('A', 'B')
83+
),
84+
'bar' => array(
85+
'type' => 'stream',
86+
'path' => '/foo',
87+
'channels' => array('!C', '!D')
88+
),
89+
)
90+
)
91+
);
92+
93+
$config = $this->process($configs);
94+
95+
// Check foo
96+
$this->assertCount(2, $config['handlers']['foo']['channels']['elements']);
97+
$this->assertEquals('inclusive', $config['handlers']['foo']['channels']['type']);
98+
$this->assertEquals('A', $config['handlers']['foo']['channels']['elements'][0]);
99+
$this->assertEquals('B', $config['handlers']['foo']['channels']['elements'][1]);
100+
// Check bar
101+
$this->assertCount(2, $config['handlers']['bar']['channels']['elements']);
102+
$this->assertEquals('exclusive', $config['handlers']['bar']['channels']['type']);
103+
$this->assertEquals('C', $config['handlers']['bar']['channels']['elements'][0]);
104+
$this->assertEquals('D', $config['handlers']['bar']['channels']['elements'][1]);
105+
}
106+
107+
/**
108+
* @expectedException Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
109+
*/
110+
public function testInvalidArrays()
111+
{
112+
$configs = array(
113+
array(
114+
'handlers' => array(
115+
'foo' => array(
116+
'type' => 'stream',
117+
'path' => '/foo',
118+
'channels' => array('A', '!B')
119+
)
120+
)
121+
)
122+
);
123+
124+
$config = $this->process($configs);
125+
}
126+
127+
public function testWithType()
128+
{
129+
$configs = array(
130+
array(
131+
'handlers' => array(
132+
'foo' => array(
133+
'type' => 'stream',
134+
'path' => '/foo',
135+
'channels' => array(
136+
'type' => 'inclusive',
137+
'elements' => array('A', 'B')
138+
)
139+
)
140+
)
141+
)
142+
);
143+
144+
$config = $this->process($configs);
145+
146+
// Check foo
147+
$this->assertCount(2, $config['handlers']['foo']['channels']['elements']);
148+
$this->assertEquals('inclusive', $config['handlers']['foo']['channels']['type']);
149+
$this->assertEquals('A', $config['handlers']['foo']['channels']['elements'][0]);
150+
$this->assertEquals('B', $config['handlers']['foo']['channels']['elements'][1]);
151+
}
152+
153+
/**
154+
* Processes an array of configurations and returns a compiled version.
155+
*
156+
* @param array $configs An array of raw configurations
157+
*
158+
* @return array A normalized array
159+
*/
160+
protected function process($configs)
161+
{
162+
$configuration = new Configuration();
163+
$processor = new Processor();
164+
return $processor->process($configuration->getConfigTreeBuilder()->buildTree(), $configs);
165+
}
166+
}

0 commit comments

Comments
 (0)