Issue 1:
Hello.

Problem

I had to implement something that seems simple:

Use a (monolog) stream handler only for our production servers.

This logger should not be present on developer machine (to avoid warning & co)

So I can not add a section in the config.yml because it depends on something

else.

So I add a new parameter in our parameters.yml.(I use to do that for many

things that are enabled only on production server.)

But here, it's very hard to achieve that.

Some solutions

Note: Solution 2 + 4 + 5 are already rejected

1. Add an enable option on each monolog handler:

# config.yml
monolog:
handlers:
only_on_production_server:
type: stream
path: "/var/log/foobar/%kernel.environment%.log"
level: error
enable: %we_are_on_production_server%

2. Add a new monolog.handler tag

class BundleExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
if ($container->hasParameter('we_are_on_production_server')) {
$def = new Definition('MyHandler')
// Listen all channels
$def->addTag('we_are_on_production_server');
// Listen only XX channel
$def->addTag('we_are_on_production_server', [channels => 'XX']);
$container->setDefinition('project.monolog.handler.prod', $def);
}
}
}

3. Give bundle configuration to PrependExtensionInterface::preprend

Note: I already need that, but for another issue which is not related to

monolog. I really miss this feature in symfony...

class BundleExtension extends Extension
{
// BC layer..
public function preprendWithConfig(array $configs, ContainerBuilder $container)
{
// here, configs is not validated, and not merged.
// Let's pretend it is ;)
if ($config['we_are_on_production_server']) {
$container->prependExtensionConfig('monolog', array(
'handlers' => array(
'only_on_production_server' => array(
'type' => 'stream',
'path' => '/var/log/foobar/%kernel.environment%.log',
'level' => 'error',
),
),
));
}
}

Temp solution:

4. load a .php config script

# config.yml
imports:
- { resource: config.yml }
- { resource: config_prod_logger.php }

if (!$container->hasParameter('we_are_on_production_server') || !$container->getParameter('we_are_on_production_server')) {
return;
}

$container->prependExtensionConfig('monolog', array(
'handlers' => array(
'only_on_production_server' => array(
'type' => 'stream',
'path' => '/var/log/foobar/%kernel.environment%.log',
'level' => 'error',
),
),
));

5. Compiler pass

Right now, I wax able to do that with a new compiler pass in my application:

<?php

namespace SensioLabs\Bundle\XXXXBundle\DependencyInjection\Compiler;

use Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\LoggerChannelPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

class LoggerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('monolog.logger')) {
return;
}

$logHekaUrl = $container->getParameter('log_heka_url');

if (!$logHekaUrl) {
return;
}

// I really don't like this part
$channelPass = null;
foreach ($container->getCompilerPassConfig()->getPasses() as $pass) {
if ($pass instanceof LoggerChannelPass) {
$channelPass = $pass;
}
}

if (!$channelPass) {
return;
}

$handlerId = $this->createSocketHandler($container, $logHekaUrl);

foreach ($channelPass->getChannels() as $channel) {
$container
->getDefinition($channel === 'app' ? 'monolog.logger' : 'monolog.logger.'.$channel)
->addMethodCall('pushHandler', array(new Reference($handlerId)));
}
}

private function createSocketHandler(ContainerBuilder $container, $logHekaUrl)
{
$bufferDefinition = new Definition('%monolog.handler.buffer.class%');
$bufferDefinition->setPublic(false);
$bufferDefinition->setArguments(array(
new Reference('monolog.handler.heka'),
));
$container->setDefinition('monolog.handler.buffer_heka', $bufferDefinition);

$hekaDefinition = new Definition('SensioLabs\Toolkit\Monolog\Handler\SocketHandler');
$hekaDefinition->setPublic(false);
$hekaDefinition->setArguments(array(
$logHekaUrl,
));
$hekaDefinition->addMethodCall('setTimeout', array(1));
$hekaDefinition->addMethodCall('setConnectionTimeout', array(1));
$container->setDefinition('monolog.handler.heka', $hekaDefinition);

return 'monolog.handler.buffer_heka';
}
}

So here, I have to duplicate some code that is already in symfony...

so ?

Did I miss something? It's weird to have to do that.

Solution 1: Quick fix that is simple to implement. But we have to put all

config in the config_prod.yml, even if it's not used.

Solution 2: It seems more powerful than solution 1. But it's a bit harder

to implement (but not so hard).

Solution 3: It's my favorite, because it allow much more things. The last

time We had to configure "framework/assets/bash_url" for production server, This

feature really miss me. We had to do some voodoo to configure properly symfony.

Solution 4: It's does not pay well with bundle. I mean, it works for a

project, but I can not ship this code in shared (vendored) bundle.

Solution 5: It works, but some logic is duplicated from MonologBundle. More

over it's far from perfect, because I miss all logic inside the MonologExtenxion

(channel, processor, nestedHandlers, ...)

WDYT?

ping @Seldaek @tucksaun

Issue 2:
Symfony 3.2

I have a system of widgets submitted via Ajax. Sometimes I can have the same form rendered twice on the same page by the same embedded controller.

When this happens, the profiler doesn't start correctly, throws a js alert with 404 and "Exception in FormDataCollector.php line 274: Serialization of 'Closure' is not allowed" is shown right at the end of the page.

For right now fixed it with

$this->get('form.factory')->createNamed('form'.random_int(1, 100), ...);