使用 Monolog 在 Symfony2 中记录 PHP 致命错误

Posted

技术标签:

【中文标题】使用 Monolog 在 Symfony2 中记录 PHP 致命错误【英文标题】:Log PHP fatal errors in Symfony2 using Monolog 【发布时间】:2014-04-17 02:13:51 【问题描述】:

我需要一种方法来捕获 php 致命错误(以及通知和警告)并使用 Monolog 记录它们。

我发现 Monolog 1.6+ 有 ErrorHandler::register() 方法,但我不知道如何在 Symfony2(生产)应用程序中使用它,以及如何在 config.yml 中正确配置它。

【问题讨论】:

通常 symfony 配置为所有 php 错误、警告和通知都转换为异常并由 symfony 处理,因此它们已经被记录了。捕获致命错误取决于您的 php 版本。 谢谢@Pazi。我正在使用 PHP 5.5,当发出致命错误时,我在 symfony 日志中找不到任何报告,而是在 apache 错误日志中发现了错误... 试过这个吗? symfony.com/doc/current/cookbook/logging/monolog_email.html 【参考方案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);
    

 

通过这种方式,我能够将每个错误记录到独白中。我不认为这个解决方案可以被认为是“最佳实践”,所以我仍在寻找更好的解决方案。

【讨论】:

嗨@fdellutri,非常有用的问题。我也在寻找一种设置 Monolog 以捕获致命错误的方法,但我还没有找到一个简单的解决方案,比如使用 monolog yml 配置。您找到更好的解决方案了吗? @SergioCosta 建议的解决方案是我发现解决此问题的唯一方法。此解决方案已投入生产,并按预期工作。【参考方案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());
    

更新:我改进了异常侦听器,现在它可以处理致命异常。经过测试(在开发环境中)!

【讨论】:

嗨@jenechka,此方法适用于引发的异常,但 PHP 错误不会引发异常。要捕获致命错误,首先,您需要注册一个错误处理程序(请参阅***.com/questions/277224/…)。我想我需要将自己的方法注册为错误处理程序,并记录错误。因此,问题可能是:“在 Symfony2 中注册错误处理程序的最佳方式是什么? @fdellutri 我已经更新了我的答案。现在它应该可以工作了。 我发布了我的问题的解决方案。现在它可以工作了,但我不知道这是否是最佳做法。

以上是关于使用 Monolog 在 Symfony2 中记录 PHP 致命错误的主要内容,如果未能解决你的问题,请参考以下文章

Symfony2/Monolog:日志级别 - 仅显示 app.INFO?

Symfony2 Monolog 配置为使用 raven 处理程序(Sentry)

配置 symfony monolog 保留 apache 日志

Symfony 2:将特定通道/处理程序的记录器注入服务

如何在 Symfony 中对 Monolog 消息进行高级过滤?

Graylog2 与 Symfony 2 (Monolog)