Лог PHP фатальные ошибки в Symfony2 с помощью Monolog

Мне нужен способ для записи фатальных ошибок PHP (но также уведомлений и предупреждений) и регистрации их с помощью Monolog.

Я обнаружил, что Monolog 1.6+ имеет метод ErrorHandler:: register(), но я не могу понять, как его использовать в приложении Symfony2 (production), и как правильно настроить его в config.yml.

Ответ 1

Благодаря @jenechka, который указал мне в правильном направлении, я думаю, что нашел решение:

services.yml:

    vir.exception.listener:
    class: %vir.exception.listener.class%
    arguments: ["@logger"]
    tags:
        - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

Обработчик ошибок:

<?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);
    }

} 

Таким образом, я могу записывать каждую ошибку в монолог. Я не думаю, что это решение можно считать "лучшей практикой", поэтому я все еще ищу лучшее решение.

Ответ 2

Создать прослушиватель исключений, как описано здесь http://symfony.com/doc/current/cookbook/service_container/event_listener.html

И см. здесь http://symfony.com/doc/current/reference/dic_tags.html#monolog-logger, как отправить регистратор в качестве аргумента для вашего слушателя

Конфигурация служебного примера:

# 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 }

Пример прослушивателя:

// 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());
    }
}

ОБНОВЛЕНО. У меня улучшен прослушиватель исключений, и теперь он обрабатывает фатальные исключения. Протестировано (в среде Dev)!