2

I want to persist a Log entity (ip,url,path,user,date...) everytime a URL is requested or served. I'm getting the "EntityManager is closed" error. I'm looking for a simple solution if possible. My code is the following:

First the YAML services:

kernelListener:
    class: AppBundle\EventListener\KernelListener
    arguments: [ '@my_logger']
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
        - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
        - { name: kernel.event_listener, event: kernel.terminate, method: onKernelTerminate }
        - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

my_logger:
    class:        AppBundle\Service\Logger
    arguments: ['@doctrine.orm.entity_manager', '@security.token_storage' ]
    scope: prototype  

Here it's my Logger service:

 namespace AppBundle\Service;
use AppBundle\Entity\Log;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;

class Logger {

private $em, $token;
public function __construct(EntityManager $em, TokenStorage $token) {
    $this->em = $em;
    $this->token_storage = $token;
}

public function persist($r) {

    $log = new Log();
    $log->setAjax($r->isXmlHttpRequest());
    $log->setIp($r->getClientIp());
    $log->setMethod($r->getMethod());
    $log->setPath($path);
    $log->setUrl($r->getUri());

    if ($this->token_storage->getToken()) {
        $user = $this->token_storage->getToken()->getUser();
        if ($user instanceof \AppBundle\Entity\User) {
            $log->setUser($user);
        }
    }

    $this->em->persist($log);
    $this->em->flush();
}

}

and the KernelListener.php

 namespace AppBundle\EventListener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use AppBundle\Service\Logger;

class KernelListener {

protected $logger;

public function __construct(Logger $logger) {
    $this->logger = $logger;
}

public function onKernelRequest(GetResponseEvent $event) {

}

public function onKernelResponse(FilterResponseEvent $event) {
}

 public function onKernelController(FilterControllerEvent $event) {
    $r = $event->getRequest();
    $this->logger->persist($r);
}

public function onKernelTerminate(PostResponseEvent $event) {        
}
}

If I remove the flush() no error is thrown, but no Log in the DB logically.

Arco Voltaico
  • 860
  • 13
  • 29

2 Answers2

1

Error EntityManager is closed appears because there were some database-level exceptions. So, logic of entity manager is following: if there was an exception, EntityManager switches to "closed" state and you can't use it anymore.

If you want to use an EntityManager - you need to create a new one. For example, as explained in https://stackoverflow.com/a/19077050/6699227:

if (!$this->entityManager->isOpen()) {
    $this->entityManager = $this->entityManager->create(
        $this->entityManager->getConnection(),
        $this->entityManager->getConfiguration()
    );
}

Another solution for your case could be to create another entity manager, just for writing logs. It will not be used by application itself, so it will never be closed.

svgrafov
  • 1,970
  • 4
  • 16
  • 32
Maksym Moskvychev
  • 1,471
  • 8
  • 11
0

Maksym was right. Thanks! In fact I used some months ago, this technique for a Service logging errors, but memory is weak specially when you are running many tasks :-(

So my working Logger is :

    public function persist($r) {
    if (!$this->em->isOpen()) {
        $this->em = $this->em->create(
                $this->em->getConnection(), $this->em->getConfiguration()
        );
    }

    $path = $r->get('_route');
    if (!$path || in_array($path, $this->paths)) {
        return null;
    }

    $log = new Log();
    $log->setAjax($r->isXmlHttpRequest());
    $log->setIp($r->getClientIp());
    $log->setMethod($r->getMethod());
    $log->setPath($path);
    $log->setUrl($r->getUri());

    if ($this->token_storage->getToken()) {
        $user = $this->token_storage->getToken()->getUser();
        if ($user instanceof \AppBundle\Entity\User) {
            $user2 = $this->em->getReference('AppBundle:User', $user->getId());
            $log->setUser($user2);
        }
    }

    $this->em->persist($log);
    $this->em->flush();
}
Arco Voltaico
  • 860
  • 13
  • 29