Skip to content

Commit 9af5037

Browse files
authored
Add fast path for ignoring errors (#1737)
1 parent 6051f41 commit 9af5037

File tree

5 files changed

+111
-44
lines changed

5 files changed

+111
-44
lines changed

src/ErrorHandler.php

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ final class ErrorHandler
100100
*/
101101
private $memoryLimitIncreaseOnOutOfMemoryErrorValue = 5 * 1024 * 1024; // 5 MiB
102102

103+
/**
104+
* @var Options|null The SDK options
105+
*/
106+
private $options;
107+
103108
/**
104109
* @var bool Whether the memory limit has been increased
105110
*/
@@ -153,12 +158,14 @@ private function __construct()
153158
/**
154159
* Registers the error handler once and returns its instance.
155160
*/
156-
public static function registerOnceErrorHandler(): self
161+
public static function registerOnceErrorHandler(?Options $options = null): self
157162
{
158163
if (self::$handlerInstance === null) {
159164
self::$handlerInstance = new self();
160165
}
161166

167+
self::$handlerInstance->options = $options;
168+
162169
if (self::$handlerInstance->isErrorHandlerRegistered) {
163170
return self::$handlerInstance;
164171
}
@@ -326,17 +333,19 @@ private function handleError(int $level, string $message, string $file, int $lin
326333
}
327334
}
328335

329-
if ($isSilencedError) {
330-
$errorAsException = new SilencedErrorException(self::ERROR_LEVELS_DESCRIPTION[$level] . ': ' . $message, 0, $level, $file, $line);
331-
} else {
332-
$errorAsException = new \ErrorException(self::ERROR_LEVELS_DESCRIPTION[$level] . ': ' . $message, 0, $level, $file, $line);
333-
}
336+
if ($this->shouldHandleError($level, $isSilencedError)) {
337+
if ($isSilencedError) {
338+
$errorAsException = new SilencedErrorException(self::ERROR_LEVELS_DESCRIPTION[$level] . ': ' . $message, 0, $level, $file, $line);
339+
} else {
340+
$errorAsException = new \ErrorException(self::ERROR_LEVELS_DESCRIPTION[$level] . ': ' . $message, 0, $level, $file, $line);
341+
}
334342

335-
$backtrace = $this->cleanBacktraceFromErrorHandlerFrames($errorAsException->getTrace(), $errorAsException->getFile(), $errorAsException->getLine());
343+
$backtrace = $this->cleanBacktraceFromErrorHandlerFrames($errorAsException->getTrace(), $errorAsException->getFile(), $errorAsException->getLine());
336344

337-
$this->exceptionReflection->setValue($errorAsException, $backtrace);
345+
$this->exceptionReflection->setValue($errorAsException, $backtrace);
338346

339-
$this->invokeListeners($this->errorListeners, $errorAsException);
347+
$this->invokeListeners($this->errorListeners, $errorAsException);
348+
}
340349

341350
if ($this->previousErrorHandler !== null) {
342351
return false !== ($this->previousErrorHandler)($level, $message, $file, $line, $errcontext);
@@ -345,6 +354,20 @@ private function handleError(int $level, string $message, string $file, int $lin
345354
return false;
346355
}
347356

357+
private function shouldHandleError(int $level, bool $silenced): bool
358+
{
359+
// If we were not given any options, we should handle all errors
360+
if ($this->options === null) {
361+
return true;
362+
}
363+
364+
if ($silenced) {
365+
return $this->options->shouldCaptureSilencedErrors();
366+
}
367+
368+
return ($this->options->getErrorTypes() & $level) !== 0;
369+
}
370+
348371
/**
349372
* Tries to handle a fatal error if any and relay them to the listeners.
350373
* It only tries to do this if we still have some reserved memory at

src/Integration/ErrorListenerIntegration.php

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,65 @@
66

77
use Sentry\ErrorHandler;
88
use Sentry\Exception\SilencedErrorException;
9+
use Sentry\Options;
910
use Sentry\SentrySdk;
1011

1112
/**
1213
* This integration hooks into the global error handlers and emits events to
1314
* Sentry.
1415
*/
15-
final class ErrorListenerIntegration extends AbstractErrorListenerIntegration
16+
final class ErrorListenerIntegration extends AbstractErrorListenerIntegration implements OptionAwareIntegrationInterface
1617
{
18+
/**
19+
* @var Options
20+
*/
21+
private $options;
22+
23+
public function setOptions(Options $options): void
24+
{
25+
$this->options = $options;
26+
}
27+
1728
/**
1829
* {@inheritdoc}
1930
*/
2031
public function setupOnce(): void
2132
{
22-
$errorHandler = ErrorHandler::registerOnceErrorHandler();
23-
$errorHandler->addErrorHandlerListener(static function (\ErrorException $exception): void {
24-
$currentHub = SentrySdk::getCurrentHub();
25-
$integration = $currentHub->getIntegration(self::class);
26-
$client = $currentHub->getClient();
27-
28-
// The client bound to the current hub, if any, could not have this
29-
// integration enabled. If this is the case, bail out
30-
if ($integration === null || $client === null) {
31-
return;
32-
}
33-
34-
if ($exception instanceof SilencedErrorException && !$client->getOptions()->shouldCaptureSilencedErrors()) {
35-
return;
36-
}
37-
38-
if (!$exception instanceof SilencedErrorException && !($client->getOptions()->getErrorTypes() & $exception->getSeverity())) {
39-
return;
40-
}
41-
42-
$integration->captureException($currentHub, $exception);
43-
});
33+
ErrorHandler::registerOnceErrorHandler($this->options)
34+
->addErrorHandlerListener(
35+
static function (\ErrorException $exception): void {
36+
$currentHub = SentrySdk::getCurrentHub();
37+
$integration = $currentHub->getIntegration(self::class);
38+
$client = $currentHub->getClient();
39+
40+
// The client bound to the current hub, if any, could not have this
41+
// integration enabled. If this is the case, bail out
42+
if ($integration === null || $client === null) {
43+
return;
44+
}
45+
46+
if ($exception instanceof SilencedErrorException && !$client->getOptions()->shouldCaptureSilencedErrors()) {
47+
return;
48+
}
49+
50+
if (!$exception instanceof SilencedErrorException && !($client->getOptions()->getErrorTypes() & $exception->getSeverity())) {
51+
return;
52+
}
53+
54+
$integration->captureException($currentHub, $exception);
55+
}
56+
);
57+
}
58+
59+
/**
60+
* @internal this is a convenience method to create an instance of this integration for tests
61+
*/
62+
public static function make(Options $options): self
63+
{
64+
$integration = new self();
65+
66+
$integration->setOptions($options);
67+
68+
return $integration;
4469
}
4570
}

src/Integration/IntegrationRegistry.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public function setupIntegrations(Options $options, LoggerInterface $logger): ar
5858

5959
$integrations[$integrationName] = $integration;
6060

61-
if ($this->setupIntegration($integration)) {
61+
if ($this->setupIntegration($integration, $options)) {
6262
$installed[] = $integrationName;
6363
}
6464
}
@@ -70,14 +70,18 @@ public function setupIntegrations(Options $options, LoggerInterface $logger): ar
7070
return $integrations;
7171
}
7272

73-
private function setupIntegration(IntegrationInterface $integration): bool
73+
private function setupIntegration(IntegrationInterface $integration, Options $options): bool
7474
{
7575
$integrationName = \get_class($integration);
7676

7777
if (isset($this->integrations[$integrationName])) {
7878
return false;
7979
}
8080

81+
if ($integration instanceof OptionAwareIntegrationInterface) {
82+
$integration->setOptions($options);
83+
}
84+
8185
$integration->setupOnce();
8286

8387
$this->integrations[$integrationName] = true;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\Integration;
6+
7+
use Sentry\Options;
8+
9+
interface OptionAwareIntegrationInterface extends IntegrationInterface
10+
{
11+
/**
12+
* Sets the options for the integration, is called before `setupOnce()`.
13+
*/
14+
public function setOptions(Options $options): void;
15+
}

tests/Integration/IntegrationRegistryTest.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,13 @@ public function setupOnce(): void
7373
];
7474

7575
yield 'Default integrations and no user integrations' => [
76-
new Options([
76+
$options = new Options([
7777
'dsn' => 'http://public@example.com/sentry/1',
7878
'default_integrations' => true,
7979
]),
8080
[
8181
ExceptionListenerIntegration::class => new ExceptionListenerIntegration(),
82-
ErrorListenerIntegration::class => new ErrorListenerIntegration(),
82+
ErrorListenerIntegration::class => ErrorListenerIntegration::make($options),
8383
FatalErrorListenerIntegration::class => new FatalErrorListenerIntegration(),
8484
RequestIntegration::class => new RequestIntegration(),
8585
TransactionIntegration::class => new TransactionIntegration(),
@@ -104,7 +104,7 @@ public function setupOnce(): void
104104
];
105105

106106
yield 'Default integrations and some user integrations' => [
107-
new Options([
107+
$options = new Options([
108108
'dsn' => 'http://public@example.com/sentry/1',
109109
'default_integrations' => true,
110110
'integrations' => [
@@ -114,7 +114,7 @@ public function setupOnce(): void
114114
]),
115115
[
116116
ExceptionListenerIntegration::class => new ExceptionListenerIntegration(),
117-
ErrorListenerIntegration::class => new ErrorListenerIntegration(),
117+
ErrorListenerIntegration::class => ErrorListenerIntegration::make($options),
118118
FatalErrorListenerIntegration::class => new FatalErrorListenerIntegration(),
119119
RequestIntegration::class => new RequestIntegration(),
120120
TransactionIntegration::class => new TransactionIntegration(),
@@ -127,7 +127,7 @@ public function setupOnce(): void
127127
];
128128

129129
yield 'Default integrations and some user integrations, one of which is also a default integration' => [
130-
new Options([
130+
$options = new Options([
131131
'dsn' => 'http://public@example.com/sentry/1',
132132
'default_integrations' => true,
133133
'integrations' => [
@@ -137,7 +137,7 @@ public function setupOnce(): void
137137
]),
138138
[
139139
ExceptionListenerIntegration::class => new ExceptionListenerIntegration(),
140-
ErrorListenerIntegration::class => new ErrorListenerIntegration(),
140+
ErrorListenerIntegration::class => ErrorListenerIntegration::make($options),
141141
FatalErrorListenerIntegration::class => new FatalErrorListenerIntegration(),
142142
RequestIntegration::class => new RequestIntegration(),
143143
FrameContextifierIntegration::class => new FrameContextifierIntegration(),
@@ -149,7 +149,7 @@ public function setupOnce(): void
149149
];
150150

151151
yield 'Default integrations and one user integration, the ModulesIntegration is also a default integration' => [
152-
new Options([
152+
$options = new Options([
153153
'dsn' => 'http://public@example.com/sentry/1',
154154
'default_integrations' => true,
155155
'integrations' => [
@@ -158,7 +158,7 @@ public function setupOnce(): void
158158
]),
159159
[
160160
ExceptionListenerIntegration::class => new ExceptionListenerIntegration(),
161-
ErrorListenerIntegration::class => new ErrorListenerIntegration(),
161+
ErrorListenerIntegration::class => ErrorListenerIntegration::make($options),
162162
FatalErrorListenerIntegration::class => new FatalErrorListenerIntegration(),
163163
RequestIntegration::class => new RequestIntegration(),
164164
TransactionIntegration::class => new TransactionIntegration(),
@@ -192,7 +192,7 @@ public function setupOnce(): void
192192
];
193193

194194
yield 'Default integrations and a callable as user integrations' => [
195-
new Options([
195+
$options = new Options([
196196
'dsn' => 'http://public@example.com/sentry/1',
197197
'default_integrations' => true,
198198
'integrations' => static function (array $defaultIntegrations): array {
@@ -201,7 +201,7 @@ public function setupOnce(): void
201201
]),
202202
[
203203
ExceptionListenerIntegration::class => new ExceptionListenerIntegration(),
204-
ErrorListenerIntegration::class => new ErrorListenerIntegration(),
204+
ErrorListenerIntegration::class => ErrorListenerIntegration::make($options),
205205
FatalErrorListenerIntegration::class => new FatalErrorListenerIntegration(),
206206
RequestIntegration::class => new RequestIntegration(),
207207
TransactionIntegration::class => new TransactionIntegration(),

0 commit comments

Comments
 (0)