From 8f6426792b90dcb80366590d59a13326335118c3 Mon Sep 17 00:00:00 2001 From: James Titcumb <james@asgrim.com> Date: Wed, 23 Mar 2022 12:03:43 +0000 Subject: [PATCH] Prevent the same exception from being sent to Scout multiple times --- src/Errors/ScoutErrorHandling.php | 13 ++++++++++++- tests/Unit/Errors/ScoutErrorHandlingTest.php | 20 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Errors/ScoutErrorHandling.php b/src/Errors/ScoutErrorHandling.php index ff68310d..032886a2 100644 --- a/src/Errors/ScoutErrorHandling.php +++ b/src/Errors/ScoutErrorHandling.php @@ -18,6 +18,7 @@ use function is_array; use function register_shutdown_function; use function set_exception_handler; +use function spl_object_hash; use const E_COMPILE_ERROR; use const E_CORE_ERROR; @@ -36,6 +37,8 @@ final class ScoutErrorHandling implements ErrorHandling private $reportingClient; /** @var Config */ private $config; + /** @var list<string> */ + private $objectHashesOfHandledExceptions = []; /** @var list<ErrorEvent> */ private $errorEvents = []; /** @var callable|null */ @@ -102,7 +105,15 @@ public function recordThrowable(Throwable $throwable): void return; } - $this->errorEvents[] = ErrorEvent::fromThrowable($this->request, $throwable); + $thisThrowableObjectHash = spl_object_hash($throwable); + + // Storing the object hashes & checking means we don't send exactly the same exception twice in one request + if (! in_array($thisThrowableObjectHash, $this->objectHashesOfHandledExceptions, true)) { + $this->objectHashesOfHandledExceptions[] = $thisThrowableObjectHash; + + $this->errorEvents[] = ErrorEvent::fromThrowable($this->request, $throwable); + } + $this->sendCollectedErrors(); } diff --git a/tests/Unit/Errors/ScoutErrorHandlingTest.php b/tests/Unit/Errors/ScoutErrorHandlingTest.php index 686f61af..4b993ee5 100644 --- a/tests/Unit/Errors/ScoutErrorHandlingTest.php +++ b/tests/Unit/Errors/ScoutErrorHandlingTest.php @@ -175,11 +175,27 @@ public function testExceptionIsNotRecordedWhenDisabled(): void $this->reportingClient ->expects(self::never()) - ->method('sendErrorToScout') - ->with(self::isInstanceOf(ErrorEvent::class)); + ->method('sendErrorToScout'); $handling->recordThrowable(new RuntimeException()); $handling->sendCollectedErrors(); } + + public function testExactlyTheSameExceptionIsNotSentTwice(): void + { + $handling = $this->errorHandlingFromConfig(Config::fromArray([Config\ConfigKey::ERRORS_ENABLED => true])); + + $this->reportingClient + ->expects(self::once()) + ->method('sendErrorToScout') + ->with(self::containsOnlyInstancesOf(ErrorEvent::class)); + + $exceptionInstance = new RuntimeException(); + + $handling->recordThrowable($exceptionInstance); + $handling->recordThrowable($exceptionInstance); + + $handling->sendCollectedErrors(); + } }