Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
.idea
composer.lock
/vendor
vendor
143 changes: 79 additions & 64 deletions Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace JustBetter\Sentry\Helper;

use ErrorException;
use InvalidArgumentException;
use JustBetter\Sentry\Block\SentryScript;
use Magento\Framework\App\Area;
use Magento\Framework\App\Config\ScopeConfigInterface;
Expand All @@ -25,6 +24,35 @@ class Data extends AbstractHelper
public const XML_PATH_SRS = 'sentry/general/';
public const XML_PATH_SRS_ISSUE_GROUPING = 'sentry/issue_grouping/';

public const NATIVE_SENTRY_CONFIG_KEYS = [
// https://docs.sentry.io/platforms/php/configuration/options/#core-options
'dsn' => ['type' => 'string'],
'environment' => ['type' => 'string'],
'max_breadcrumbs' => ['type' => 'int'],
'attach_stacktrace' => ['type' => 'bool'],
'send_default_pii' => ['type' => 'bool'],
'server_name' => ['type' => 'string'],
'in_app_include' => ['type' => 'array'],
'in_app_exclude' => ['type' => 'array'],
'max_request_body_size' => ['type' => 'string'],
'max_value_length' => ['type' => 'int'],
// https://docs.sentry.io/platforms/php/configuration/options/#error-monitoring-options
'sample_rate' => ['type' => 'float'],
'ignore_exceptions' => ['type' => 'array'],
'error_types' => ['type' => 'int'],
'context_lines' => ['type' => 'int'],
// https://docs.sentry.io/platforms/php/configuration/options/#tracing-options
'traces_sample_rate' => ['type' => 'float'],
'ignore_transactions' => ['type' => 'array'],
'trace_propagation_targets' => ['type' => 'array'],
// https://docs.sentry.io/platforms/php/profiling/#enabling-profiling
'profiles_sample_rate' => ['type' => 'float'],
// https://docs.sentry.io/platforms/php/configuration/options/#transport-options
'http_proxy' => ['type' => 'string'],
'http_connect_timeout' => ['type' => 'int'],
'http_timeout' => ['type' => 'int'],
];

/**
* @var ScopeConfigInterface
*/
Expand All @@ -39,22 +67,19 @@ class Data extends AbstractHelper
* @var array
*/
protected $configKeys = [
'dsn',
'logrocket_key',
'log_level',
'errorexception_reporting',
'ignore_exceptions',
'mage_mode_development',
'environment',
'js_sdk_version',
'tracing_enabled',
'tracing_sample_rate',
'performance_tracking_enabled',
'performance_tracking_excluded_areas',
'profiles_sample_rate',
'ignore_js_errors',
'disable_default_integrations',
'clean_stacktrace',
...self::NATIVE_SENTRY_CONFIG_KEYS,
'logrocket_key' => ['type' => 'array'],
'log_level' => ['type' => 'int'],
'errorexception_reporting' => ['type' => 'int'], /* @deprecated by @see: error_types https://docs.sentry.io/platforms/php/configuration/options/#error_types */
'mage_mode_development' => ['type' => 'bool'],
'js_sdk_version' => ['type' => 'string'],
'tracing_enabled' => ['type' => 'bool'],
'tracing_sample_rate' => ['type' => 'float'], /* @deprecated by @see: traces_sample_rate https://docs.sentry.io/platforms/php/configuration/options/#error_types */
'performance_tracking_enabled' => ['type' => 'bool'],
'performance_tracking_excluded_areas' => ['type' => 'array'],
'ignore_js_errors' => ['type' => 'array'],
'disable_default_integrations' => ['type' => 'array'],
'clean_stacktrace' => ['type' => 'bool'],
];

/**
Expand Down Expand Up @@ -91,16 +116,6 @@ public function getDSN()
return $this->collectModuleConfig()['dsn'];
}

/**
* Get sample rate for php profiling.
*
* @return float
*/
public function getPhpProfileSampleRate(): float
{
return (float) ($this->collectModuleConfig()['profiles_sample_rate'] ?? 0);
}

/**
* Whether tracing is enabled.
*/
Expand All @@ -114,7 +129,7 @@ public function isTracingEnabled(): bool
*/
public function getTracingSampleRate(): float
{
return (float) ($this->collectModuleConfig()['tracing_sample_rate'] ?? 0.2);
return (float) ($this->collectModuleConfig()['traces_sample_rate'] ?? $this->collectModuleConfig()['tracing_sample_rate'] ?? 0.2);
}

/**
Expand All @@ -132,25 +147,7 @@ public function getDisabledDefaultIntegrations(): array
*/
public function getIgnoreJsErrors()
{
$list = $this->collectModuleConfig()['ignore_js_errors'];

if ($list === null) {
return null;
}

try {
$config = $this->collectModuleConfig();
$list = is_array($config['ignore_js_errors'])
? $config['ignore_js_errors']
: $this->serializer->unserialize($config['ignore_js_errors']);
} catch (InvalidArgumentException $e) {
throw new RuntimeException(
__('Sentry configuration error: `ignore_js_errors` has to be an array or `null`. Given type: %s', gettype($list)), // phpcs:ignore
$e
);
}

return $list;
return $this->collectModuleConfig()['ignore_js_errors'];
}

/**
Expand Down Expand Up @@ -229,13 +226,16 @@ public function collectModuleConfig(): array
$this->config[$storeId]['enabled'] = $this->scopeConfig->getValue('sentry/environment/enabled', ScopeInterface::SCOPE_STORE)
?? $this->deploymentConfig->get('sentry') !== null;
} catch (TableNotFoundException|FileSystemException|RuntimeException $e) {
$this->config[$storeId]['enabled'] = null;
$this->config[$storeId]['enabled'] = $this->deploymentConfig->get('sentry') !== null;
}

foreach ($this->configKeys as $value) {
foreach ($this->configKeys as $value => $config) {
try {
$this->config[$storeId][$value] = $this->scopeConfig->getValue('sentry/environment/'.$value, ScopeInterface::SCOPE_STORE)
?? $this->deploymentConfig->get('sentry/'.$value);
$this->config[$storeId][$value] = $this->processConfigValue(
$this->scopeConfig->getValue('sentry/environment/'.$value, ScopeInterface::SCOPE_STORE)
?? $this->deploymentConfig->get('sentry/'.$value),
$config
);
} catch (TableNotFoundException|FileSystemException|RuntimeException $e) {
$this->config[$storeId][$value] = null;
}
Expand All @@ -244,6 +244,30 @@ public function collectModuleConfig(): array
return $this->config[$storeId];
}

/**
* Parse the config value to the type defined in the config.
*
* @param mixed $value
* @param array $config
*
* @return mixed
*/
public function processConfigValue(mixed $value, array $config): mixed
{
if ($value === null) {
return null;
}

return match ($config['type']) {
'array' => is_array($value) ? $value : $this->serializer->unserialize($value),
'int' => (int) $value,
'float' => (float) $value,
'bool' => (bool) $value,
'string' => (string) $value,
default => $value,
};
}

/**
* Whether Sentry is active.
*
Expand Down Expand Up @@ -510,9 +534,9 @@ public function stripStoreCode(): bool
*
* @return int
*/
public function getErrorExceptionReporting(): int
public function getErrorTypes(): int
{
return (int) ($this->collectModuleConfig()['errorexception_reporting'] ?? error_reporting());
return (int) ($this->collectModuleConfig()['error_types'] ?? $this->collectModuleConfig()['errorexception_reporting'] ?? error_reporting());
}

/**
Expand All @@ -522,16 +546,7 @@ public function getErrorExceptionReporting(): int
*/
public function getIgnoreExceptions(): array
{
$config = $this->collectModuleConfig();
if (is_array($config['ignore_exceptions'])) {
return $config['ignore_exceptions'];
}

try {
return $this->serializer->unserialize($config['ignore_exceptions']);
} catch (InvalidArgumentException $e) {
return [];
}
return $this->collectModuleConfig()['ignore_exceptions'] ?? [];
}

/**
Expand All @@ -543,7 +558,7 @@ public function getIgnoreExceptions(): array
*/
public function shouldCaptureException(Throwable $ex): bool
{
if ($ex instanceof ErrorException && !($ex->getSeverity() & $this->getErrorExceptionReporting())) {
if ($ex instanceof ErrorException && !($ex->getSeverity() & $this->getErrorTypes())) {
return false;
}

Expand Down
76 changes: 58 additions & 18 deletions Plugin/GlobalExceptionCatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,32 @@ public function aroundLaunch(AppInterface $subject, callable $proceed)
return $proceed();
}

$config = $this->prepareConfig();

$this->sentryInteraction->initialize(array_filter($config->getData()));
$this->sentryPerformance->startTransaction($subject);

try {
return $response = $proceed();
} catch (Throwable $exception) {
$this->sentryInteraction->captureException($exception);

throw $exception;
} finally {
$this->sentryPerformance->finishTransaction($response ?? 500);
}
}

/**
* Prepare all the config passed to sentry.
*
* @return DataObject
*/
public function prepareConfig(): DataObject
{
/** @var DataObject $config */
$config = $this->dataObjectFactory->create();
$config->setData(array_intersect_key($this->sentryHelper->collectModuleConfig(), SentryHelper::NATIVE_SENTRY_CONFIG_KEYS));

$config->setDsn($this->sentryHelper->getDSN());
if ($release = $this->releaseIdentifier->getReleaseId()) {
Expand All @@ -63,6 +87,36 @@ public function aroundLaunch(AppInterface $subject, callable $proceed)
$config->setEnvironment($environment);
}

$config->setBeforeBreadcrumb(function (\Sentry\Breadcrumb $breadcrumb): ?\Sentry\Breadcrumb {
$data = $this->dataObjectFactory->create();
$data->setBreadcrumb($breadcrumb);
$this->eventManager->dispatch('sentry_before_breadcrumb', [
'sentry_breadcrumb' => $data,
]);

return $data->getBreadcrumb();
});

$config->setBeforeSendTransaction(function (\Sentry\Event $transaction): ?\Sentry\Event {
$data = $this->dataObjectFactory->create();
$data->setTransaction($transaction);
$this->eventManager->dispatch('sentry_before_send_transaction', [
'sentry_transaction' => $data,
]);

return $data->getTransaction();
});

$config->setBeforeSendCheckIn(function (\Sentry\Event $checkIn): ?\Sentry\Event {
$data = $this->dataObjectFactory->create();
$data->setCheckIn($checkIn);
$this->eventManager->dispatch('sentry_before_send_check_in', [
'sentry_check_in' => $data,
]);

return $data->getCheckIn();
});

$config->setBeforeSend(function (\Sentry\Event $event, ?\Sentry\EventHint $hint): ?\Sentry\Event {
$data = $this->dataObjectFactory->create();
$data->setEvent($event);
Expand All @@ -80,32 +134,18 @@ public function aroundLaunch(AppInterface $subject, callable $proceed)
static fn (IntegrationInterface $integration) => !in_array(get_class($integration), $disabledDefaultIntegrations)
));

$config->setIgnoreExceptions($this->sentryHelper->getIgnoreExceptions());
$config->setErrorTypes($this->sentryHelper->getErrorExceptionReporting());
$config->setErrorTypes($this->sentryHelper->getErrorTypes());

if ($this->sentryHelper->isPerformanceTrackingEnabled()) {
$config->setTracesSampleRate($this->sentryHelper->getTracingSampleRate());
}

if ($rate = $this->sentryHelper->getPhpProfileSampleRate()) {
$config->setData('profiles_sample_rate', $rate);
} else {
$config->unsetTracesSampleRate(null);
}

$this->eventManager->dispatch('sentry_before_init', [
'config' => $config,
]);

$this->sentryInteraction->initialize($config->getData());
$this->sentryPerformance->startTransaction($subject);

try {
return $response = $proceed();
} catch (Throwable $exception) {
$this->sentryInteraction->captureException($exception);

throw $exception;
} finally {
$this->sentryPerformance->finishTransaction($response ?? 500);
}
return $config;
}
}
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ This module uses the [Magento Deployment Configuration](https://devdocs.magento.
'logrocket_key' => 'example/example',
'environment' => null,
'log_level' => \Monolog\Logger::WARNING,
'errorexception_reporting' => E_ALL,
'error_types' => E_ALL,
'ignore_exceptions' => [],
'mage_mode_development' => false,
'js_sdk_version' => \JustBetter\Sentry\Block\SentryScript::CURRENT_VERSION,
'tracing_enabled' => true,
'tracing_sample_rate' => 0.5,
'traces_sample_rate' => 0.5,
'disable_default_integrations' => [
\Sentry\Integration\ModulesIntegration::class,
]
Expand All @@ -64,13 +64,13 @@ Next to that there are some configuration options under Stores > Configuration >
| `dsn` | — | The DSN you got from Sentry for your project. You can find the DSN in the project settings under "Client Key (DSN)" |
| `environment` | — | Specify the environment under which the deployed version is running. Common values: production, staging, development. Helps differentiate errors between environments. |
| `log_level` | `\Monolog\Logger::WARNING` | Specify from which logging level on Sentry should get the messages. |
| `errorexception_reporting` | `E_ALL` | If the Exception is an instance of [ErrorException](https://www.php.net/manual/en/class.errorexception.php), send the error to Sentry if it matches the error reporting. Uses the same syntax as [Error Reporting](https://www.php.net/manual/en/function.error-reporting.php), e.g., `E_ERROR` | E_WARNING`. |
| `error_types` | `E_ALL` | If the Exception is an instance of [ErrorException](https://www.php.net/manual/en/class.errorexception.php), send the error to Sentry if it matches the error reporting. Uses the same syntax as [Error Reporting](https://www.php.net/manual/en/function.error-reporting.php), e.g., `E_ERROR` | E_WARNING`. |
| `ignore_exceptions` | `[]` | If the class being thrown matches any in this list, do not send it to Sentry, e.g., `[\Magento\Framework\Exception\NoSuchEntityException::class]` |
| `clean_stacktrace` | `true` | Whether unnecessary files (like Interceptor.php, Proxy.php, and Factory.php) should be removed from the stacktrace. (They will not be removed if they threw the error.) |
| `mage_mode_development` | `false` | If set to true, you will receive issues in Sentry even if Magento is running in develop mode. |
| `js_sdk_version` | `\JustBetter\Sentry\Block\SentryScript::CURRENT_VERSION` | If set, loads the explicit version of the JavaScript SDK of Sentry. |
| `tracing_enabled` | `false` | If set to true, tracing is enabled (bundle file is loaded automatically). |
| `tracing_sample_rate` | `0.2` | If tracing is enabled, set the sample rate. |
| `traces_sample_rate` | `0.2` | If tracing is enabled, set the sample rate. |
| `performance_tracking_enabled` | `false` | if performance tracking is enabled, a performance report got generated for the request. |
| `performance_tracking_excluded_areas` | `['adminhtml', 'crontab']` | if `performance_tracking_enabled` is enabled, we recommend to exclude the `adminhtml` & `crontab` area. |
| `profiles_sample_rate` | `0` (disabled) | if this option is larger than 0 (zero), the module will create a profile of the request. Please note that you have to install [Excimer](https://www.mediawiki.org/wiki/Excimer) on your server to use profiling. [Sentry documentation](https://docs.sentry.io/platforms/php/profiling/). You have to enable tracing too. |
Expand All @@ -88,9 +88,9 @@ using the "Variables" in Adobe Commerce using the following variables:
| `CONFIG__SENTRY__ENVIRONMENT__LOGROCKET_KEY` | string |
| `CONFIG__SENTRY__ENVIRONMENT__ENVIRONMENT` | string |
| `CONFIG__SENTRY__ENVIRONMENT__LOG_LEVEL` | integer |
| `CONFIG__SENTRY__ENVIRONMENT__ERROREXCEPTION_REPORTING` | integer |
| `CONFIG__SENTRY__ENVIRONMENT__ERROR_TYPES` | integer |
| `CONFIG__SENTRY__ENVIRONMENT__IGNORE_EXCEPTIONS` | JSON array of classes |
| `CONFIG__SENTRY__ENVIRONMENT__CLEAN_STACKTRACE` | boolean |
| `CONFIG__SENTRY__ENVIRONMENT__CLEAN_STACKTRACE` | boolean |
| `CONFIG__SENTRY__ENVIRONMENT__MAGE_MODE_DEVELOPMENT` | string |
| `CONFIG__SENTRY__ENVIRONMENT__JS_SDK_VERSION` | string |
| `CONFIG__SENTRY__ENVIRONMENT__TRACING_ENABLED` | boolean |
Expand Down Expand Up @@ -122,6 +122,12 @@ public function execute(\Magento\Framework\Event\Observer $observer)

Example: https://github.com/justbetter/magento2-sentry-filter-events

This same thing is the case for
| sentry_before_send | https://docs.sentry.io/platforms/php/configuration/options/#before_send |
| sentry_before_send_transaction | https://docs.sentry.io/platforms/php/configuration/options/#before_send_transaction |
| sentry_before_send_check_in | https://docs.sentry.io/platforms/php/configuration/options/#before_send_check_in |
| sentry_before_breadcrumb | https://docs.sentry.io/platforms/php/configuration/options/#before_breadcrumb |

## Compatibility
The module is tested on Magento version 2.4.x with sentry sdk version 3.x. feel free to fork this project or make a pull request.

Expand Down
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"Performance"
],
"type": "magento2-module",
"version": "4.1.0",
"license": "MIT",
"require": {
"php": ">=8.0",
Expand Down
Loading