Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.0] Refactor the error and exception handlers #584

Merged
merged 6 commits into from
May 13, 2018
Merged

[2.0] Refactor the error and exception handlers #584

merged 6 commits into from
May 13, 2018

Conversation

ste93cry
Copy link
Contributor

@ste93cry ste93cry commented Apr 24, 2018

Q A
Branch? 2.0
Bug fix? no
New feature? yes
BC breaks? yes
Deprecations? no
Tests pass? yes
License MIT

This PR is a huge refactoring of the error and exception handlers. I tried to make them as transparent as possible in regards to other handlers, so the error handler converts the errors to exceptions to be able to log them but never throws them unless necessary. The aim of my work is to fix the many bugs that we're experiencing in 1.x, in particular #579, #552, #391, #343. I'm currently tracking an issue with Zend Diactoros (zendframework/zend-diactoros#298) that prevents any error past the first one from being logged through custom error handlers after usage of the Stream class.

@ste93cry ste93cry added this to the Release 2.0 milestone Apr 24, 2018
Copy link
Contributor

@Jean85 Jean85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a good work for now! 👍

For test, mind that a try...catch would stop even failed assertions.

/**
* @var ErrorHandler The instance of the error handler
*/
private $errorHandler;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this back up to reduce the diff?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've decided to remove the property as the client does not necessarily need to know the instance of the error handler and so that there is no circular reference between them

// This prevents duplicated exceptions in PHP 7.0+
if (PHP_VERSION_ID >= 70000 && E_ERROR === $type) {
return false;
if (!empty($error) && $error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should wrap those two lines with a private method to give it a name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracting the code that just checks whether the error is a fatal error does not makes much sense imho, and there would also be the cost of calling one more function

* @expectedException \UnexpectedValueException
* @expectedExceptionMessage The value of the $reservedMemorySize argument must be an integer greater than 0.
*/
public function testConstructorThrowsWhenReservedMemorySizeIsWrong()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not using the data from the provider

// If there is no exception handler that can run after the current one
// give back the exception to the native PHP handler
if (null === $this->previousExceptionHandler) {
throw $exception;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't re-throwing change the stack trace of the exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After a few tests it seems it doesn't

$this->previousExceptionHandler = null;

try {
call_user_func($previousExceptionHandler, $exception);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$previousExceptionHandler($exception) is maybe slightly faster?


return $this;
$this->handleException($previousExceptionHandlerException);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this loop-prone?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it should be as the next time the function will be called $this->previousExceptionHandler will be null and after sending the exception to Sentry the exception itself will be rethrown

$pendingRequests = Assert::getObjectAttribute($transport, 'pendingRequests');

Assert::assertEmpty($pendingRequests);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those asserts can trigger the autoloading. If this is done under PHP 5 inside an error handling context, it fails silently. You should add an echo at the end and expect it to be sure that all those assertions are executed correctly.

$foo = str_repeat('x', 1024 * 1024 * 30);
?>
--EXPECTF--
Fatal error: Allowed memory size of %d bytes exhausted (tried to allocate %d bytes) in %s on line %d
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you add something to be sure that you really handled the error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, I've added an echo statement after all the assertions whose existence is checked in the --EXPECTF-- section of the PHPT test

@Jean85 Jean85 mentioned this pull request May 4, 2018
15 tasks
@Jean85 Jean85 changed the title [WIP] [2.0] Refactor the error and exception handlers [2.0] Refactor the error and exception handlers May 4, 2018
Copy link
Contributor

@Jean85 Jean85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now! 👍 Great work!

@ste93cry
Copy link
Contributor Author

ste93cry commented May 4, 2018

I still have to complete the refactor of the ErrorHandler for the breadcrumbs. Please don't merge this PR yet until I remove the WIP prefix

@Jean85
Copy link
Contributor

Jean85 commented May 4, 2018

Sorry, I thought it was complete!

@Jean85 Jean85 changed the title [2.0] Refactor the error and exception handlers [WIP] [2.0] Refactor the error and exception handlers May 4, 2018
@ste93cry
Copy link
Contributor Author

ste93cry commented May 6, 2018

@dcramer @stayallive @Jean85 In 1.x there is an additional error handler that only registers a breadcrumb. Does it makes sense to leave that class which should duplicate all the logic of the new ErrorHandler (with all the code to make it working even with PHP bug 63206) or can we just drop the install_default_breadcrumb_handlers option and just always log the breadcrumb in case of error (which imho it makes sense)?

@Jean85
Copy link
Contributor

Jean85 commented May 6, 2018

IMHO the option is indipentent from the practical implementation of the handler. I agree that we can reuse the same code to do that stuff, but I would leave them separate, so an end user would still be able to customize the breadcrumbs.

Maybe we can create two instances? Or two different child classes?

@ste93cry
Copy link
Contributor Author

ste93cry commented May 6, 2018

so an end user would still be able to customize the breadcrumbs.

Would it makes sense for an user to use only the error handler that registers the breadcrumbs and not the handler that captures the error itself?

@Jean85
Copy link
Contributor

Jean85 commented May 6, 2018

No, obviously not, why?

@ste93cry
Copy link
Contributor Author

ste93cry commented May 6, 2018

Because then it would not make sense to have two error handlers, one for capturing the errors and one for registering the breadcrumb. Or at least, I don't see the sense behind this. Having two handlers means reserving memory for both of them (to handle fatal errors) and duplicating the logic for each of them. Also, I don't see why an user would want to not register the breadcrumbs but instead capture the errors

@Jean85
Copy link
Contributor

Jean85 commented May 6, 2018

The scenario that I want to preserve is the possibility of replacing (or enhancing) the breadcrumb handler. If you collapse both handler this would not be possible anymore.

@ste93cry ste93cry requested a review from Jean85 May 9, 2018 20:21
@ste93cry
Copy link
Contributor Author

ste93cry commented May 9, 2018

I updated the PR by refactoring the breadcrumb handler and it's ready for another round of code reviews. The zendframework/zend-diactoros#298 issue persists and if nothing changes before releasing 2.0 we will have to switch PSR-7 library or no errors after the first one will be logged and sent to the Sentry server

Copy link
Contributor

@Jean85 Jean85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic work!! 🎉


$errorAsException = new \ErrorException(self::ERROR_LEVELS_DESCRIPTION[$level] . ': ' . $message, 0, $level, $file, $line);

$backtrace = $errorAsException->getTrace();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be inlined

@ste93cry
Copy link
Contributor Author

I fixed the fact that fatal errors were reported twice in PHP 7+ (yes, again...) and this should be the good time. Before merging, should I switch PSR-7 library in this PR or do you prefer to postpone it later?

@Jean85
Copy link
Contributor

Jean85 commented May 13, 2018

I think that switching PSR-7 implementor should be done in a separate PSR, let's keep it simple!

@ste93cry ste93cry changed the title [WIP] [2.0] Refactor the error and exception handlers [2.0] Refactor the error and exception handlers May 13, 2018
@ste93cry ste93cry merged commit ec2ec1e into getsentry:2.0 May 13, 2018
@ste93cry ste93cry deleted the error-handler-refactoring branch May 13, 2018 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants