From 636b6927b98e1acdcc1c3e01c899de304b8bb161 Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Tue, 18 Mar 2025 12:19:54 +0100 Subject: [PATCH 1/8] Add excludes for exception message pattern and exception codes --- Classes/SentryClient.php | 11 ++++++++++- Configuration/Settings.yaml | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index e0e4682..cec4c09 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -54,6 +54,8 @@ class SentryClient protected float $sampleRate = 1; protected array $excludeExceptionTypes = []; + protected array $excludeExceptionMessagePatterns = []; + protected array $excludeExceptionCodes = []; protected ?StacktraceBuilder $stacktraceBuilder = null; /** @@ -99,6 +101,8 @@ public function injectSettings(array $settings): void $this->sampleRate = (float)($settings['sampleRate'] ?? 1); $this->excludeExceptionTypes = $settings['capture']['excludeExceptionTypes'] ?? []; + $this->excludeExceptionMessagePatterns = $settings['capture']['excludeExceptionMessagePatterns'] ?? []; + $this->excludeExceptionCodes = $settings['capture']['excludeExceptionCodes'] ?? []; } public function initializeObject(): void @@ -200,7 +204,12 @@ public function captureThrowable(Throwable $throwable, array $extraData = [], ar $tags['exception_code'] = (string)$throwable->getCode(); - $captureException = (!in_array(get_class($throwable), $this->excludeExceptionTypes, true)); + $isThrowableExcludedByClass = in_array(get_class($throwable), $this->excludeExceptionTypes, true); + $isThrowableExcludedByMessagePattern = array_reduce($this->excludeExceptionMessagePatterns, static function($carry, $pattern) use ($throwable) { + return $carry || preg_match($pattern, $throwable->getMessage()); + }, false); + $isThrowableExcludedByCode = in_array($throwable->getCode(), $this->excludeExceptionCodes, true); + $captureException = !$isThrowableExcludedByClass && !$isThrowableExcludedByMessagePattern && !$isThrowableExcludedByCode; if ($captureException) { $this->setTags(); $this->configureScope($extraData, $tags); diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 24b4687..bf10e18 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -6,6 +6,8 @@ Flownative: sampleRate: 1.0 capture: excludeExceptionTypes: [] + excludeExceptionMessagePatterns: [] + excludeExceptionCodes: [] Neos: Flow: From c50fae3af161f1cf1d8be9976a7a4da4f0653273 Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Tue, 18 Mar 2025 12:58:35 +0100 Subject: [PATCH 2/8] Try with more explicit condition on preg_match --- Classes/SentryClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index cec4c09..43d1b8e 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -206,7 +206,7 @@ public function captureThrowable(Throwable $throwable, array $extraData = [], ar $isThrowableExcludedByClass = in_array(get_class($throwable), $this->excludeExceptionTypes, true); $isThrowableExcludedByMessagePattern = array_reduce($this->excludeExceptionMessagePatterns, static function($carry, $pattern) use ($throwable) { - return $carry || preg_match($pattern, $throwable->getMessage()); + return $carry || preg_match($pattern, $throwable->getMessage()) === 1; }, false); $isThrowableExcludedByCode = in_array($throwable->getCode(), $this->excludeExceptionCodes, true); $captureException = !$isThrowableExcludedByClass && !$isThrowableExcludedByMessagePattern && !$isThrowableExcludedByCode; From af1aa81b03b922120b409741832a6eea6dda0f8e Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Mon, 28 Apr 2025 15:59:33 +0200 Subject: [PATCH 3/8] Inject settings in early logger --- Classes/Package.php | 7 +++++-- Classes/SentryClient.php | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Classes/Package.php b/Classes/Package.php index ab84ffd..95b8f75 100644 --- a/Classes/Package.php +++ b/Classes/Package.php @@ -3,6 +3,7 @@ namespace Flownative\Sentry; +use Neos\Flow\Configuration\ConfigurationManager; use Neos\Flow\Core\Booting\Sequence; use Neos\Flow\Core\Bootstrap; use Neos\Flow\Package\Package as BasePackage; @@ -13,11 +14,13 @@ public function boot(Bootstrap $bootstrap) { $dispatcher = $bootstrap->getSignalSlotDispatcher(); - $dispatcher->connect(Sequence::class, 'afterInvokeStep', function ($step) { + $dispatcher->connect(Sequence::class, 'afterInvokeStep', function ($step) use ($bootstrap) { if ($step->getIdentifier() === 'neos.flow:objectmanagement:runtime') { + $configurationManager = $bootstrap->getObjectManager()->get(ConfigurationManager::class); + $settings = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, $this->getPackageKey()); // instantiate client to set up Sentry and register error handler early /** @noinspection PhpExpressionResultUnusedInspection */ - new SentryClient(); + new SentryClient($settings); } }); } diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index 43d1b8e..cead854 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -82,13 +82,17 @@ class SentryClient */ protected $packageManager; - public function __construct() + public function __construct(array $settings = []) { // Try to set from environment variables – this allows for very early use. // If not set, the results will be empty strings. See injectSettings() below. $this->dsn = (string)getenv('SENTRY_DSN'); $this->environment = (string)getenv('SENTRY_ENVIRONMENT'); $this->release = (string)getenv('SENTRY_RELEASE'); + + if (!empty($settings)) { + $this->injectSettings($settings); + } } public function injectSettings(array $settings): void From eb49e356a01dcd28010e419e8c2afa59a47305cc Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Mon, 28 Apr 2025 17:12:45 +0200 Subject: [PATCH 4/8] WIP: Reduce error level and filter earlier in SDK --- Classes/SentryClient.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index cead854..077f70b 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -41,6 +41,7 @@ use Sentry\Stacktrace; use Sentry\StacktraceBuilder; use Sentry\State\Scope; +use Sentry\Tracing\Transaction; use Throwable; /** @@ -137,6 +138,27 @@ public function initializeObject(): void FLOW_PATH_ROOT . '/Packages/Libraries/neos/flow-log/' ], 'attach_stacktrace' => true, + 'error_types' => E_ERROR, + 'before_send' => function (Event $event, EventHint $hint): ?Event { + $isExcludedByMessagePattern = array_reduce($this->excludeExceptionMessagePatterns, static function($carry, $pattern) use ($event, $hint) { + return $carry || preg_match($pattern, $event->getMessage()) === 1; + }, false); + if ($isExcludedByMessagePattern) { + return null; + } + + $isThrowableExcludedByClass = $hint->exception && in_array(get_class($hint->exception), $this->excludeExceptionTypes, true); + if ($isThrowableExcludedByClass) { + return null; + } + + $isThrowableExcludedByCode = $hint->exception ?? in_array($hint->exception->getCode(), $this->excludeExceptionCodes, true); + if ($isThrowableExcludedByCode) { + return null; + } + + return $event; + }, ]); $client = SentrySdk::getCurrentHub()->getClient(); From 64bcbd66626588797b58284f01a806ac806eed9d Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Fri, 16 May 2025 21:56:22 +0200 Subject: [PATCH 5/8] Better implementation for exception type, message pattern and code filters --- Classes/SentryClient.php | 55 ++++++++++++++++++++++--------------- Configuration/Settings.yaml | 2 +- README.md | 20 ++++++++++++-- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index 077f70b..81defa2 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -83,6 +83,11 @@ class SentryClient */ protected $packageManager; + /** + * @var int + */ + protected int $errorLevel; + public function __construct(array $settings = []) { // Try to set from environment variables – this allows for very early use. @@ -108,6 +113,7 @@ public function injectSettings(array $settings): void $this->excludeExceptionTypes = $settings['capture']['excludeExceptionTypes'] ?? []; $this->excludeExceptionMessagePatterns = $settings['capture']['excludeExceptionMessagePatterns'] ?? []; $this->excludeExceptionCodes = $settings['capture']['excludeExceptionCodes'] ?? []; + $this->errorLevel = $settings['errorLevel'] ?? E_ERROR; } public function initializeObject(): void @@ -138,22 +144,10 @@ public function initializeObject(): void FLOW_PATH_ROOT . '/Packages/Libraries/neos/flow-log/' ], 'attach_stacktrace' => true, - 'error_types' => E_ERROR, - 'before_send' => function (Event $event, EventHint $hint): ?Event { - $isExcludedByMessagePattern = array_reduce($this->excludeExceptionMessagePatterns, static function($carry, $pattern) use ($event, $hint) { - return $carry || preg_match($pattern, $event->getMessage()) === 1; - }, false); - if ($isExcludedByMessagePattern) { - return null; - } - - $isThrowableExcludedByClass = $hint->exception && in_array(get_class($hint->exception), $this->excludeExceptionTypes, true); - if ($isThrowableExcludedByClass) { - return null; - } - - $isThrowableExcludedByCode = $hint->exception ?? in_array($hint->exception->getCode(), $this->excludeExceptionCodes, true); - if ($isThrowableExcludedByCode) { + 'error_types' => $this->errorLevel, + 'before_send' => function (Event $event, ?EventHint $hint): ?Event { + $hasThrowableAndShouldSkip = $hint?->exception && !$this->shouldCaptureThrowable($hint->exception); + if ($hasThrowableAndShouldSkip) { return null; } @@ -230,12 +224,7 @@ public function captureThrowable(Throwable $throwable, array $extraData = [], ar $tags['exception_code'] = (string)$throwable->getCode(); - $isThrowableExcludedByClass = in_array(get_class($throwable), $this->excludeExceptionTypes, true); - $isThrowableExcludedByMessagePattern = array_reduce($this->excludeExceptionMessagePatterns, static function($carry, $pattern) use ($throwable) { - return $carry || preg_match($pattern, $throwable->getMessage()) === 1; - }, false); - $isThrowableExcludedByCode = in_array($throwable->getCode(), $this->excludeExceptionCodes, true); - $captureException = !$isThrowableExcludedByClass && !$isThrowableExcludedByMessagePattern && !$isThrowableExcludedByCode; + $captureException = $this->shouldCaptureThrowable($throwable); if ($captureException) { $this->setTags(); $this->configureScope($extraData, $tags); @@ -379,4 +368,26 @@ private function addThrowableToEvent(Throwable $throwable, Event $event): void $event->setExceptions($exceptions); } + + private function shouldCaptureThrowable(Throwable $throwable): bool + { + $isExcludedByMessagePattern = array_reduce($this->excludeExceptionMessagePatterns, static function($carry, $pattern) use ($throwable) { + return $carry || preg_match($pattern, $throwable->getMessage()) === 1; + }, false); + if ($isExcludedByMessagePattern) { + return false; + } + + $isThrowableExcludedByClass = in_array(get_class($throwable), $this->excludeExceptionTypes, true); + if ($isThrowableExcludedByClass) { + return false; + } + + $isThrowableExcludedByCode = in_array($throwable->getCode(), $this->excludeExceptionCodes, true); + if ($isThrowableExcludedByCode) { + return false; + } + + return true; + } } diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index bf10e18..0bcbf47 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -8,7 +8,7 @@ Flownative: excludeExceptionTypes: [] excludeExceptionMessagePatterns: [] excludeExceptionCodes: [] - + errorLevel: '%E_ERROR%' Neos: Flow: log: diff --git a/README.md b/README.md index d0e0e34..ad70bcf 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,21 @@ Flownative: The default is 1 – 100% percent of all errors are sampled. +The PHP error level for automatically detected errors by the Sentry SDK can be set using + +```yaml +Flownative: + Sentry: + errorTypes: '%E_ERROR%' +``` + +The default is `E_ERROR`. See [PHP error constants](https://www.php.net/manual/en/errorfunc.constants.php) for available error levels. + +**Beware:** a low error log level can lead to your application not loading anymore and your Sentry account being flooded with error messages. + Throwables (that includes exceptions and runtime errors) are logged as -Sentry events. You may specify a list of exceptions which should not be -recorded. If such an exception is thrown, it will only be logged as a +Sentry events. You may specify a list of exceptions types, exception message regular expressions or exception codes +which should not be recorded. If such an exception is thrown, it will only be logged as a "notice". ```yaml @@ -69,6 +81,10 @@ Flownative: capture: excludeExceptionTypes: - 'Neos\Flow\Mvc\Controller\Exception\InvalidControllerException' + excludeExceptionMessagePatterns: + - '/^Warning: fopen\(.*/' + excludeExceptionCodes: + - 1391972021 ``` If an ignored exception is handled by this Sentry client, it is logged From 40f8e62e9a0633a0e9402ee27294f74d4167a332 Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Fri, 16 May 2025 21:58:22 +0200 Subject: [PATCH 6/8] CLEANUP: Remove unused import --- Classes/SentryClient.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index 81defa2..2262e58 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -41,7 +41,6 @@ use Sentry\Stacktrace; use Sentry\StacktraceBuilder; use Sentry\State\Scope; -use Sentry\Tracing\Transaction; use Throwable; /** From c52dff0fbe67b982dd383fd306a17b7ebc109545 Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Fri, 16 May 2025 21:59:56 +0200 Subject: [PATCH 7/8] CHORE: Move property declaration --- Classes/SentryClient.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index 2262e58..41f9f1b 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -53,6 +53,7 @@ class SentryClient protected string $release; protected float $sampleRate = 1; + protected int $errorLevel; protected array $excludeExceptionTypes = []; protected array $excludeExceptionMessagePatterns = []; protected array $excludeExceptionCodes = []; @@ -82,11 +83,6 @@ class SentryClient */ protected $packageManager; - /** - * @var int - */ - protected int $errorLevel; - public function __construct(array $settings = []) { // Try to set from environment variables – this allows for very early use. From 301d592e36e08ab9d71e02851f2892926b4f2375 Mon Sep 17 00:00:00 2001 From: Felix Gradinaru Date: Fri, 16 May 2025 22:00:41 +0200 Subject: [PATCH 8/8] CHORE: Add blank line --- Configuration/Settings.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 0bcbf47..e1f2a0d 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -9,6 +9,7 @@ Flownative: excludeExceptionMessagePatterns: [] excludeExceptionCodes: [] errorLevel: '%E_ERROR%' + Neos: Flow: log: