无法捕获全局异常

时间:2013-11-21 19:40:33

标签: php

如果指定的日志文件出现问题,Monolog的StreamHandler会抛出\UnexpectedValueException

在我的代码中,我试图抓住\UnexpectedValueException,但我一直无法抓住。

我的代码是:

<?php
namespace myNamespace;

use Monolog\Logger as Monolog;
use Monolog\Handler\StreamHandler;


class Logger {

    private static $instance;

    private static function init()
    {
        if (!self::$instance)
        {
            $logger = new Monolog(MY_LOG_CHANNEL);

            try 
            {                
                $logger->pushHandler(new StreamHandler(MY_LOG_PATH . "/" . 
                   MY_LOG_NAME, Monolog::NOTICE));            
            }
            catch (\UnexpectedValueException $e)
            {
                writeln("Error starting logger" . $e->getMessage());
                die;
            }

            // and so on

它不起作用,我明白了:

Fatal error: Uncaught exception 'UnexpectedValueException' with message 'The stream or
file [somefile.log] could not be opened: failed to open stream: No such file or
directory' in [path to src/Monolog/Handler/StreamHandler.php ]

据我了解,他们已经转移到全局命名空间,所以我应该能够在那里找到它。为什么我不能?我已经尝试了名称空间\myNamesace\UnexpectedValueException甚至Monolog\UnexpectedValueException全局或本地的所有组合,但无济于事。

显然我错过了什么,请问是什么?

修改

在另一堂课中,我正在做if (file_exists($fileName)) /** do file_get_contents() etc */ else Logger::error($filename . "does not exist")

当我致电Logger::error()

时,self::init()内会触发错误

导致错误是因为我(故意)对日志文件路径进行了操作,如果它是有效的日志文件路径,那么代码运行正常。显然,我想捕捉到这个错误,因此是try / catch。

跟踪中的下一行是上面代码中的行:$logger->pushHandler(new StreamHandler(MY_LOG_PATH . "/" . MY_LOG_NAME, Monolog::NOTICE));

有趣的是,如果我故意错误拼写文件名var,那么我正在捕捉异常的唯一其他地方(目前这只是脚手架代码,还没有业务逻辑)在/** do file_get_contents() etc */位内。 {1}} barfs和标准 file_get_contents正如我期望的那样catch (Exception $e)

2 个答案:

答案 0 :(得分:2)

这是一个很老的问题,可能在此期间得到解决。但是,为了将来的访问者,我想指出,当日志条目写入给定文件时,抛出异常,而不是在构造时。

try { } catch () {}设置的方式,创建StreamHandler时会出现异常。但是,只要您尝试通过$logger->error("Error message")之类的调用发送到日志就会发生真正的异常,因此您应该在那里捕获异常。

另一方面,我认为从日志库中抛出异常是人们可以做的最愚蠢的事情之一。记录应该是幂等的,不会影响正在运行的应用程序的状态。

答案 1 :(得分:0)

在旧的silex应用程序中,我也遇到了这个问题。我想登录到logstash实例,但是如果logstash不可用,它也不会中断。

幸运的是,我的应用程序只使用了针对Psr\Log\LoggerInterface键入的记录器,因此我可以编写一个装饰器来防止异常在一个地方中断应用程序,而不必在代码库。

它看起来像这样:

<?php

namespace Dreamlines\DirectBookingForm\ServiceProvider;

use Psr\Log\LoggerInterface;

/**
 * DontThrowLoggerDecorator
 *
 * Monolog will break on info, error, etc. calls
 * This decorator wrap the LoggerInterface and ensures that failure to log won't break the app
 **/
class DontThrowLoggerDecorator implements LoggerInterface
{
    /**
     * @var LoggerInterface
     */
    private $logger;

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


    /**
     * @inheritDoc
     */
    public function emergency($message, array $context = array())
    {

        try {
            $this->logger->emergency($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function alert($message, array $context = array())
    {
        try {
            $this->logger->alert($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function critical($message, array $context = array())
    {
        try {
            $this->logger->critical($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function error($message, array $context = array())
    {
        try {
            $this->logger->error($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function warning($message, array $context = array())
    {
        try {
            $this->logger->warning($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function notice($message, array $context = array())
    {
        try {
            $this->logger->notice($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function info($message, array $context = array())
    {
        try {
            $this->logger->info($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function debug($message, array $context = array())
    {
        try {
            $this->logger->debug($message, $context);
        } catch (\Exception $e) {
        }
    }

    /**
     * @inheritDoc
     */
    public function log($level, $message, array $context = array())
    {
        try {
            $this->logger->log($level, $message, $context);
        } catch (\Exception $e) {
        }
    }
}

然后我将记录器实例包装一次,在我的silex应用程序中,我这样做是这样的:

$app['NOT_BREAKING_LOGGER'] = $app->share(
    function () use ($app) {
        return new DontThrowLoggerDecorator($app['monolog']);
    }
);

我将在每个服务中注入NOT_BREAKING_LOGGER,而不是独白。