diff --git a/Classes/Command/SentryCommandController.php b/Classes/Command/SentryCommandController.php index 6d559f3..24b0b55 100644 --- a/Classes/Command/SentryCommandController.php +++ b/Classes/Command/SentryCommandController.php @@ -14,18 +14,17 @@ */ use Flownative\Sentry\SentryClient; -use Flownative\Sentry\Test\JsonSerializableTestArgument; use Flownative\Sentry\Test\SentryClientTestException; use Flownative\Sentry\Test\StringableTestArgument; use Flownative\Sentry\Test\ThrowingClass; -use Neos\Flow\Annotations\Inject; +use Neos\Flow\Annotations as Flow; use Neos\Flow\Cli\CommandController; use Sentry\Severity; final class SentryCommandController extends CommandController { /** - * @Inject + * @Flow\Inject * @var SentryClient */ protected $sentryClient; @@ -43,6 +42,7 @@ final class SentryCommandController extends CommandController public function testCommand(): void { $this->output->outputLine('Testing Sentry setup …'); + $this->output->outputLine(); $this->output->outputLine('Using the following configuration:'); $options = $this->sentryClient->getOptions(); @@ -58,7 +58,7 @@ public function testCommand(): void 'Value' ]); - $eventId = $this->sentryClient->captureMessage( + $captureResult = $this->sentryClient->captureMessage( 'Flownative Sentry Plugin Test', Severity::debug(), [ @@ -66,7 +66,14 @@ public function testCommand(): void ] ); - $this->outputLine('An informational message was sent to Sentry Event ID: #%s', [$eventId]); + $this->outputLine(); + $this->outputLine('An informational message was sent to Sentry'); + if ($captureResult) { + $this->outputLine('Event ID: #' . $captureResult->eventId . ''); + } else { + $this->outputLine('' . $captureResult->message . ''); + } + $this->outputLine(); $this->outputLine('This command will now throw an exception for testing purposes.'); $this->outputLine(); diff --git a/Classes/Context/DefaultUserContextService.php b/Classes/Context/DefaultUserContextService.php index eeec18c..07d023f 100644 --- a/Classes/Context/DefaultUserContextService.php +++ b/Classes/Context/DefaultUserContextService.php @@ -18,10 +18,6 @@ class DefaultUserContextService implements UserContextServiceInterface { - /** - * @param Context $securityContext - * @return UserContextInterface - */ public function getUserContext(Context $securityContext): UserContextInterface { $userContext = new UserContext(); diff --git a/Classes/Context/UserContext.php b/Classes/Context/UserContext.php index 7637ffb..fbd7756 100644 --- a/Classes/Context/UserContext.php +++ b/Classes/Context/UserContext.php @@ -15,64 +15,37 @@ class UserContext implements UserContextInterface { - /** - * @var string - */ - private $id = ''; + private string $id = ''; - /** - * @var string - */ - private $username = ''; + private string $username = ''; - /** - * @var string - */ - private $email = ''; + private string $email = ''; - /** - * @return string - */ public function getId(): string { return $this->id; } - /** - * @param string $id - */ public function setId(string $id): void { $this->id = $id; } - /** - * @return string - */ public function getUsername(): string { return $this->username; } - /** - * @param string $username - */ public function setUsername(string $username): void { $this->username = $username; } - /** - * @return string - */ public function getEmail(): string { return $this->email; } - /** - * @param string $email - */ public function setEmail(string $email): void { $this->email = $email; diff --git a/Classes/Context/UserContextInterface.php b/Classes/Context/UserContextInterface.php index 17815c2..dec6ae3 100644 --- a/Classes/Context/UserContextInterface.php +++ b/Classes/Context/UserContextInterface.php @@ -15,19 +15,10 @@ interface UserContextInterface { - /** - * @return string|null - */ public function getId(): ?string; - /** - * @return string|null - */ public function getUsername(): ?string; - /** - * @return string|null - */ public function getEmail(): ?string; /** @@ -36,8 +27,6 @@ public function getEmail(): ?string; * "id", "username", "email" * * The keys must exist, but the values may be empty - * - * @return array */ public function toArray(): array; } diff --git a/Classes/Context/UserContextServiceInterface.php b/Classes/Context/UserContextServiceInterface.php index 36425b7..e521986 100644 --- a/Classes/Context/UserContextServiceInterface.php +++ b/Classes/Context/UserContextServiceInterface.php @@ -19,9 +19,6 @@ interface UserContextServiceInterface { /** * Returns ContextData to be added to the sentry entry - * - * @param Context $securityContext - * @return UserContextInterface */ public function getUserContext(Context $securityContext): UserContextInterface; } diff --git a/Classes/Exception/DebugExceptionHandler.php b/Classes/Exception/DebugExceptionHandler.php deleted file mode 100755 index a55beef..0000000 --- a/Classes/Exception/DebugExceptionHandler.php +++ /dev/null @@ -1,59 +0,0 @@ -renderingOptions = $this->resolveCustomRenderingOptions($exception); - - $exceptionWasLogged = false; - if ($this->throwableStorage instanceof ThrowableStorageInterface && isset($this->renderingOptions['logException']) && $this->renderingOptions['logException']) { - $message = $this->throwableStorage->logThrowable($exception); - $this->logger->critical($message); - $exceptionWasLogged = true; - } - - try { - if ($sentryClient = self::getSentryClient()) { - $sentryClient->captureThrowable($exception); - } - } catch (\Throwable $e) { - } - - if (PHP_SAPI === 'cli') { - # Doesn't return: - $this->echoExceptionCli($exception, $exceptionWasLogged); - } else { - $this->echoExceptionWeb($exception); - } - } -} diff --git a/Classes/Exception/ProductionExceptionHandler.php b/Classes/Exception/ProductionExceptionHandler.php deleted file mode 100755 index 8feabf0..0000000 --- a/Classes/Exception/ProductionExceptionHandler.php +++ /dev/null @@ -1,59 +0,0 @@ -renderingOptions = $this->resolveCustomRenderingOptions($exception); - - $exceptionWasLogged = false; - if ($this->throwableStorage instanceof ThrowableStorageInterface && isset($this->renderingOptions['logException']) && $this->renderingOptions['logException']) { - $message = $this->throwableStorage->logThrowable($exception); - $this->logger->critical($message); - $exceptionWasLogged = true; - } - - try { - if ($sentryClient = self::getSentryClient()) { - $sentryClient->captureThrowable($exception); - } - } catch (\Throwable $e) { - } - - if (PHP_SAPI === 'cli') { - # Doesn't return: - $this->echoExceptionCli($exception, $exceptionWasLogged); - } else { - $this->echoExceptionWeb($exception); - } - } -} diff --git a/Classes/Log/CaptureResult.php b/Classes/Log/CaptureResult.php new file mode 100644 index 0000000..d44fb89 --- /dev/null +++ b/Classes/Log/CaptureResult.php @@ -0,0 +1,12 @@ +capturingMessage) { return; @@ -49,29 +45,24 @@ public function append(string $message, int $severity = LOG_INFO, $additionalDat $sentryClient = self::getSentryClient(); if ($severity <= LOG_NOTICE && $sentryClient) { - switch ($severity) { - case LOG_WARNING: - $sentrySeverity = Severity::warning(); - break; - case LOG_ERR: - $sentrySeverity = Severity::error(); - break; - case LOG_CRIT: - case LOG_ALERT: - case LOG_EMERG: - $sentrySeverity = Severity::fatal(); - break; - default: - $sentrySeverity = Severity::info(); + $sentrySeverity = match ($severity) { + LOG_WARNING => Severity::warning(), + LOG_ERR => Severity::error(), + LOG_CRIT, LOG_ALERT, LOG_EMERG => Severity::fatal(), + default => Severity::info(), + }; + $captureResult = $sentryClient->captureMessage($message, $sentrySeverity, ['Additional Data' => $additionalData]); + if ($captureResult) { + $message .= ' (Sentry: #' . $captureResult->eventId . ')'; + } else { + $message .= ' (Sentry: ' . $captureResult->message . ')'; } - - $sentryClient->captureMessage($message, $sentrySeverity, ['Additional Data' => $additionalData]); } - parent::append($message, $severity, $additionalData, $packageKey, $className, $methodName); } catch (\Throwable $throwable) { - echo sprintf('SentryFileBackend: %s (%s)', $throwable->getMessage(), $throwable->getCode()); + parent::append(sprintf('%s (%s)', $throwable->getMessage(), $throwable->getCode()), LOG_ERR, 'Flownative.Sentry', __CLASS__, __METHOD__); } finally { + parent::append($message, $severity, $additionalData, $packageKey, $className, $methodName); $this->capturingMessage = false; } } diff --git a/Classes/Log/SentryStorage.php b/Classes/Log/SentryStorage.php new file mode 100644 index 0000000..df7960c --- /dev/null +++ b/Classes/Log/SentryStorage.php @@ -0,0 +1,89 @@ + in of : - See also: + * + * @param \Throwable $throwable + * @param array $additionalData + * @return string Informational message about the stored throwable + */ + public function logThrowable(\Throwable $throwable, array $additionalData = []): string + { + $message = $this->getErrorLogMessage($throwable); + try { + if ($sentryClient = self::getSentryClient()) { + $captureResult = $sentryClient->captureThrowable($throwable, $additionalData); + if ($captureResult->suceess) { + $message .= ' (Sentry: #' . $captureResult->eventId . ')'; + } else { + $message .= ' (Sentry: ' . $captureResult->message . ')'; + } + } + } catch (\Throwable $e) { + $message .= ' – Error capturing message: ' . $this->getErrorLogMessage($e); + } + + return $message; + } + + protected function getErrorLogMessage(\Throwable $error): string + { + $errorCodeNumber = ($error->getCode() > 0) ? ' #' . $error->getCode() : ''; + $backTrace = $error->getTrace(); + $line = isset($backTrace[0]['line']) ? ' in line ' . $backTrace[0]['line'] . ' of ' . $backTrace[0]['file'] : ''; + + return 'Exception' . $errorCodeNumber . $line . ': ' . $error->getMessage(); + } +} diff --git a/Classes/SentryClient.php b/Classes/SentryClient.php index 3dad613..e7801c7 100644 --- a/Classes/SentryClient.php +++ b/Classes/SentryClient.php @@ -16,6 +16,7 @@ use Flownative\Sentry\Context\UserContext; use Flownative\Sentry\Context\UserContextServiceInterface; use Flownative\Sentry\Context\WithExtraDataInterface; +use Flownative\Sentry\Log\CaptureResult; use GuzzleHttp\Psr7\ServerRequest; use Jenssegers\Agent\Agent; use Neos\Flow\Annotations as Flow; @@ -182,12 +183,19 @@ public function getOptions(): Options return new Options(); } - public function captureThrowable(Throwable $throwable, array $extraData = [], array $tags = []): void + public function captureThrowable(Throwable $throwable, array $extraData = [], array $tags = []): CaptureResult { if (empty($this->dsn)) { - return; + return new CaptureResult( + false, + 'Failed capturing message, because no Sentry DSN was set. Please check your settings.', + '' + ); } + $message = ''; + $sentryEventId = ''; + if ($throwable instanceof WithReferenceCodeInterface) { $extraData['Reference Code'] = $throwable->getReferenceCode(); } @@ -215,33 +223,23 @@ public function captureThrowable(Throwable $throwable, array $extraData = [], ar $this->addThrowableToEvent($throwable, $event); $sentryEventId = SentrySdk::getCurrentHub()->captureEvent($event); } else { - $sentryEventId = 'ignored'; - } - if ($this->logger) { - $this->logger->log( - ($captureException ? LogLevel::CRITICAL : LogLevel::NOTICE), - sprintf( - 'Exception #%s: %s (Ref: %s | Sentry: %s)', - $throwable->getCode(), - $throwable->getMessage(), - ($throwable instanceof WithReferenceCodeInterface ? $throwable->getReferenceCode() : '-'), - $sentryEventId - ) - ); + $message = 'ignored'; } + return new CaptureResult( + true, + $message, + (string)$sentryEventId + ); } - public function captureMessage(string $message, Severity $severity, array $extraData = [], array $tags = []): ?EventId + public function captureMessage(string $message, Severity $severity, array $extraData = [], array $tags = []): CaptureResult { if (empty($this->dsn)) { - if ($this->logger) { - $this->logger->warning('Sentry: Failed capturing message, because no Sentry DSN was set. Please check your settings.'); - } - return null; - } - - if (preg_match('/Sentry: [0-9a-f]{32}/', $message) === 1) { - return null; + return new CaptureResult( + false, + 'Failed capturing message, because no Sentry DSN was set. Please check your settings.', + '' + ); } $this->configureScope($extraData, $tags); @@ -250,18 +248,11 @@ public function captureMessage(string $message, Severity $severity, array $extra ]); $sentryEventId = \Sentry\captureMessage($message, $severity, $eventHint); - if ($this->logger) { - $this->logger->log( - (string)$severity, - sprintf( - '%s (Sentry: %s)', - $message, - $sentryEventId - ) - ); - } - - return $sentryEventId; + return new CaptureResult( + true, + '', + (string)$sentryEventId + ); } private function configureScope(array $extraData, array $tags): void diff --git a/Classes/SentryClientTrait.php b/Classes/SentryClientTrait.php index f1481fc..37289af 100644 --- a/Classes/SentryClientTrait.php +++ b/Classes/SentryClientTrait.php @@ -19,9 +19,6 @@ trait SentryClientTrait { - /** - * @return SentryClient|null - */ protected static function getSentryClient(): ?SentryClient { if (!Bootstrap::$staticObjectManager instanceof ObjectManagerInterface || Bootstrap::$staticObjectManager instanceof CompileTimeObjectManager) { diff --git a/Configuration/Development/Settings.yaml b/Configuration/Development/Settings.yaml deleted file mode 100644 index 07db229..0000000 --- a/Configuration/Development/Settings.yaml +++ /dev/null @@ -1,5 +0,0 @@ -Neos: - Flow: - error: - exceptionHandler: - className: Flownative\Sentry\Exception\DebugExceptionHandler diff --git a/Configuration/Production/Settings.yaml b/Configuration/Production/Settings.yaml deleted file mode 100644 index 00ecc2c..0000000 --- a/Configuration/Production/Settings.yaml +++ /dev/null @@ -1,5 +0,0 @@ -Neos: - Flow: - error: - exceptionHandler: - className: Flownative\Sentry\Exception\ProductionExceptionHandler diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 99ec675..63abb36 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -10,5 +10,12 @@ Flownative: Neos: Flow: log: - systemLogger: - backend: Flownative\Sentry\Log\SentryFileBackend + psr3: + Neos\Flow\Log\PsrLoggerFactory: + systemLogger: + default: + class: 'Flownative\Sentry\Log\SentryFileBackend' + throwables: + storageClass: 'Flownative\Sentry\Log\SentryStorage' + optionsByImplementation: + 'Flownative\Sentry\Log\SentryStorage': [] diff --git a/README.md b/README.md index 56a1f21..f8b0402 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Packagist](https://img.shields.io/packagist/v/flownative/sentry.svg)](https://packagist.org/packages/flownative/sentry) [![Maintenance level: Love](https://img.shields.io/badge/maintenance-%E2%99%A1%E2%99%A1%E2%99%A1-ff69b4.svg)](https://www.flownative.com/en/products/open-source.html) -# Sentry integration for Flow 6.x, 7.x and Flow 8.x +# Sentry integration for Flow 8.x and 9.x This [Flow](https://flow.neos.io) package allows you to automate reporting of errors to [Sentry](https://www.sentry.io) @@ -121,6 +121,4 @@ Test "previous" exception thrown by the SentryCommandController Line: 78 Open Data/Logs/Exceptions/2021030308325919ecbf.txt for a full stack trace. - - -```` +``` diff --git a/composer.json b/composer.json index 69d3bbe..2527c06 100644 --- a/composer.json +++ b/composer.json @@ -15,9 +15,9 @@ ], "require": { "ext-json": "*", - "php": "^7.4 || ^8.0", - "neos/flow": "^6.3 || ^7.0 || ^8.0 || ^9.0 || @dev", - "sentry/sdk": "^3.0", + "php": "^8.1", + "neos/flow": "^8.0 || ^9.0 || @dev", + "sentry/sentry": "^4.0", "jenssegers/agent": "^2.6" }, "autoload": {