10

I need a way to capture PHP fatal errors (but also notices and warnings) and logging them by using Monolog.

I found that Monolog 1.6+ has the ErrorHandler::register() method, but I can't figure out how to use it in a Symfony2 (production) application, and how properly configure it in config.yml.

3
  • Usually symfony is configured that all php errors, warnings and notices are converted to exception and get handled by symfony, so they get already logged. Catching fatals depends on your php version. Commented Mar 12, 2014 at 18:08
  • Thank you @Pazi. I'm using PHP 5.5 and when a Fatal error is issued I cannot find anything reported in the symfony logs, instead I found errors logged in the apache error log... Commented Mar 12, 2014 at 18:31
  • Tried this? symfony.com/doc/current/cookbook/logging/monolog_email.html Commented Mar 12, 2014 at 18:56

2 Answers 2

11

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:

<?php

namespace 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.

Sign up to request clarification or add additional context in comments.

2 Comments

Hi @fdellutri, really useful question. I am also looking for a way to setup Monolog to catch Fatal Errors and I havent found a simple solution, like using monolog yml configuration. Have you found a better solution ?
@SergioCosta the proposed solution is the only that I found to solve this issue. This solution is in production and it works as expected.
1

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.yml
parameters:
    # ...

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.php
namespace 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)!

3 Comments

Hi @jenechka, this method works for exceptions raised, but PHP errors doesn't raise an exception. To catch a fatal, first of all, you need to register an error handler (see stackoverflow.com/questions/277224/…). I think I need to register as error handler a my own method, and log the error. So, the questionon could be: "which is the best way to register an error handler in Symfony2?
@fdellutri I have updated my answer. Now it should work.
I posted a solution to my question. Now it works, but I don't know if it is a best practice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.