3

We have developed an extension to capture errors and exceptions in Magento and then forward them to a third-party (in this case Raygun.io).

We are able to use set_error_handler to capture errors without trouble, however set_exception_handler can not work as Magento captures all exceptions in app/Mage.php (this is the case with at least 1.8 and 1.9). Because of this we have resorted to asking users to make a small modification to app/Mage.php to forward the exception to our extension.

The code below is added to app/Mage.php:693:

    die();
} catch (Exception $e) {
    // BEGIN
    if (class_exists('Springtimesoft_Raygun_Helper_Data')) {
        self::helper('springtimesoft_raygun')->exceptionHandler($e);
    }
    // END

    if (self::isInstalled() || self::$_isDownloader) {

Obviously hacking core is bad, is there a better method? Following the logic line by line hasn't shown any alternatives so far, not even a very "hacky" method (for all intents and purposes we can't redefine functions in PHP).

One alternative is to fire a new event here so that at least other extensions are able to make use of this as well. Ideally Magento could add this in a future version (2.x?).

Edit

As per the answer below, it looks like the best way forward is to periodically check for new exceptions that have been added into var/log. This doesn't allow for catching exceptions at the time they occur but is close enough and doesn't require modifying Mage.php.

1

This is the catch that is used to handle exceptions in run() method inside Mage.php

catch (Exception $e) {
        if (self::isInstalled() || self::$_isDownloader) {
            self::printException($e);
            exit();
        }
        try {
            self::dispatchEvent('mage_run_exception', array('exception' => $e));
            if (!headers_sent() && self::isInstalled()) {
                header('Location:' . self::getUrl('install'));
            } else {
                self::printException($e);
            }
        } catch (Exception $ne) {
            self::printException($ne, $e->getMessage());
        }
    }

As you can see, Magento uses an event in order to handle all exceptions.

self::dispatchEvent('mage_run_exception', array('exception' => $e));

The parameter passed to the event mage_run_exception is the exception varible itself $e. After that Magento is printing this exception with

 self::printException($ne, $e->getMessage());

So the event mage_run_exception is a perfect place for your need. You can observe this event and do whatever things with the exception variable as like this.

Observer.php

public function ExceptionHandler($observer) 
{
    $exceptions = $observer->getEvent()->getException();

   //do something with $exception here
   if (class_exists('Springtimesoft_Raygun_Helper_Data')) {
       self::helper('springtimesoft_raygun')->exceptionHandler($exception);
   }

    return;
}

EDIT

Unfortunately, it is not possible(As per my knowledge). This is because the catch method that you are concentrated in uses php core class for exception. See the definition

catch (Exception $e) {
}

Due to this, you cannot do anything without altering the base code. If magento used any of its own core classes instead of php core class Exception, we could have a mere possibility to do this job without touching Mage.php. However in this case, it is unavoidable. However you can do below code for better result

catch (Namespace_Modulename_Model_Exception $e) {
}

where Namespace_Modulename is your own module. This will make $e as your own exception class object. Now by decalring the class Namespace_Modulename_Model_Exception, you can do anything with exception. In this case your custom exception handing class should looks like this.

File : app\code\local\Namespace\Modulename\Model\Exception.php

<?php

class Namespace_Modulename_Model_Exception extends Exception 
{
           //your alreation methods comes here.
}

Please note that, I didn't try this before. In theory, it should work.

Additional Note :- When developer mode is disabled, Magento places all exceptions in var/log directory. By processing files in that directory using file manipulation methods, you can get all exceptions that is generating for each request. For an example you can retrieve exception from those files, place it in an email and then send and so on.

|improve this answer|||||
  • Thank you for your detailed answer, if you look at the code above you'll notice that before dispatching the event Magento checks self::isInstalled() which returns true, leading to self::printException($e); exit();. So in most cases we're still unable to catch exceptions with this method. – Springtimesoft Sep 23 '14 at 4:20
  • oh I see.. then what about other two catches that are appearing above this? How did you handle those errors ? – Rajeev K Tomy Sep 23 '14 at 4:55
  • We haven't hooked into those exceptions so far as they didn't appear to be useful (the first looks like it handles an expired session and the second handles 404 errors, so we aren't too concerned with these). Though we are certainly open to a solution that can handle all of the above! – Springtimesoft Sep 23 '14 at 5:31
  • Please go through my edit – Rajeev K Tomy Sep 23 '14 at 5:51
  • Thanks. I'm not sure that renaming the exception would work as all Exceptions would then need to inherit from the renamed class. The var/log method is better as it doesn't involve changing Mage.php. I previously thought the exceptions weren't being written to var/log even with developer mode off, but I will test this further. In this case we still can't catch exceptions at the time they happen but perhaps as early as the next request (or via cron). – Springtimesoft Sep 23 '14 at 6:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.