Skip to content

Commit

Permalink
Support chained exceptions in PHP 5.3+
Browse files Browse the repository at this point in the history
  • Loading branch information
dcramer committed May 6, 2014
1 parent d5fe02a commit 71282f1
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 28 deletions.
69 changes: 47 additions & 22 deletions lib/Raven/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,12 @@ public function captureMessage($message, $params=array(), $level_or_options=arra
*/
public function captureException($exception, $culprit_or_options=null, $logger=null, $vars=null)
{
$has_chained_exceptions = version_compare(PHP_VERSION, '5.3.0', '>=');

if (in_array(get_class($exception), $this->exclude)) {
return null;
}

$exc_message = $exception->getMessage();
if (empty($exc_message)) {
$exc_message = '<unknown exception>';
}

if (!is_array($culprit_or_options)) {
$data = array();
if ($culprit_or_options !== null) {
Expand All @@ -209,13 +206,53 @@ public function captureException($exception, $culprit_or_options=null, $logger=n
$data = $culprit_or_options;
}

$data['message'] = $exc_message;
// TODO(dcramer): DRY this up
$message = $exception->getMessage();
if (empty($message)) {
$message = '<unknown exception>';
}

do {
$exc_message = $exception->getMessage();
if (empty($exc_message)) {
$exc_message = '<unknown exception>';
}

$exc_data = array(
'value' => $exc_message,
'type' => get_class($exception),
'module' => $exception->getFile() .':'. $exception->getLine(),
);

/**'sentry.interfaces.Exception'
* Exception::getTrace doesn't store the point at where the exception
* was thrown, so we have to stuff it in ourselves. Ugh.
*/
$trace = $exception->getTrace();
$frame_where_exception_thrown = array(
'file' => $exception->getFile(),
'line' => $exception->getLine(),
);

array_unshift($trace, $frame_where_exception_thrown);

// manually trigger autoloading, as it's not done in some edge cases due to PHP bugs (see #60149)
if (!class_exists('Raven_Stacktrace')) {
spl_autoload_call('Raven_Stacktrace');
}

$exc_data['stacktrace'] = array(
'frames' => Raven_Stacktrace::get_stack_info($trace, $this->trace, $this->shift_vars, $vars),
);

$exceptions[] = $exc_data;

} while ($has_chained_exceptions && $exception = $exception->getPrevious());

$data['message'] = $message;
$data['sentry.interfaces.Exception'] = array(
'value' => $exc_message,
'type' => get_class($exception),
'module' => $exception->getFile() .':'. $exception->getLine(),
'values' => array_reverse($exceptions),
);

if ($logger !== null) {
$data['logger'] = $logger;
}
Expand All @@ -228,18 +265,6 @@ public function captureException($exception, $culprit_or_options=null, $logger=n
}
}

/**'sentry.interfaces.Exception'
* Exception::getTrace doesn't store the point at where the exception
* was thrown, so we have to stuff it in ourselves. Ugh.
*/
$trace = $exception->getTrace();
$frame_where_exception_thrown = array(
'file' => $exception->getFile(),
'line' => $exception->getLine(),
);

array_unshift($trace, $frame_where_exception_thrown);

return $this->capture($data, $trace, $vars);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Raven/Serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Raven_Serializer
* Serialize an object (recursively) into something safe for data
* sanitization and encoding.
*/
public static function serialize($value, $max_depth=6, $_depth=0)
public static function serialize($value, $max_depth=7, $_depth=0)

This comment has been minimized.

Copy link
@fox-hellraiser

fox-hellraiser May 13, 2014

7 for $max_depth is not enought, because some values of stacktrace will be serialized with string "Array of length N", and Sentry will not display any chained exceptions, except last ("child"). My tests tells that 8 is enoght for this, but better is set greater value (10, for example), or some "smart check" for future.

P.S. simple test case that breaks depth 7 for me:

try {
    throw new LogicException('GRANDPARENT');
} catch (Exception $grandParentException) {
    try {
        throw new RuntimeException('PARENT', 0, $grandParentException);
    } catch (Exception $parentException) {
        try {
            throw new InvalidArgumentException('CHILD', 0, $parentException);
        } catch (Exception $childException) {
            // use raven to capture exception here
        }
    }
}
{
if (is_object($value) || is_resource($value)) {
return self::serializeValue($value);
Expand Down
11 changes: 6 additions & 5 deletions test/Raven/Tests/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,13 @@ public function testCaptureExceptionSetsInterfaces()
$event = array_pop($events);

$exc = $event['sentry.interfaces.Exception'];
$this->assertEquals($exc['value'], 'Foo bar');
$this->assertEquals($exc['type'], 'Exception');
$this->assertFalse(empty($exc['module']));
$this->assertEquals(count($exc['values']), 1);
$this->assertEquals($exc['values'][0]['value'], 'Foo bar');
$this->assertEquals($exc['values'][0]['type'], 'Exception');
$this->assertFalse(empty($exc['values'][0]['module']));

$this->assertFalse(empty($event['sentry.interfaces.Stacktrace']['frames']));
$frames = $event['sentry.interfaces.Stacktrace']['frames'];
$this->assertFalse(empty($exc['values'][0]['stacktrace']['frames']));
$frames = $exc['values'][0]['stacktrace']['frames'];
$frame = $frames[count($frames) - 1];
$this->assertTrue($frame['lineno'] > 0);
$this->assertEquals($frame['module'], 'ClientTest.php:Raven_Tests_ClientTest');
Expand Down

0 comments on commit 71282f1

Please sign in to comment.