From d66f03d538e3f1c38cd04cbf60ccbb5ab404600a Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 3 Nov 2025 20:08:31 +0100 Subject: [PATCH] Add support for sentry. --- .env.example | 8 + app/Api/V1/Requests/AggregateFormRequest.php | 3 +- app/Exceptions/Handler.php | 39 +-- composer.json | 1 + composer.lock | 239 ++++++++++++++++++- config/firefly.php | 63 ++--- config/sentry.php | 126 ++++++++++ 7 files changed, 430 insertions(+), 49 deletions(-) create mode 100644 config/sentry.php diff --git a/.env.example b/.env.example index e6737be1e7..6810317fdf 100644 --- a/.env.example +++ b/.env.example @@ -275,6 +275,14 @@ DISABLE_CSP_HEADER=false TRACKER_SITE_ID= TRACKER_URL= +# +# You can automatically submit errors to the Firefly III developer using sentry.io +# +# This is entirely optional of course. If you run into errors, I will gladly accept GitHub +# issues or a forwared email message. +# +TRACK_ERRORS=false + # # Firefly III supports webhooks. These are security sensitive and must be enabled manually first. # diff --git a/app/Api/V1/Requests/AggregateFormRequest.php b/app/Api/V1/Requests/AggregateFormRequest.php index eda707b798..8ac9d0949b 100644 --- a/app/Api/V1/Requests/AggregateFormRequest.php +++ b/app/Api/V1/Requests/AggregateFormRequest.php @@ -37,7 +37,7 @@ abstract class AggregateFormRequest extends ApiRequest */ protected array $requests = []; - /** @return class-string[] */ + /** @return array */ abstract protected function getRequests(): array; public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): void @@ -47,6 +47,7 @@ abstract class AggregateFormRequest extends ApiRequest // instantiate all subrequests and share current requests' bags with them Log::debug('Initializing AggregateFormRequest.'); + /** @var array|string $config */ foreach ($this->getRequests() as $config) { $requestClass = is_array($config) ? array_shift($config) : $config; diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index bc010ba8fa..3b4eb49151 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -42,6 +42,7 @@ use Illuminate\Validation\ValidationException as LaravelValidationException; use Laravel\Passport\Exceptions\OAuthServerException as LaravelOAuthException; use League\OAuth2\Server\Exception\OAuthServerException; use Override; +use Sentry\Laravel\Integration; use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -49,11 +50,11 @@ use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Throwable; - use function Safe\json_encode; use function Safe\parse_url; // temp + /** * Class Handler */ @@ -65,7 +66,7 @@ class Handler extends ExceptionHandler * @var array> */ protected $dontReport - = [ + = [ AuthenticationException::class, LaravelValidationException::class, NotFoundHttpException::class, @@ -81,7 +82,14 @@ class Handler extends ExceptionHandler * Register the exception handling callbacks for the application. */ #[Override] - public function register(): void {} + public function register(): void + { + if (true === config('firefly.track_errors')) { + $this->reportable(function (Throwable $e) { + Integration::captureUnhandledException($e); + }); + } + } /** * Render an exception into an HTTP response. It's complex but lucky for us, we never use it because @@ -160,7 +168,7 @@ class Handler extends ExceptionHandler $errorCode = 500; $errorCode = $e instanceof MethodNotAllowedHttpException ? 405 : $errorCode; - $isDebug = (bool) config('app.debug', false); + $isDebug = (bool)config('app.debug', false); if ($isDebug) { Log::debug(sprintf('Return JSON %s with debug.', $e::class)); @@ -219,13 +227,13 @@ class Handler extends ExceptionHandler public function report(Throwable $e): void { self::$lastError = $e; - $doMailError = (bool) config('firefly.send_error_message'); + $doMailError = (bool)config('firefly.send_error_message'); if ($this->shouldntReportLocal($e) || !$doMailError) { parent::report($e); return; } - $userData = [ + $userData = [ 'id' => 0, 'email' => 'unknown@example.com', ]; @@ -234,9 +242,9 @@ class Handler extends ExceptionHandler $userData['email'] = auth()->user()->email; } - $headers = request()->headers->all(); + $headers = request()->headers->all(); - $data = [ + $data = [ 'class' => $e::class, 'errorMessage' => $e->getMessage(), 'time' => Carbon::now()->format('r'), @@ -254,8 +262,8 @@ class Handler extends ExceptionHandler ]; // create job that will mail. - $ipAddress = request()->ip() ?? '0.0.0.0'; - $job = new MailError($userData, (string) config('firefly.site_owner'), $ipAddress, $data); + $ipAddress = request()->ip() ?? '0.0.0.0'; + $job = new MailError($userData, (string)config('firefly.site_owner'), $ipAddress, $data); dispatch($job); parent::report($e); @@ -264,9 +272,9 @@ class Handler extends ExceptionHandler private function shouldntReportLocal(Throwable $e): bool { return null !== Arr::first( - $this->dontReport, - static fn ($type) => $e instanceof $type - ); + $this->dontReport, + static fn($type) => $e instanceof $type + ); } /** @@ -275,7 +283,7 @@ class Handler extends ExceptionHandler * @param Request $request */ #[Override] - protected function invalid($request, LaravelValidationException $exception): \Illuminate\Http\Response|JsonResponse|RedirectResponse + protected function invalid($request, LaravelValidationException $exception): \Illuminate\Http\Response | JsonResponse | RedirectResponse { // protect against open redirect when submitting invalid forms. $previous = app('steam')->getSafePreviousUrl(); @@ -283,8 +291,7 @@ class Handler extends ExceptionHandler return redirect($redirect ?? $previous) ->withInput(Arr::except($request->input(), $this->dontFlash)) - ->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag)) - ; + ->withErrors($exception->errors(), $request->input('_error_bag', $exception->errorBag)); } /** diff --git a/composer.json b/composer.json index 037c109753..38f19e3e14 100644 --- a/composer.json +++ b/composer.json @@ -103,6 +103,7 @@ "psr/log": "<4", "ramsey/uuid": "^4.7", "rcrowe/twigbridge": "^0.14", + "sentry/sentry-laravel": "^4.18", "spatie/laravel-html": "^3.2", "spatie/laravel-ignition": "^2", "spatie/period": "^2.4", diff --git a/composer.lock b/composer.lock index 6bb9b491ee..ba0c780fe9 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5c65637d2a997c3503e4922eb7647e2a", + "content-hash": "946638fa99da77780e75953c338d9a55", "packages": [ { "name": "bacon/bacon-qr-code", @@ -1808,6 +1808,66 @@ }, "time": "2022-03-31T05:55:34+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, { "name": "laravel-notification-channels/pushover", "version": "4.1.2", @@ -5903,6 +5963,183 @@ }, "time": "2025-08-20T11:25:49+00:00" }, + { + "name": "sentry/sentry", + "version": "4.17.1", + "source": { + "type": "git", + "url": "https://github.com/getsentry/sentry-php.git", + "reference": "5c696b8de57e841a2bf3b6f6eecfd99acfdda80c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/5c696b8de57e841a2bf3b6f6eecfd99acfdda80c", + "reference": "5c696b8de57e841a2bf3b6f6eecfd99acfdda80c", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "guzzlehttp/psr7": "^1.8.4|^2.1.1", + "jean85/pretty-package-versions": "^1.5|^2.0.4", + "php": "^7.2|^8.0", + "psr/log": "^1.0|^2.0|^3.0", + "symfony/options-resolver": "^4.4.30|^5.0.11|^6.0|^7.0" + }, + "conflict": { + "raven/raven": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^1.8.4|^2.1.1", + "monolog/monolog": "^1.6|^2.0|^3.0", + "phpbench/phpbench": "^1.0", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^8.5|^9.6", + "vimeo/psalm": "^4.17" + }, + "suggest": { + "monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler." + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Sentry\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sentry", + "email": "accounts@sentry.io" + } + ], + "description": "PHP SDK for Sentry (http://sentry.io)", + "homepage": "http://sentry.io", + "keywords": [ + "crash-reporting", + "crash-reports", + "error-handler", + "error-monitoring", + "log", + "logging", + "profiling", + "sentry", + "tracing" + ], + "support": { + "issues": "https://github.com/getsentry/sentry-php/issues", + "source": "https://github.com/getsentry/sentry-php/tree/4.17.1" + }, + "funding": [ + { + "url": "https://sentry.io/", + "type": "custom" + }, + { + "url": "https://sentry.io/pricing/", + "type": "custom" + } + ], + "time": "2025-10-23T15:19:24+00:00" + }, + { + "name": "sentry/sentry-laravel", + "version": "4.18.0", + "source": { + "type": "git", + "url": "https://github.com/getsentry/sentry-laravel.git", + "reference": "b9a647f93f9a040eaf6f21d0684f2351310d3360" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/b9a647f93f9a040eaf6f21d0684f2351310d3360", + "reference": "b9a647f93f9a040eaf6f21d0684f2351310d3360", + "shasum": "" + }, + "require": { + "illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0", + "nyholm/psr7": "^1.0", + "php": "^7.2 | ^8.0", + "sentry/sentry": "^4.16.0", + "symfony/psr-http-message-bridge": "^1.0 | ^2.0 | ^6.0 | ^7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.11", + "guzzlehttp/guzzle": "^7.2", + "laravel/folio": "^1.1", + "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0 | ^11.0 | ^12.0", + "livewire/livewire": "^2.0 | ^3.0", + "mockery/mockery": "^1.3", + "orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.4 | ^9.3 | ^10.4 | ^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Sentry": "Sentry\\Laravel\\Facade" + }, + "providers": [ + "Sentry\\Laravel\\ServiceProvider", + "Sentry\\Laravel\\Tracing\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-0": { + "Sentry\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sentry", + "email": "accounts@sentry.io" + } + ], + "description": "Laravel SDK for Sentry (https://sentry.io)", + "homepage": "https://sentry.io", + "keywords": [ + "crash-reporting", + "crash-reports", + "error-handler", + "error-monitoring", + "laravel", + "log", + "logging", + "profiling", + "sentry", + "tracing" + ], + "support": { + "issues": "https://github.com/getsentry/sentry-laravel/issues", + "source": "https://github.com/getsentry/sentry-laravel/tree/4.18.0" + }, + "funding": [ + { + "url": "https://sentry.io/", + "type": "custom" + }, + { + "url": "https://sentry.io/pricing/", + "type": "custom" + } + ], + "time": "2025-10-20T12:57:51+00:00" + }, { "name": "spatie/backtrace", "version": "1.8.1", diff --git a/config/firefly.php b/config/firefly.php index 634cf70045..37c9d78e30 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -107,6 +107,7 @@ return [ 'demo_password' => env('DEMO_PASSWORD', ''), 'tracker_site_id' => env('TRACKER_SITE_ID', ''), 'tracker_url' => env('TRACKER_URL', ''), + 'track_errors' => env('TRACK_ERRORS', false), // authentication settings 'authentication_guard' => envNonEmpty('AUTHENTICATION_GUARD', 'web'), @@ -410,7 +411,7 @@ return [ ], - 'rule-actions' => [ + 'rule-actions' => [ 'set_category' => SetCategory::class, 'clear_category' => ClearCategory::class, 'set_budget' => SetBudget::class, @@ -444,7 +445,7 @@ return [ // 'set_foreign_amount' => SetForeignAmount::class, // 'set_foreign_currency' => SetForeignCurrency::class, ], - 'context-rule-actions' => [ + 'context-rule-actions' => [ 'set_category', 'set_budget', 'add_tag', @@ -463,13 +464,13 @@ return [ 'convert_transfer', ], - 'test-triggers' => [ + 'test-triggers' => [ 'limit' => 10, 'range' => 200, ], // expected source types for each transaction type, in order of preference. - 'expected_source_types' => [ + 'expected_source_types' => [ 'source' => [ TransactionTypeEnum::WITHDRAWAL->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], TransactionTypeEnum::DEPOSIT->value => [AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::CASH->value], @@ -514,7 +515,7 @@ return [ TransactionTypeEnum::LIABILITY_CREDIT->value => [AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], ], ], - 'allowed_opposing_types' => [ + 'allowed_opposing_types' => [ 'source' => [ AccountTypeEnum::ASSET->value => [ AccountTypeEnum::ASSET->value, @@ -604,7 +605,7 @@ return [ ], ], // depending on the account type, return the allowed transaction types: - 'allowed_transaction_types' => [ + 'allowed_transaction_types' => [ 'source' => [ AccountTypeEnum::ASSET->value => [ TransactionTypeEnum::WITHDRAWAL->value, @@ -673,7 +674,7 @@ return [ ], // having the source + dest will tell you the transaction type. - 'account_to_transaction' => [ + 'account_to_transaction' => [ AccountTypeEnum::ASSET->value => [ AccountTypeEnum::ASSET->value => TransactionTypeEnum::TRANSFER->value, AccountTypeEnum::CASH->value => TransactionTypeEnum::WITHDRAWAL->value, @@ -738,7 +739,7 @@ return [ ], // allowed source -> destination accounts. - 'source_dests' => [ + 'source_dests' => [ TransactionTypeEnum::WITHDRAWAL->value => [ AccountTypeEnum::ASSET->value => [AccountTypeEnum::EXPENSE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::CASH->value], AccountTypeEnum::LOAN->value => [AccountTypeEnum::EXPENSE->value, AccountTypeEnum::CASH->value], @@ -777,7 +778,7 @@ return [ ], ], // if you add fields to this array, don't forget to update the export routine (ExportDataGenerator). - 'journal_meta_fields' => [ + 'journal_meta_fields' => [ // sepa 'sepa_cc', 'sepa_ct_op', @@ -811,47 +812,47 @@ return [ 'recurrence_count', 'recurrence_date', ], - 'webhooks' => [ + 'webhooks' => [ 'max_attempts' => env('WEBHOOK_MAX_ATTEMPTS', 3), ], - 'can_have_virtual_amounts' => [AccountTypeEnum::ASSET->value], - 'can_have_opening_balance' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], - 'dynamic_creation_allowed' => [ + 'can_have_virtual_amounts' => [AccountTypeEnum::ASSET->value], + 'can_have_opening_balance' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], + 'dynamic_creation_allowed' => [ AccountTypeEnum::EXPENSE->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::INITIAL_BALANCE->value, AccountTypeEnum::RECONCILIATION->value, AccountTypeEnum::LIABILITY_CREDIT->value, ], - 'valid_asset_fields' => ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'], - 'valid_cc_fields' => ['account_role', 'cc_monthly_payment_date', 'cc_type', 'account_number', 'currency_id', 'BIC', 'include_net_worth'], - 'valid_account_fields' => ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth', 'liability_direction'], + 'valid_asset_fields' => ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'], + 'valid_cc_fields' => ['account_role', 'cc_monthly_payment_date', 'cc_type', 'account_number', 'currency_id', 'BIC', 'include_net_worth'], + 'valid_account_fields' => ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth', 'liability_direction'], // dynamic date ranges are as follows: - 'dynamic_date_ranges' => ['last7', 'last30', 'last90', 'last365', 'MTD', 'QTD', 'YTD'], + 'dynamic_date_ranges' => ['last7', 'last30', 'last90', 'last365', 'MTD', 'QTD', 'YTD'], - 'allowed_sort_parameters' => [ + 'allowed_sort_parameters' => [ 'Account' => ['id', 'order', 'name', 'iban', 'active', 'account_type_id', - 'current_balance', - 'pc_current_balance', - 'opening_balance', - 'pc_opening_balance', - 'virtual_balance', - 'pc_virtual_balance', - 'debt_amount', - 'pc_debt_amount', - 'balance_difference', - 'pc_balance_difference', + 'current_balance', + 'pc_current_balance', + 'opening_balance', + 'pc_opening_balance', + 'virtual_balance', + 'pc_virtual_balance', + 'debt_amount', + 'pc_debt_amount', + 'balance_difference', + 'pc_balance_difference', ], ], - 'allowed_db_sort_parameters' => [ + 'allowed_db_sort_parameters' => [ 'Account' => ['id', 'order', 'name', 'iban', 'active', 'account_type_id'], ], // preselected account lists possibilities: - 'preselected_accounts' => ['all', 'assets', 'liabilities'], + 'preselected_accounts' => ['all', 'assets', 'liabilities'], // allowed to store a piggy bank in: - 'piggy_bank_account_types' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], + 'piggy_bank_account_types' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value], ]; diff --git a/config/sentry.php b/config/sentry.php new file mode 100644 index 0000000000..b4256e1524 --- /dev/null +++ b/config/sentry.php @@ -0,0 +1,126 @@ + 'SENTRY_LARAVEL_DSN=https://cf9d7aea92537db1e97e3e758b88b0a3@o4510302583848960.ingest.de.sentry.io/4510302585290832', + 'release' => env('SENTRY_RELEASE'), + + // When left empty or `null` the Laravel environment will be used (usually discovered from `APP_ENV` in your `.env`) + 'environment' => env('SENTRY_ENVIRONMENT'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#sample_rate + 'sample_rate' => env('SENTRY_SAMPLE_RATE') === null ? 1.0 : (float)env('SENTRY_SAMPLE_RATE'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#traces_sample_rate + 'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_TRACES_SAMPLE_RATE'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#profiles-sample-rate + 'profiles_sample_rate' => env('SENTRY_PROFILES_SAMPLE_RATE') === null ? null : (float)env('SENTRY_PROFILES_SAMPLE_RATE'), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#enable_logs + 'enable_logs' => env('SENTRY_ENABLE_LOGS', false), + + // The minimum log level that will be sent to Sentry as logs using the `sentry_logs` logging channel + 'logs_channel_level' => env('SENTRY_LOG_LEVEL', env('SENTRY_LOGS_LEVEL', env('LOG_LEVEL', 'debug'))), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#send_default_pii + 'send_default_pii' => env('SENTRY_SEND_DEFAULT_PII', false), + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore_exceptions + 'ignore_exceptions' => [ + \Illuminate\Auth\AuthenticationException::class, + ], + + // @see: https://docs.sentry.io/platforms/php/guides/laravel/configuration/options/#ignore_transactions + 'ignore_transactions' => [ + // Ignore Laravel's default health URL + '/up', + ], + + // Breadcrumb specific configuration + 'breadcrumbs' => [ + // Capture Laravel logs as breadcrumbs + 'logs' => env('SENTRY_BREADCRUMBS_LOGS_ENABLED', true), + + // Capture Laravel cache events (hits, writes etc.) as breadcrumbs + 'cache' => env('SENTRY_BREADCRUMBS_CACHE_ENABLED', true), + + // Capture Livewire components like routes as breadcrumbs + 'livewire' => env('SENTRY_BREADCRUMBS_LIVEWIRE_ENABLED', true), + + // Capture SQL queries as breadcrumbs + 'sql_queries' => env('SENTRY_BREADCRUMBS_SQL_QUERIES_ENABLED', true), + + // Capture SQL query bindings (parameters) in SQL query breadcrumbs + 'sql_bindings' => env('SENTRY_BREADCRUMBS_SQL_BINDINGS_ENABLED', false), + + // Capture queue job information as breadcrumbs + 'queue_info' => env('SENTRY_BREADCRUMBS_QUEUE_INFO_ENABLED', true), + + // Capture command information as breadcrumbs + 'command_info' => env('SENTRY_BREADCRUMBS_COMMAND_JOBS_ENABLED', true), + + // Capture HTTP client request information as breadcrumbs + 'http_client_requests' => env('SENTRY_BREADCRUMBS_HTTP_CLIENT_REQUESTS_ENABLED', true), + + // Capture send notifications as breadcrumbs + 'notifications' => env('SENTRY_BREADCRUMBS_NOTIFICATIONS_ENABLED', true), + ], + + // Performance monitoring specific configuration + 'tracing' => [ + // Trace queue jobs as their own transactions (this enables tracing for queue jobs) + 'queue_job_transactions' => env('SENTRY_TRACE_QUEUE_ENABLED', true), + + // Capture queue jobs as spans when executed on the sync driver + 'queue_jobs' => env('SENTRY_TRACE_QUEUE_JOBS_ENABLED', true), + + // Capture SQL queries as spans + 'sql_queries' => env('SENTRY_TRACE_SQL_QUERIES_ENABLED', true), + + // Capture SQL query bindings (parameters) in SQL query spans + 'sql_bindings' => env('SENTRY_TRACE_SQL_BINDINGS_ENABLED', false), + + // Capture where the SQL query originated from on the SQL query spans + 'sql_origin' => env('SENTRY_TRACE_SQL_ORIGIN_ENABLED', true), + + // Define a threshold in milliseconds for SQL queries to resolve their origin + 'sql_origin_threshold_ms' => env('SENTRY_TRACE_SQL_ORIGIN_THRESHOLD_MS', 100), + + // Capture views rendered as spans + 'views' => env('SENTRY_TRACE_VIEWS_ENABLED', true), + + // Capture Livewire components as spans + 'livewire' => env('SENTRY_TRACE_LIVEWIRE_ENABLED', true), + + // Capture HTTP client requests as spans + 'http_client_requests' => env('SENTRY_TRACE_HTTP_CLIENT_REQUESTS_ENABLED', true), + + // Capture Laravel cache events (hits, writes etc.) as spans + 'cache' => env('SENTRY_TRACE_CACHE_ENABLED', true), + + // Capture Redis operations as spans (this enables Redis events in Laravel) + 'redis_commands' => env('SENTRY_TRACE_REDIS_COMMANDS', false), + + // Capture where the Redis command originated from on the Redis command spans + 'redis_origin' => env('SENTRY_TRACE_REDIS_ORIGIN_ENABLED', true), + + // Capture send notifications as spans + 'notifications' => env('SENTRY_TRACE_NOTIFICATIONS_ENABLED', true), + + // Enable tracing for requests without a matching route (404's) + 'missing_routes' => env('SENTRY_TRACE_MISSING_ROUTES_ENABLED', false), + + // Configures if the performance trace should continue after the response has been sent to the user until the application terminates + // This is required to capture any spans that are created after the response has been sent like queue jobs dispatched using `dispatch(...)->afterResponse()` for example + 'continue_after_response' => env('SENTRY_TRACE_CONTINUE_AFTER_RESPONSE', true), + + // Enable the tracing integrations supplied by Sentry (recommended) + 'default_integrations' => env('SENTRY_TRACE_DEFAULT_INTEGRATIONS_ENABLED', true), + ], + +];