Question

I'm trying to create a custom monolog processor to attach the current user to an error mailer.

When declaring a service like so:

monolog.processor.mail:
        class: MyVendor\Monolog\Processor\MailProcessor
        arguments:
            - @mailer
            - @security.context
        tags:
            - { name: monolog.processor, method: processRecord }

I get a circular reference:

[Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException]
Circular reference detected for service "monolog.processor.mail",
path: "router -> monolog.logger.router -> monolog.processor.mail
-> security.context -> security.authentication.manager
-> fos_user.user_provider.username_email-> fos_user.user_manager
-> doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection
-> doctrine.dbal.logger -> monolog.logger.doctrine".

What would be the best practice solution here?

A related forum thread: http://forum.symfony-project.org/viewtopic.php?t=40306&p=131081#p131143

This thread shows that:

  • Setter injection doesn't solve the issue (i tried this as well)
  • Injecting the container causes an infinitive recursion (this i have not confirmed)

Also tried this script http://pastebin.com/AuvFgTY3 to get the user from the session.

if ($this->session !== null) {
    if ($this->session->has($this->securityKey)) {
        $token = unserialize($this->session->get($this->securityKey));
        $this->currentUser = $token->getUser();
    }
}

This gave the following error:

Warning: ini_set(): A session is active. You cannot change the session module's ini settings at this time in
C:\inetpub\symfony23\vendor\symfony\symfony\src\Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler.php
on line 56

I do understand that the security.context has not yet been build for services which request the logger very early on. For my class it's not a problem since i will set the user to undefined. So ideally the security.context would be setter injected AFTER the security.context service has been created. However i can not change the priority on the logger to be constructed very late because it's needed early on. So maybe the question resolves to: how to recreate the service again after security.context has been initialized? Not sure if scope prototype would help here??

Was it helpful?

Solution

Create handler on kernel request and extract user:

// src/AppBundle/Monolog/UserProcessor.php

namespace AppBundle\Monolog;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class UserProcessor
{
    private $tokenStorage;
    private $user;

    public function __construct(TokenStorageInterface $tokenStorage)
    {
        $this->tokenStorage = $tokenStorage;
    }

    public function __invoke(array $record)
    {
        if (null !== $this->user) {
            $record['extra']['user'] = $this->user->getUsername();
        }

        return $record;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        if (null === $token = $this->tokenStorage->getToken()) {
            return;
        }

        if (!is_object($user = $token->getUser())) {
            // e.g. anonymous authentication
            return;
        }

        $this->user = $user;
    }
}

Register new processor:

# app/config/services.yml

services:
    monolog.processor.user:
    class: AppBundle\Monolog\UserProcessor
    arguments:  ["@security.token_storage"]
    tags:
        - { name: monolog.processor }
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

Symfony Documentation has problem

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top