Skip to content

FEATURE: Extended Capture Excludes and Configuration for Early Logging #42

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Classes/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
});
}
Expand Down
45 changes: 43 additions & 2 deletions Classes/SentryClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ class SentryClient
protected string $release;

protected float $sampleRate = 1;
protected int $errorLevel;
protected array $excludeExceptionTypes = [];
protected array $excludeExceptionMessagePatterns = [];
protected array $excludeExceptionCodes = [];
protected ?StacktraceBuilder $stacktraceBuilder = null;

/**
Expand All @@ -80,13 +83,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
Expand All @@ -99,6 +106,9 @@ 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'] ?? [];
$this->errorLevel = $settings['errorLevel'] ?? E_ERROR;
}

public function initializeObject(): void
Expand Down Expand Up @@ -129,6 +139,15 @@ public function initializeObject(): void
FLOW_PATH_ROOT . '/Packages/Libraries/neos/flow-log/'
],
'attach_stacktrace' => true,
'error_types' => $this->errorLevel,
'before_send' => function (Event $event, ?EventHint $hint): ?Event {
$hasThrowableAndShouldSkip = $hint?->exception && !$this->shouldCaptureThrowable($hint->exception);
if ($hasThrowableAndShouldSkip) {
return null;
}

return $event;
},
]);

$client = SentrySdk::getCurrentHub()->getClient();
Expand Down Expand Up @@ -200,7 +219,7 @@ public function captureThrowable(Throwable $throwable, array $extraData = [], ar

$tags['exception_code'] = (string)$throwable->getCode();

$captureException = (!in_array(get_class($throwable), $this->excludeExceptionTypes, true));
$captureException = $this->shouldCaptureThrowable($throwable);
if ($captureException) {
$this->setTags();
$this->configureScope($extraData, $tags);
Expand Down Expand Up @@ -344,4 +363,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;
}
}
3 changes: 3 additions & 0 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Flownative:
sampleRate: 1.0
capture:
excludeExceptionTypes: []
excludeExceptionMessagePatterns: []
excludeExceptionCodes: []
errorLevel: '%E_ERROR%'

Neos:
Flow:
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down