zendframework / zend-log

Log component from Zend Framework
BSD 3-Clause "New" or "Revised" License
62 stars 51 forks source link

Logger is not logging fatal errors or exceptions. #94

Closed spectravp closed 5 years ago

spectravp commented 5 years ago

Logger is not logging fatal errors or exceptions.

Code to reproduce the issue

// module/Application/src/Module.php
namespace Application;

use Zend\Log\Logger;
use Zend\Log\Writer\Stream;
use Zend\Mvc\MvcEvent;

/**
 * Class Module
 * @package Application
 */
class Module
{
    /**
     * @param MvcEvent $e
     */
    public function onBootstrap(MvcEvent $e)
    {
        $errorLogger = new Logger();
        $writer = new Stream('log/errors.log');
        $errorLogger->addWriter($writer);

        Logger::registerErrorHandler($errorLogger);
        Logger::registerErrorHandler($errorLogger);
        Logger::registerExceptionHandler($errorLogger);

        $errorLogger->info('Test' . microtime(true));
    }
}

// module/Application/src/Controller/IndexController.php
namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        throw new \Exception('Fred', 500);
        return $this->view;
    }
}

Expected results

Expect the "Test" message to be written and the Exception to be written.

Actual results

Only the "Test" message is written:

2018-10-05T12:32:31-04:00 INFO (6): Test1538757151.7417
samsonasik commented 5 years ago

you can use dispatch.error event

spectravp commented 5 years ago

@samsonasik No, you shouldn't use that event. That event is specific to using the MVC Router and specific to only dispatch errors. What about render errors, route errors, etc. What if a filter throws an exception or a database adapter that is bootstrapped before the MVC cycle?

The logger is supposed to catch fatal errors and other Exceptions. i.e., this package is a wrapper that registers an error handler using php's native set_exception_handler(), register_shutdown_function(), and set_error_handler() functions.

samsonasik commented 5 years ago

If there is code that run before MVC, next it will always hit in mvc cycle, so for example, you have a service that not registered, and need to be called, eg: in controller factory, it will hit dispatch.error. For render error, you can use render.error event. There is no route error event, while 404 page can programmatically be handled via 'dispatch.error' and 'dispatch' event with EventManager and SharedEventManager.

To make your code sample work, you can use dispatch.error and render.error like below:

    public function onBootstrap(MvcEvent $e)
    {
        $errorLogger = new Logger();
        $writer      = new Stream('log/errors.log');
        $errorLogger->addWriter($writer);

        Logger::registerErrorHandler($errorLogger);
        Logger::registerExceptionHandler($errorLogger);

        $eventManager = $e->getApplication()->getEventManager();
        foreach (['dispatch.error', 'render.error'] as $row) {
            $eventManager->attach($row, function ($e) {
                $exception = $e->getParam('exception');
                if (! $exception) {
                    return;
                }

                throw $exception;
            });
        }
    }

Anyway, I created a module that handle php errors & exceptions in ZF2/3 and Zend Expressive 1,2,3 application that even handle all kinds of fatal errors: https://github.com/samsonasik/ErrorHeroModule

spectravp commented 5 years ago

Perhaps, but I shouldn’t have to wire up my own event listeners. This is a documented feature of Zend Log. It should work and it doesn’t. And fatal errors will not trigger the dispatch error event. For example, if the application attempts to call a native php function that isn’t available, such as those provided via the MongoDB pecl extension. Adding an external package isn’t a solution. Writing code to replace the documented functionality isn’t a solution. I do appreciate you taking the time to respond.

samsonasik commented 5 years ago

The zend-log documentation doesn't say that Logger::registerExceptionHandler($errorLogger); will ("it just works") in MVC process for logging.

If you call the exception inside IndexController::indexAction(), it means, it will trigger dispatch.error event, the Exception by default is not thrown, it is catched via try catch with bring exception object to be re-used, eg: in error/index.phtml.

Xerkus commented 5 years ago

Logger can only catch uncaught exceptions at the top most scope, where they otherwise get converted into fatal errors. See https://secure.php.net/manual/en/function.set-exception-handler.php Exceptions in MVC applications are normally caught by framework and error handling event is triggered. Unless there is another error/exception during error handling, you won't have uncaught exceptions. Expressive applications have error middlewares to do the same.

Fatal errors halt execution and can not be intercepted with error handler as they are not recoverable. See https://secure.php.net/manual/en/function.set-error-handler.php

Xerkus commented 5 years ago

Unless I misunderstood and registerFatalErrorShutdownFunction() does not work for fatal errors.