Log PHP fatal errors in Symfony2 using Monolog
Thanks to @jenechka, who pointed me to the right direction, I think I found a solution:
services.yml:
vir.exception.listener: class: %vir.exception.listener.class% arguments: ["@logger"] tags: - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Error Handler:
<?phpnamespace Mitecube\VoglioilruoloBundle\Listener;use Symfony\Component\Debug\ErrorHandler;use Symfony\Component\HttpKernel\Event\GetResponseEvent;use Psr\Log\LoggerInterface;class VoglioilruoloErrorHandler extends ErrorHandler { private $logger; private $prevErrorHandler; public function __construct(LoggerInterface $logger) { $this->logger = $logger; $this->prevErrorHandler = set_error_handler(array($this, 'handle')); register_shutdown_function(array($this, 'handleFatal')); } public function onKernelRequest(GetResponseEvent $event) { } public function handle($level, $message, $file = 'unknown', $line = 0, $context = array()) { $this->logger->error($level . ": " . $message . " - in file " . $file . " - at line " . $line); return parent::handle($level, $message, $file, $line, $context); }}
In this way I able to log each error to monolog. I don't think this solution can be considered a "best practice", so I'm still looking for a better solution.
Create exception listener as described here http://symfony.com/doc/current/cookbook/service_container/event_listener.html
And see here http://symfony.com/doc/current/reference/dic_tags.html#monolog-logger how to send logger as argument to your listener
Service example configuration:
# src/Acme/DemoBundle/Resources/config/services.ymlparameters: # ...services: # ... kernel.listener.your_listener_name: class: Acme\DemoBundle\EventListener\AcmeExceptionListener arguments: ["@logger"] tags: - { name: kernel.event_listener, event: kernel.exception, method: onKernelException } - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } - { name: monolog.logger, channel: tema }
Listener example:
// src/Acme/DemoBundle/EventListener/AcmeExceptionListener.phpnamespace Acme\DemoBundle\EventListener;use Symfony\Component\Debug\ExceptionHandler;use Symfony\Component\HttpKernel\Event\GetResponseEvent;use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;use Symfony\Component\HttpFoundation\Response;use Psr\Log\LoggerInterface;class ExceptionListener extends ExceptionHandler{ private $logger; private $prevExceptionHandler; public function __construct(LoggerInterface $logger) { $this->logger = $logger; // Set our handle method as fatal exception handler. // It is required to extend Symfony\Component\Debug\ExceptionHandler $this->prevExceptionHandler = set_exception_handler(array($this, 'handle')); } public function onKernelRequest(GetResponseEvent $event) { } /** * Handles non fatal exceptions (normal way). */ public function onKernelException(GetResponseForExceptionEvent $event) { // You get the exception object from the received event $exception = $event->getException(); // Log exception. $this->logger->error($exception->getMessage()); // ... } /** * Overwrite ExceptionHandler method. */ public function handle(\Exception $exception) { // Call our custom handler. $this->onFatalErrorException($exception); // Call exception handler that was overridden. // Or try to call parent::handle($exception) if (is_array($this->prevExceptionHandler) && $this->prevExceptionHandler[0] instanceof ExceptionHandler) { $this->prevExceptionHandler[0]->handle($exception); } } public function onFatalErrorException(\Exception $exception) { // Do anything you want... $this->logger->error('Hey, I got it: '. $exception->getMessage()); }}
UPDATED: I have improved exception listener and now it handles fatal exceptions. Tested (on dev environment)!