Configuration dynamique avec Symfony ExpressionLanguage

Photo Credit: Christophe Paquignon

Configuration dynamique avec Symfony ExpressionLanguage

Grâce à notre bundle MonologExtra, nous avons la possibilité d’inclure des informations statiques dans le contexte de nos logs. Nous souhaiterions maintenant avoir aussi d’autres informations plus dynamiques comme le nom de l’utilisateur.

Pour cela, nous avons donc ajouté la possibilité de configurer une expression qui sera évaluée par le composant ExpressionLanguage de Symfony de cette manière :

m6_web_monolog_extra:
    processors:
        userProcessor:
            type: ContextInformation
            config:
                env: expr(container.getParameter('kernel.environment'))
                user: expr(container.get('security.context').getToken() ? container.get('security.context').getToken().getUser().getUsername() : 'anonymous')

Pour interpréter cette expression, nous avons injecté dans notre processeur Monolog une instance de ExpressionLanguage ainsi que le container :

services:
  m6_web_monolog_extra.expression_language:
    class: Symfony\Component\ExpressionLanguage\ExpressionLanguage
    public: false
  m6_web_monolog_extra.processor.contextInformation:
    abstract: true
    class: M6Web\Bundle\MonologExtraBundle\Processor\ContextInformationProcessor
    arguments:
      - @service_container
      - @m6_web_monolog_extra.expression_language
    calls:
      - [ setConfiguration, []]

Nous utilisons une définition de service abstraite qui sert de modèle pour les services qui sont générés à partir de la configuration sémantique gérée par l’extension du bundle :

<?php
foreach ($config['processors'] as $name => $processor) {
    $serviceId = sprintf('%s.processor.%s', $alias, is_int($name) ? uniqid() : $name);

    $definition = clone $container->getDefinition(sprintf('%s.processor.%s', $alias, $processor['type']));
    $definition->setAbstract(false);

    $tagOptions = [];
    if (array_key_exists('channel', $processor)) {
        $tagOptions['channel'] = $processor['channel'];
    }
    if (array_key_exists('handler', $processor)) {
        $tagOptions['handler'] = $processor['handler'];
    }
    $definition->addtag('monolog.processor', $tagOptions);

    if (array_key_exists('config', $processor)) {
        if ($definition->hasMethodCall('setConfiguration')) {
            $definition->removeMethodCall('setConfiguration');
            $definition->addMethodCall('setConfiguration', [$processor['config']]);
        } else {
            throw new InvalidConfigurationException(sprintf('"%s" processor is not configurable.', $processor['type']));
        }
    }

    $container->setDefinition($serviceId, $definition);
}

Et l’expression est finalement évaluée par le processeur en utilisant le composant quand la valeur est de la forme expr(...), ceci permettant de garder une compatibilité ascendante avec les configurations statiques précédentes.

<?php 
protected function evaluateValue($value)
{
    if (preg_match('/^expr\((.*)\)$/', $value, $matches)) {
        return $this->expressionLanguage->evaluate($matches[1], ['container' => $this->container]);
    }
    return $value;
}

Avec la configuration présentée au début, nous récupérons ainsi l’environnement et l’utilisateur connecté dans le contexte de nos logs.

MonologExtraBundle est disponible en open-source sur le compte GitHub de M6Web.


En passant, si vous avez trouvé une faute de frappe, vous pouvez forker et éditer ce post. Merci beaucoup !