diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index cea3cf83..da978ff3 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -21,14 +21,6 @@ jobs:
php: [ "8.1", "8.0", "7.4", "7.3", "7.2" ]
packages:
# All versions below should be test on PHP ^7.1 (Sentry SDK requirement)
- - { laravel: 5.1.*, testbench: 3.1.*, phpunit: 5.7.* }
- - { laravel: 5.2.*, testbench: 3.2.*, phpunit: 5.7.* }
- - { laravel: 5.3.*, testbench: 3.3.*, phpunit: 5.7.* }
- - { laravel: 5.4.*, testbench: 3.4.*, phpunit: 5.7.* }
- - { laravel: 5.5.*, testbench: 3.5.*, phpunit: 6.5.* }
- - { laravel: 5.6.*, testbench: 3.6.*, phpunit: 7.5.* }
- - { laravel: 5.7.*, testbench: 3.7.*, phpunit: 7.5.* }
- - { laravel: 5.8.*, testbench: 3.8.*, phpunit: 7.5.* }
- { laravel: ^6.0, testbench: 4.7.*, phpunit: 8.4.* }
- { laravel: ^7.0, testbench: 5.1.*, phpunit: 8.4.* }
@@ -48,43 +40,11 @@ jobs:
- php: "7.2"
packages: { laravel: ^8.0, testbench: ^6.0, phpunit: 9.3.* }
- - php: "8.0"
- packages: { laravel: 5.1.*, testbench: 3.1.*, phpunit: 5.7.* }
- - php: "8.0"
- packages: { laravel: 5.2.*, testbench: 3.2.*, phpunit: 5.7.* }
- - php: "8.0"
- packages: { laravel: 5.3.*, testbench: 3.3.*, phpunit: 5.7.* }
- - php: "8.0"
- packages: { laravel: 5.4.*, testbench: 3.4.*, phpunit: 5.7.* }
- - php: "8.0"
- packages: { laravel: 5.5.*, testbench: 3.5.*, phpunit: 6.5.* }
- - php: "8.0"
- packages: { laravel: 5.6.*, testbench: 3.6.*, phpunit: 7.5.* }
- - php: "8.0"
- packages: { laravel: 5.7.*, testbench: 3.7.*, phpunit: 7.5.* }
- - php: "8.0"
- packages: { laravel: 5.8.*, testbench: 3.8.*, phpunit: 7.5.* }
- php: "8.0"
packages: { laravel: ^6.0, testbench: 4.7.*, phpunit: 8.4.* }
- php: "8.0"
packages: { laravel: ^7.0, testbench: 5.1.*, phpunit: 8.4.* }
- - php: "8.1"
- packages: { laravel: 5.1.*, testbench: 3.1.*, phpunit: 5.7.* }
- - php: "8.1"
- packages: { laravel: 5.2.*, testbench: 3.2.*, phpunit: 5.7.* }
- - php: "8.1"
- packages: { laravel: 5.3.*, testbench: 3.3.*, phpunit: 5.7.* }
- - php: "8.1"
- packages: { laravel: 5.4.*, testbench: 3.4.*, phpunit: 5.7.* }
- - php: "8.1"
- packages: { laravel: 5.5.*, testbench: 3.5.*, phpunit: 6.5.* }
- - php: "8.1"
- packages: { laravel: 5.6.*, testbench: 3.6.*, phpunit: 7.5.* }
- - php: "8.1"
- packages: { laravel: 5.7.*, testbench: 3.7.*, phpunit: 7.5.* }
- - php: "8.1"
- packages: { laravel: 5.8.*, testbench: 3.8.*, phpunit: 7.5.* }
- php: "8.1"
packages: { laravel: ^6.0, testbench: 4.7.*, phpunit: 8.4.* }
- php: "8.1"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4630d591..fcd95b74 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,33 @@
## Unreleased
+## 3.0.0
+
+**New features**
+
+- We are now creating more spans to give you better insights into the performance of your application
+ - Add a `http.client` span. This span indicates the time that is spent when using the Laravel HTTP client (#585)
+ - Add a `http.route` span. This span indicates the time that is spent inside a controller method or route closure (#593)
+ - Add a `db.transaction` span. This span indicates the time that is spent inside a database transaction (#594)
+- Add support for [Dynamic Sampling](https://docs.sentry.io/product/data-management-settings/dynamic-sampling/), allowing developers to set a server-side sampling rate without the need to re-deploy their applications
+ - Add support for Dynamic Sampling (#572)
+
+**Breaking changes**
+
+- Laravel Lumen is no longer supported
+ - Drop support for Laravel Lumen (#579)
+- Laravel versions 5.0 - 5.8 are no longer supported
+ - Drop support for Laravel 5.x (#581)
+- Remove `Sentry\Integration::extractNameForRoute()`, it's alternative `Sentry\Integration::extractNameAndSourceForRoute()` is marked as `@internal` (#580)
+- Remove internal `Sentry\Integration::currentTracingSpan()`, use `SentrySdk::getCurrentHub()->getSpan()` if you were using this internal method (#592)
+
+**Other changes**
+
+- Set the tracing transaction name on the `Illuminate\Routing\Events\RouteMatched` instead of at the end of the request (#580)
+- Remove extracting route name or controller for transaction names (#583). This unifies the transaction names to a more concise format.
+- Simplify Sentry meta tag retrieval, by adding `Sentry\Laravel\Integration::sentryMeta()` (#586)
+- Fix tracing with nested queue jobs (mostly when running jobs in the `sync` driver) (#592)
+
## 2.14.2
- Fix extracting command input resulting in errors when calling Artisan commands programatically with `null` as an argument value (#589)
@@ -9,6 +36,7 @@
## 2.14.1
- Fix not setting the correct SDK ID and version when running the `sentry:test` command (#582)
+- Transaction names now only show the parameterized URL (`/some/{route}`) instead of the route name or controller class (#583)
## 2.14.0
diff --git a/README.md b/README.md
index 30be5cbd..29089b1c 100644
--- a/README.md
+++ b/README.md
@@ -78,15 +78,21 @@ try {
## Laravel Version Compatibility
-- Laravel `<= 4.2.x` is supported until `0.8.x`
-- Laravel `<= 5.7.x` on PHP `<= 7.0` is supported until `0.11.x`
-- Laravel `>= 5.x.x` on PHP `>= 7.1` is supported in all versions
+The Laravel versions listed below are all currently supported:
+
- Laravel `>= 6.x.x` on PHP `>= 7.2` is supported starting from `1.2.0`
- Laravel `>= 7.x.x` on PHP `>= 7.2` is supported starting from `1.7.0`
- Laravel `>= 8.x.x` on PHP `>= 7.3` is supported starting from `1.9.0`
- Laravel `>= 9.x.x` on PHP `>= 8.0` is supported starting from `2.11.0`
-Please note that of version `>= 2.0.0` we require PHP Version `>= 7.2` because we are using our new [PHP SDK](https://github.com/getsentry/sentry-php) underneath.
+Please note that starting with version `>= 2.0.0` we require PHP Version `>= 7.2` because we are using our new [PHP SDK](https://github.com/getsentry/sentry-php) underneath.
+
+The Laravel and Lumen version listed below were supported in previous versions:
+
+- Laravel `<= 4.2.x` is supported until `0.8.x`
+- Laravel `<= 5.7.x` on PHP `<= 7.0` is supported until `0.11.x`
+- Laravel `>= 5.x.x` on PHP `>= 7.1` is supported until `2.14.x`
+- Laravel Lumen is supported until `2.14.x`
## Contributing to the SDK
diff --git a/composer.json b/composer.json
index 108447d4..c3643221 100644
--- a/composer.json
+++ b/composer.json
@@ -22,21 +22,24 @@
],
"require": {
"php": "^7.2 | ^8.0",
- "illuminate/support": "5.0 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0",
- "sentry/sentry": "^3.3",
+ "illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0",
+ "sentry/sentry": "^3.9",
"sentry/sdk": "^3.1",
"symfony/psr-http-message-bridge": "^1.0 | ^2.0",
"nyholm/psr7": "^1.0"
},
+ "conflict": {
+ "laravel/lumen-framework": "*"
+ },
"autoload": {
"psr-0": {
"Sentry\\Laravel\\": "src/"
}
},
"require-dev": {
- "phpunit/phpunit": "^5.7 | ^6.5 | ^7.5 | ^8.4 | ^9.3",
- "laravel/framework": "5.0 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0",
- "orchestra/testbench": "3.1 - 3.8 | ^4.7 | ^5.1 | ^6.0 | ^7.0",
+ "phpunit/phpunit": "^8.4 | ^9.3",
+ "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0",
+ "orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0",
"friendsofphp/php-cs-fixer": "^3.11",
"mockery/mockery": "^1.3"
},
@@ -61,8 +64,8 @@
},
"extra": {
"branch-alias": {
- "dev-3.x": "3.x-dev",
- "dev-master": "2.x-dev",
+ "dev-master": "3.x-dev",
+ "dev-2.x": "2.x-dev",
"dev-1.x": "1.x-dev",
"dev-0.x": "0.x-dev"
},
diff --git a/config/sentry.php b/config/sentry.php
index b0e4b700..08c918bf 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -45,6 +45,9 @@
// Indicates if the tracing integrations supplied by Sentry should be loaded
'default_integrations' => true,
+
+ // Indicates that requests without a matching route should be traced
+ 'missing_routes' => false,
],
// @see: https://docs.sentry.io/platforms/php/configuration/options/#send-default-pii
@@ -52,6 +55,4 @@
'traces_sample_rate' => (float)(env('SENTRY_TRACES_SAMPLE_RATE', 0.0)),
- 'controllers_base_namespace' => env('SENTRY_CONTROLLERS_BASE_NAMESPACE', 'App\\Http\\Controllers'),
-
];
diff --git a/src/Sentry/Laravel/Console/PublishCommand.php b/src/Sentry/Laravel/Console/PublishCommand.php
index 04cd0d54..19f253ef 100644
--- a/src/Sentry/Laravel/Console/PublishCommand.php
+++ b/src/Sentry/Laravel/Console/PublishCommand.php
@@ -10,13 +10,6 @@
class PublishCommand extends Command
{
- /**
- * Laravel 5.0.x: The name and signature of the console command.
- *
- * @var string
- */
- protected $name = 'sentry:publish';
-
/**
* The name and signature of the console command.
*
diff --git a/src/Sentry/Laravel/Console/TestCommand.php b/src/Sentry/Laravel/Console/TestCommand.php
index d921317d..67c60847 100644
--- a/src/Sentry/Laravel/Console/TestCommand.php
+++ b/src/Sentry/Laravel/Console/TestCommand.php
@@ -12,17 +12,11 @@
use Sentry\State\HubInterface;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\TransactionContext;
+use Sentry\Tracing\TransactionSource;
use Throwable;
class TestCommand extends Command
{
- /**
- * Laravel 5.0.x: The name and signature of the console command.
- *
- * @var string
- */
- protected $name = 'sentry:test';
-
/**
* The name and signature of the console command.
*
@@ -145,6 +139,7 @@ public function log($level, $message, array $context = []): void
$transactionContext = new TransactionContext();
$transactionContext->setSampled(true);
$transactionContext->setName('Sentry Test Transaction');
+ $transactionContext->setSource(TransactionSource::custom());
$transactionContext->setOp('sentry.test');
$transaction = $hub->startTransaction($transactionContext);
diff --git a/src/Sentry/Laravel/EventHandler.php b/src/Sentry/Laravel/EventHandler.php
index 5ad15db6..e2f32924 100644
--- a/src/Sentry/Laravel/EventHandler.php
+++ b/src/Sentry/Laravel/EventHandler.php
@@ -3,24 +3,19 @@
namespace Sentry\Laravel;
use Exception;
-use Illuminate\Auth\Events\Authenticated;
-use Illuminate\Console\Events\CommandFinished;
-use Illuminate\Console\Events\CommandStarting;
+use Illuminate\Auth\Events as AuthEvents;
+use Illuminate\Console\Events as ConsoleEvents;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Database\Events\QueryExecuted;
+use Illuminate\Database\Events as DatabaseEvents;
use Illuminate\Http\Request;
-use Illuminate\Log\Events\MessageLogged;
-use Illuminate\Queue\Events\JobExceptionOccurred;
-use Illuminate\Queue\Events\JobProcessed;
-use Illuminate\Queue\Events\JobProcessing;
-use Illuminate\Queue\Events\WorkerStopping;
+use Illuminate\Log\Events as LogEvents;
+use Illuminate\Queue\Events as QueueEvents;
use Illuminate\Queue\QueueManager;
-use Illuminate\Routing\Events\RouteMatched;
-use Illuminate\Routing\Route;
+use Illuminate\Routing\Events as RoutingEvents;
use Laravel\Octane\Events as Octane;
use Laravel\Sanctum\Events as Sanctum;
use RuntimeException;
@@ -38,17 +33,11 @@ class EventHandler
* @var array
*/
protected static $eventHandlerMap = [
- 'router.matched' => 'routerMatched', // Until Laravel 5.1
- 'Illuminate\Routing\Events\RouteMatched' => 'routeMatched', // Since Laravel 5.2
-
- 'illuminate.query' => 'query', // Until Laravel 5.1
- 'Illuminate\Database\Events\QueryExecuted' => 'queryExecuted', // Since Laravel 5.2
-
- 'illuminate.log' => 'log', // Until Laravel 5.3
- 'Illuminate\Log\Events\MessageLogged' => 'messageLogged', // Since Laravel 5.4
-
- 'Illuminate\Console\Events\CommandStarting' => 'commandStarting', // Since Laravel 5.5
- 'Illuminate\Console\Events\CommandFinished' => 'commandFinished', // Since Laravel 5.5
+ LogEvents\MessageLogged::class => 'messageLogged',
+ RoutingEvents\RouteMatched::class => 'routeMatched',
+ DatabaseEvents\QueryExecuted::class => 'queryExecuted',
+ ConsoleEvents\CommandStarting::class => 'commandStarting',
+ ConsoleEvents\CommandFinished::class => 'commandFinished',
];
/**
@@ -57,8 +46,8 @@ class EventHandler
* @var array
*/
protected static $authEventHandlerMap = [
- 'Illuminate\Auth\Events\Authenticated' => 'authenticated', // Since Laravel 5.3
- 'Laravel\Sanctum\Events\TokenAuthenticated' => 'sanctumTokenAuthenticated', // Since Sanctum 2.13
+ AuthEvents\Authenticated::class => 'authenticated',
+ Sanctum\TokenAuthenticated::class => 'sanctumTokenAuthenticated', // Since Sanctum 2.13
];
/**
@@ -67,10 +56,10 @@ class EventHandler
* @var array
*/
protected static $queueEventHandlerMap = [
- 'Illuminate\Queue\Events\JobProcessed' => 'queueJobProcessed', // Since Laravel 5.2
- 'Illuminate\Queue\Events\JobProcessing' => 'queueJobProcessing', // Since Laravel 5.2
- 'Illuminate\Queue\Events\WorkerStopping' => 'queueWorkerStopping', // Since Laravel 5.2
- 'Illuminate\Queue\Events\JobExceptionOccurred' => 'queueJobExceptionOccurred', // Since Laravel 5.2
+ QueueEvents\JobProcessed::class => 'queueJobProcessed',
+ QueueEvents\JobProcessing::class => 'queueJobProcessing',
+ QueueEvents\WorkerStopping::class => 'queueWorkerStopping',
+ QueueEvents\JobExceptionOccurred::class => 'queueJobExceptionOccurred',
];
/**
@@ -79,17 +68,17 @@ class EventHandler
* @var array
*/
protected static $octaneEventHandlerMap = [
- 'Laravel\Octane\Events\RequestReceived' => 'octaneRequestReceived',
- 'Laravel\Octane\Events\RequestTerminated' => 'octaneRequestTerminated',
+ Octane\RequestReceived::class => 'octaneRequestReceived',
+ Octane\RequestTerminated::class => 'octaneRequestTerminated',
- 'Laravel\Octane\Events\TaskReceived' => 'octaneTaskReceived',
- 'Laravel\Octane\Events\TaskTerminated' => 'octaneTaskTerminated',
+ Octane\TaskReceived::class => 'octaneTaskReceived',
+ Octane\TaskTerminated::class => 'octaneTaskTerminated',
- 'Laravel\Octane\Events\TickReceived' => 'octaneTickReceived',
- 'Laravel\Octane\Events\TickTerminated' => 'octaneTickTerminated',
+ Octane\TickReceived::class => 'octaneTickReceived',
+ Octane\TickTerminated::class => 'octaneTickTerminated',
- 'Laravel\Octane\Events\WorkerErrorOccurred' => 'octaneWorkerErrorOccurred',
- 'Laravel\Octane\Events\WorkerStopping' => 'octaneWorkerStopping',
+ Octane\WorkerErrorOccurred::class => 'octaneWorkerErrorOccurred',
+ Octane\WorkerStopping::class => 'octaneWorkerStopping',
];
/**
@@ -151,9 +140,9 @@ class EventHandler
/**
* Indicates if we pushed a scope for the queue.
*
- * @var bool
+ * @var int
*/
- private $pushedQueueScope = false;
+ private $pushedQueueScopeCount = 0;
/**
* Indicates if we pushed a scope for Octane.
@@ -184,76 +173,40 @@ public function __construct(Container $container, array $config)
/**
* Attach all event handlers.
*/
- public function subscribe(): void
+ public function subscribe(Dispatcher $dispatcher): void
{
- /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
- try {
- $dispatcher = $this->container->make(Dispatcher::class);
-
- foreach (static::$eventHandlerMap as $eventName => $handler) {
- $dispatcher->listen($eventName, [$this, $handler]);
- }
- } catch (BindingResolutionException $e) {
- // If we cannot resolve the event dispatcher we also cannot listen to events
+ foreach (static::$eventHandlerMap as $eventName => $handler) {
+ $dispatcher->listen($eventName, [$this, $handler]);
}
}
/**
* Attach all authentication event handlers.
*/
- public function subscribeAuthEvents(): void
+ public function subscribeAuthEvents(Dispatcher $dispatcher): void
{
- /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
- try {
- $dispatcher = $this->container->make(Dispatcher::class);
-
- foreach (static::$authEventHandlerMap as $eventName => $handler) {
- $dispatcher->listen($eventName, [$this, $handler]);
- }
- } catch (BindingResolutionException $e) {
- // If we cannot resolve the event dispatcher we also cannot listen to events
+ foreach (static::$authEventHandlerMap as $eventName => $handler) {
+ $dispatcher->listen($eventName, [$this, $handler]);
}
}
/**
* Attach all queue event handlers.
*/
- public function subscribeOctaneEvents(): void
+ public function subscribeOctaneEvents(Dispatcher $dispatcher): void
{
- /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
- try {
- $dispatcher = $this->container->make(Dispatcher::class);
-
- foreach (static::$octaneEventHandlerMap as $eventName => $handler) {
- $dispatcher->listen($eventName, [$this, $handler]);
- }
- } catch (BindingResolutionException $e) {
- // If we cannot resolve the event dispatcher we also cannot listen to events
+ foreach (static::$octaneEventHandlerMap as $eventName => $handler) {
+ $dispatcher->listen($eventName, [$this, $handler]);
}
}
/**
* Attach all queue event handlers.
- *
- * @param \Illuminate\Queue\QueueManager $queue
*/
- public function subscribeQueueEvents(QueueManager $queue): void
+ public function subscribeQueueEvents(Dispatcher $dispatcher): void
{
- $queue->looping(function () {
- $this->cleanupScopeForTaskWithinLongRunningProcessWhen($this->pushedQueueScope);
-
- $this->pushedQueueScope = false;
- });
-
- /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
- try {
- $dispatcher = $this->container->make(Dispatcher::class);
-
- foreach (static::$queueEventHandlerMap as $eventName => $handler) {
- $dispatcher->listen($eventName, [$this, $handler]);
- }
- } catch (BindingResolutionException $e) {
- // If we cannot resolve the event dispatcher we also cannot listen to events
+ foreach (static::$queueEventHandlerMap as $eventName => $handler) {
+ $dispatcher->listen($eventName, [$this, $handler]);
}
}
@@ -263,7 +216,7 @@ public function subscribeQueueEvents(QueueManager $queue): void
* @param string $method
* @param array $arguments
*/
- public function __call($method, $arguments)
+ public function __call(string $method, array $arguments)
{
$handlerMethod = "{$method}Handler";
@@ -278,14 +231,9 @@ public function __call($method, $arguments)
}
}
- /**
- * Until Laravel 5.1
- *
- * @param Route $route
- */
- protected function routerMatchedHandler(Route $route)
+ protected function routeMatchedHandler(RoutingEvents\RouteMatched $match): void
{
- $routeName = Integration::extractNameForRoute($route);
+ [$routeName] = Integration::extractNameAndSourceForRoute($match->route);
Integration::addBreadcrumb(new Breadcrumb(
Breadcrumb::LEVEL_INFO,
@@ -297,106 +245,32 @@ protected function routerMatchedHandler(Route $route)
Integration::setTransaction($routeName);
}
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Routing\Events\RouteMatched $match
- */
- protected function routeMatchedHandler(RouteMatched $match)
- {
- $this->routerMatchedHandler($match->route);
- }
-
- /**
- * Until Laravel 5.1
- *
- * @param string $query
- * @param array $bindings
- * @param int $time
- * @param string $connectionName
- */
- protected function queryHandler($query, $bindings, $time, $connectionName)
+ protected function queryExecutedHandler(DatabaseEvents\QueryExecuted $query): void
{
if (!$this->recordSqlQueries) {
return;
}
- $this->addQueryBreadcrumb($query, $bindings, $time, $connectionName);
- }
-
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Database\Events\QueryExecuted $query
- */
- protected function queryExecutedHandler(QueryExecuted $query)
- {
- if (!$this->recordSqlQueries) {
- return;
- }
-
- $this->addQueryBreadcrumb($query->sql, $query->bindings, $query->time, $query->connectionName);
- }
-
- /**
- * Helper to add an query breadcrumb.
- *
- * @param string $query
- * @param array $bindings
- * @param float|null $time
- * @param string $connectionName
- */
- private function addQueryBreadcrumb($query, $bindings, $time, $connectionName)
- {
- $data = ['connectionName' => $connectionName];
+ $data = ['connectionName' => $query->connectionName];
- if ($time !== null) {
- $data['executionTimeMs'] = $time;
+ if ($query->time !== null) {
+ $data['executionTimeMs'] = $query->time;
}
if ($this->recordSqlBindings) {
- $data['bindings'] = $bindings;
+ $data['bindings'] = $query->bindings;
}
Integration::addBreadcrumb(new Breadcrumb(
Breadcrumb::LEVEL_INFO,
Breadcrumb::TYPE_DEFAULT,
'sql.query',
- $query,
+ $query->sql,
$data
));
}
- /**
- * Until Laravel 5.3
- *
- * @param string $level
- * @param string $message
- * @param array|null $context
- */
- protected function logHandler($level, $message, $context)
- {
- $this->addLogBreadcrumb($level, $message, is_array($context) ? $context : []);
- }
-
- /**
- * Since Laravel 5.4
- *
- * @param \Illuminate\Log\Events\MessageLogged $logEntry
- */
- protected function messageLoggedHandler(MessageLogged $logEntry)
- {
- $this->addLogBreadcrumb($logEntry->level, $logEntry->message, $logEntry->context);
- }
-
- /**
- * Helper to add an log breadcrumb.
- *
- * @param string $level Log level. May be any standard.
- * @param string|null $message Log message.
- * @param array $context Log context.
- */
- private function addLogBreadcrumb(string $level, ?string $message, array $context = []): void
+ protected function messageLoggedHandler(LogEvents\MessageLogged $logEntry): void
{
if (!$this->recordLaravelLogs) {
return;
@@ -405,35 +279,25 @@ private function addLogBreadcrumb(string $level, ?string $message, array $contex
// A log message with `null` as value will not be recorded by Laravel
// however empty strings are logged so we mimick that behaviour to
// check for `null` to stay consistent with how Laravel logs it
- if ($message === null) {
+ if ($logEntry->message === null) {
return;
}
Integration::addBreadcrumb(new Breadcrumb(
- $this->logLevelToBreadcrumbLevel($level),
+ $this->logLevelToBreadcrumbLevel($logEntry->level),
Breadcrumb::TYPE_DEFAULT,
- 'log.' . $level,
- $message,
- $context
+ 'log.' . $logEntry->level,
+ $logEntry->message,
+ $logEntry->context
));
}
- /**
- * Since Laravel 5.3
- *
- * @param \Illuminate\Auth\Events\Authenticated $event
- */
- protected function authenticatedHandler(Authenticated $event)
+ protected function authenticatedHandler(AuthEvents\Authenticated $event): void
{
$this->configureUserScopeFromModel($event->user);
}
- /**
- * Since Sanctum 2.13
- *
- * @param \Laravel\Sanctum\Events\TokenAuthenticated $event
- */
- protected function sanctumTokenAuthenticatedHandler(Sanctum\TokenAuthenticated $event)
+ protected function sanctumTokenAuthenticatedHandler(Sanctum\TokenAuthenticated $event): void
{
$this->configureUserScopeFromModel($event->token->tokenable);
}
@@ -480,18 +344,11 @@ private function configureUserScopeFromModel($authUser): void
});
}
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Queue\Events\JobProcessing $event
- */
- protected function queueJobProcessingHandler(JobProcessing $event)
+ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event): void
{
- $this->cleanupScopeForTaskWithinLongRunningProcessWhen($this->pushedQueueScope);
-
$this->prepareScopeForTaskWithinLongRunningProcess();
- $this->pushedQueueScope = true;
+ ++$this->pushedQueueScopeCount;
if (!$this->recordQueueInfo) {
return;
@@ -518,43 +375,27 @@ protected function queueJobProcessingHandler(JobProcessing $event)
));
}
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Queue\Events\JobExceptionOccurred $event
- */
- protected function queueJobExceptionOccurredHandler(JobExceptionOccurred $event)
+ protected function queueJobExceptionOccurredHandler(QueueEvents\JobExceptionOccurred $event): void
{
+ $this->cleanupScopeForTaskWithinLongRunningProcessWhen($this->pushedQueueScopeCount > 0);
+
$this->afterTaskWithinLongRunningProcess();
}
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Queue\Events\JobProcessed $event
- */
- protected function queueJobProcessedHandler(JobProcessed $event)
+ protected function queueJobProcessedHandler(QueueEvents\JobProcessed $event): void
{
+ $this->cleanupScopeForTaskWithinLongRunningProcessWhen($this->pushedQueueScopeCount > 0);
+
$this->afterTaskWithinLongRunningProcess();
}
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Queue\Events\WorkerStopping $event
- */
- protected function queueWorkerStoppingHandler(WorkerStopping $event)
+ protected function queueWorkerStoppingHandler(QueueEvents\WorkerStopping $event): void
{
// Flush any and all events that were possibly generated by queue jobs
Integration::flushEvents();
}
- /**
- * Since Laravel 5.5
- *
- * @param \Illuminate\Console\Events\CommandStarting $event
- */
- protected function commandStartingHandler(CommandStarting $event)
+ protected function commandStartingHandler(ConsoleEvents\CommandStarting $event): void
{
if ($event->command) {
Integration::configureScope(static function (Scope $scope) use ($event): void {
@@ -577,12 +418,7 @@ protected function commandStartingHandler(CommandStarting $event)
}
}
- /**
- * Since Laravel 5.5
- *
- * @param \Illuminate\Console\Events\CommandFinished $event
- */
- protected function commandFinishedHandler(CommandFinished $event)
+ protected function commandFinishedHandler(ConsoleEvents\CommandFinished $event): void
{
if ($this->recordCommandInfo) {
Integration::addBreadcrumb(new Breadcrumb(
diff --git a/src/Sentry/Laravel/Http/SetRequestMiddleware.php b/src/Sentry/Laravel/Http/SetRequestMiddleware.php
index bbdfca4e..ffa85e6a 100644
--- a/src/Sentry/Laravel/Http/SetRequestMiddleware.php
+++ b/src/Sentry/Laravel/Http/SetRequestMiddleware.php
@@ -4,11 +4,10 @@
use Closure;
use Illuminate\Container\Container;
+use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Http\Request;
-use Nyholm\Psr7\Factory\Psr17Factory;
use Psr\Http\Message\ServerRequestInterface;
use Sentry\State\HubInterface;
-use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
/**
* This middleware caches a PSR-7 version of the request as early as possible.
@@ -21,7 +20,7 @@ public function handle(Request $request, Closure $next)
$container = Container::getInstance();
if ($container->bound(HubInterface::class)) {
- $psrRequest = $this->resolvePsrRequest($request);
+ $psrRequest = $this->resolvePsrRequest($container);
if ($psrRequest !== null) {
$container->instance(LaravelRequestFetcher::CONTAINER_PSR7_INSTANCE_KEY, $psrRequest);
@@ -31,26 +30,12 @@ public function handle(Request $request, Closure $next)
return $next($request);
}
- /**
- * This code was copied from the Laravel codebase which was introduced in Laravel 6.
- *
- * The reason we have it copied here is because older (<6.0) versions of Laravel use a different
- * method to construct the PSR-7 request object which requires other packages to create that object
- * but most importantly it does not function when those packages are not available resulting in errors
- *
- * So long story short, this is here to backport functionality to Laravel <6.0
- * if we drop support for those versions in the future we can reconsider this and
- * move back to using the container binding provided by Laravel for the PSR-7 object
- *
- * @see https://github.com/laravel/framework/blob/cb550b5bdc2b2c4cf077082adabde0144a72d190/src/Illuminate/Routing/RoutingServiceProvider.php#L127-L146
- */
- private function resolvePsrRequest(Request $request): ?ServerRequestInterface
+ private function resolvePsrRequest(Container $container): ?ServerRequestInterface
{
- if (class_exists(Psr17Factory::class) && class_exists(PsrHttpFactory::class)) {
- $psr17Factory = new Psr17Factory;
-
- return (new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory))
- ->createRequest($request);
+ try {
+ return $container->make(ServerRequestInterface::class);
+ } catch (BindingResolutionException $e) {
+ // This happens if Laravel doesn't have the correct classes available to construct the PSR-7 object
}
return null;
diff --git a/src/Sentry/Laravel/Integration.php b/src/Sentry/Laravel/Integration.php
index f400aaf5..a9df0948 100644
--- a/src/Sentry/Laravel/Integration.php
+++ b/src/Sentry/Laravel/Integration.php
@@ -3,9 +3,8 @@
namespace Sentry\Laravel;
use Illuminate\Routing\Route;
-use Illuminate\Support\Str;
use Sentry\SentrySdk;
-use Sentry\Tracing\Span;
+use Sentry\Tracing\TransactionSource;
use function Sentry\addBreadcrumb;
use function Sentry\configureScope;
use Sentry\Breadcrumb;
@@ -20,11 +19,6 @@ class Integration implements IntegrationInterface
*/
private static $transaction;
- /**
- * @var null|string
- */
- private static $baseControllerNamespace;
-
/**
* {@inheritdoc}
*/
@@ -93,14 +87,6 @@ public static function setTransaction(?string $transaction): void
self::$transaction = $transaction;
}
- /**
- * @param null|string $namespace
- */
- public static function setControllersBaseNamespace(?string $namespace): void
- {
- self::$baseControllerNamespace = $namespace !== null ? trim($namespace, '\\') : null;
- }
-
/**
* Block until all async events are processed for the HTTP transport.
*
@@ -117,150 +103,63 @@ public static function flushEvents(): void
}
/**
- * Extract the readable name for a route.
+ * Extract the readable name for a route and the transaction source for where that route name came from.
*
* @param \Illuminate\Routing\Route $route
*
- * @return string
- */
- public static function extractNameForRoute(Route $route): string
- {
- $routeName = null;
-
- // someaction (route name/alias)
- if ($route->getName()) {
- $routeName = self::extractNameForNamedRoute($route->getName());
- }
-
- // Some\Controller@someAction (controller action)
- if (empty($routeName) && $route->getActionName()) {
- $routeName = self::extractNameForActionRoute($route->getActionName());
- }
-
- // /someaction // Fallback to the url
- if (empty($routeName) || $routeName === 'Closure') {
- $routeName = '/' . ltrim($route->uri(), '/');
- }
-
- return $routeName;
- }
-
- /**
- * Extract the readable name for a Lumen route.
- *
- * @param array $routeData The array of route data
- * @param string $path The path of the request
+ * @return array{0: string, 1: \Sentry\Tracing\TransactionSource}
*
- * @return string
+ * @internal This helper is used in various places to extra meaninful info from a Laravel Route object.
*/
- public static function extractNameForLumenRoute(array $routeData, string $path): string
+ public static function extractNameAndSourceForRoute(Route $route): array
{
- $routeName = null;
-
- $route = $routeData[1] ?? [];
-
- // someaction (route name/alias)
- if (!empty($route['as'])) {
- $routeName = self::extractNameForNamedRoute($route['as']);
- }
-
- // Some\Controller@someAction (controller action)
- if (empty($routeName) && !empty($route['uses'])) {
- $routeName = self::extractNameForActionRoute($route['uses']);
- }
-
- // /someaction // Fallback to the url
- if (empty($routeName) || $routeName === 'Closure') {
- $routeUri = array_reduce(
- array_keys($routeData[2]),
- static function ($carry, $key) use ($routeData) {
- return str_replace($routeData[2][$key], "{{$key}}", $carry);
- },
- $path
- );
-
- $routeName = '/' . ltrim($routeUri, '/');
- }
-
- return $routeName;
+ return [
+ '/' . ltrim($route->uri(), '/'),
+ TransactionSource::route(),
+ ];
}
/**
- * Take a route name and return it only if it's a usable route name.
- *
- * @param string $name
+ * Retrieve the meta tags with tracing information to link this request to front-end requests.
+ * This propagates the Dynamic Sampling Context.
*
- * @return string|null
+ * @return string
*/
- private static function extractNameForNamedRoute(string $name): ?string
+ public static function sentryMeta(): string
{
- // Laravel 7 route caching generates a route names if the user didn't specify one
- // theirselfs to optimize route matching. These route names are useless to the
- // developer so if we encounter a generated route name we discard the value
- if (Str::contains($name, 'generated::')) {
- return null;
- }
-
- // If the route name ends with a `.` we assume an incomplete group name prefix
- // we discard this value since it will most likely not mean anything to the
- // developer and will be duplicated by other unnamed routes in the group
- if (Str::endsWith($name, '.')) {
- return null;
- }
-
- return $name;
+ return self::sentryTracingMeta() . self::sentryBaggageMeta();
}
/**
- * Take a controller action and strip away the base namespace if needed.
- *
- * @param string $action
+ * Retrieve the `sentry-trace` meta tag with tracing information to link this request to front-end requests.
*
* @return string
*/
- private static function extractNameForActionRoute(string $action): string
+ public static function sentryTracingMeta(): string
{
- $routeName = ltrim($action, '\\');
+ $span = SentrySdk::getCurrentHub()->getSpan();
- $baseNamespace = self::$baseControllerNamespace ?? '';
-
- if (empty($baseNamespace)) {
- return $routeName;
+ if ($span === null) {
+ return '';
}
- // Strip away the base namespace from the action name
- // @see: Str::after, but this is not available before Laravel 5.4 so we use a inlined version
- return array_reverse(explode($baseNamespace . '\\', $routeName, 2))[0];
+ return sprintf('', $span->toTraceparent());
}
/**
- * Retrieve the meta tags with tracing information to link this request to front-end requests.
+ * Retrieve the `baggage` meta tag with information to link this request to front-end requests.
+ * This propagates the Dynamic Sampling Context.
*
* @return string
*/
- public static function sentryTracingMeta(): string
+ public static function sentryBaggageMeta(): string
{
- $span = self::currentTracingSpan();
+ $span = SentrySdk::getCurrentHub()->getSpan();
if ($span === null) {
return '';
}
- $content = sprintf('', $span->toTraceparent());
- // $content .= sprintf('', $span->getDescription());
-
- return $content;
- }
-
- /**
- * Get the current active tracing span from the scope.
- *
- * @return \Sentry\Tracing\Span|null
- *
- * @internal This is used internally as an easy way to retrieve the current active tracing span.
- */
- public static function currentTracingSpan(): ?Span
- {
- return SentrySdk::getCurrentHub()->getSpan();
+ return sprintf('', $span->toBaggage());
}
}
diff --git a/src/Sentry/Laravel/LogChannel.php b/src/Sentry/Laravel/LogChannel.php
index 77d3b65a..30d926f6 100644
--- a/src/Sentry/Laravel/LogChannel.php
+++ b/src/Sentry/Laravel/LogChannel.php
@@ -14,7 +14,7 @@ class LogChannel extends LogManager
*
* @return Logger
*/
- public function __invoke(array $config): Logger
+ public function __invoke(array $config = []): Logger
{
$handler = new SentryHandler(
$this->app->make(HubInterface::class),
diff --git a/src/Sentry/Laravel/ServiceProvider.php b/src/Sentry/Laravel/ServiceProvider.php
index 596c4cac..5e01a2c6 100644
--- a/src/Sentry/Laravel/ServiceProvider.php
+++ b/src/Sentry/Laravel/ServiceProvider.php
@@ -2,11 +2,12 @@
namespace Sentry\Laravel;
+use Illuminate\Contracts\Container\BindingResolutionException;
+use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Http\Kernel as HttpKernelInterface;
use Illuminate\Foundation\Application as Laravel;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Log\LogManager;
-use Laravel\Lumen\Application as Lumen;
use RuntimeException;
use Sentry\ClientBuilder;
use Sentry\ClientBuilderInterface;
@@ -34,7 +35,8 @@ class ServiceProvider extends BaseServiceProvider
'integrations',
// This is kept for backwards compatibility and can be dropped in a future breaking release
'breadcrumbs.sql_bindings',
- // The base namespace for controllers to strip of the beginning of controller class names
+
+ // This config option is no longer in use but to prevent errors when upgrading we leave it here to be discarded
'controllers_base_namespace',
];
@@ -48,10 +50,7 @@ public function boot(): void
if ($this->hasDsnSet()) {
$this->bindEvents();
- if ($this->app instanceof Lumen) {
- $this->app->middleware(SetRequestMiddleware::class);
- $this->app->middleware(SetRequestIpMiddleware::class);
- } elseif ($this->app->bound(HttpKernelInterface::class)) {
+ if ($this->app->bound(HttpKernelInterface::class)) {
/** @var \Illuminate\Foundation\Http\Kernel $httpKernel */
$httpKernel = $this->app->make(HttpKernelInterface::class);
@@ -78,10 +77,6 @@ public function boot(): void
*/
public function register(): void
{
- if ($this->app instanceof Lumen) {
- $this->app->configure(static::$abstract);
- }
-
$this->mergeConfigFrom(__DIR__ . '/../../../config/sentry.php', static::$abstract);
$this->configureAndRegisterClient();
@@ -102,18 +97,25 @@ protected function bindEvents(): void
$handler = new EventHandler($this->app, $userConfig);
- $handler->subscribe();
+ try {
+ /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
+ $dispatcher = $this->app->make(Dispatcher::class);
- if ($this->app->bound('octane')) {
- $handler->subscribeOctaneEvents();
- }
+ $handler->subscribe($dispatcher);
- if ($this->app->bound('queue')) {
- $handler->subscribeQueueEvents($this->app->make('queue'));
- }
+ if ($this->app->bound('octane')) {
+ $handler->subscribeOctaneEvents($dispatcher);
+ }
- if (isset($userConfig['send_default_pii']) && $userConfig['send_default_pii'] !== false) {
- $handler->subscribeAuthEvents();
+ if ($this->app->bound('queue')) {
+ $handler->subscribeQueueEvents($dispatcher, $this->app->make('queue'));
+ }
+
+ if (isset($userConfig['send_default_pii']) && $userConfig['send_default_pii'] !== false) {
+ $handler->subscribeAuthEvents($dispatcher);
+ }
+ } catch (BindingResolutionException $e) {
+ // If we cannot resolve the event dispatcher we also cannot listen to events
}
}
@@ -133,12 +135,6 @@ protected function registerArtisanCommands(): void
*/
protected function configureAndRegisterClient(): void
{
- $userConfig = $this->getUserConfig();
-
- if (isset($userConfig['controllers_base_namespace'])) {
- Integration::setControllersBaseNamespace($userConfig['controllers_base_namespace']);
- }
-
$this->app->bind(ClientBuilderInterface::class, function () {
$basePath = base_path();
$userConfig = $this->getUserConfig();
@@ -169,7 +165,7 @@ protected function configureAndRegisterClient(): void
return $clientBuilder;
});
- $this->app->singleton(HubInterface::class, function ($app) {
+ $this->app->singleton(HubInterface::class, function () {
/** @var \Sentry\ClientBuilderInterface $clientBuilder */
$clientBuilder = $this->app->make(ClientBuilderInterface::class);
@@ -177,7 +173,7 @@ protected function configureAndRegisterClient(): void
$userIntegrations = $this->resolveIntegrationsFromUserConfig();
- $options->setIntegrations(function (array $integrations) use ($options, $userIntegrations, $app) {
+ $options->setIntegrations(function (array $integrations) use ($options, $userIntegrations) {
if ($options->hasDefaultIntegrations()) {
// Remove the default error and fatal exception listeners to let Laravel handle those
// itself. These event are still bubbling up through the documented changes in the users
diff --git a/src/Sentry/Laravel/Tracing/EventHandler.php b/src/Sentry/Laravel/Tracing/EventHandler.php
index 054dc62e..3850d540 100644
--- a/src/Sentry/Laravel/Tracing/EventHandler.php
+++ b/src/Sentry/Laravel/Tracing/EventHandler.php
@@ -3,22 +3,25 @@
namespace Sentry\Laravel\Tracing;
use Exception;
-use Illuminate\Contracts\Container\BindingResolutionException;
-use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Events as DatabaseEvents;
+use Illuminate\Http\Client\Events as HttpClientEvents;
use Illuminate\Queue\Events as QueueEvents;
use Illuminate\Queue\Queue;
use Illuminate\Queue\QueueManager;
+use Illuminate\Routing\Events as RoutingEvents;
use RuntimeException;
use Sentry\Laravel\Integration;
use Sentry\SentrySdk;
+use Sentry\Tracing\Span;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\SpanStatus;
use Sentry\Tracing\TransactionContext;
+use Sentry\Tracing\TransactionSource;
class EventHandler
{
+ public const QUEUE_PAYLOAD_BAGGAGE_DATA = 'sentry_baggage_data';
public const QUEUE_PAYLOAD_TRACE_PARENT_DATA = 'sentry_trace_parent_data';
/**
@@ -27,8 +30,14 @@ class EventHandler
* @var array
*/
protected static $eventHandlerMap = [
- 'illuminate.query' => 'query', // Until Laravel 5.1
- DatabaseEvents\QueryExecuted::class => 'queryExecuted', // Since Laravel 5.2
+ RoutingEvents\RouteMatched::class => 'routeMatched',
+ DatabaseEvents\QueryExecuted::class => 'queryExecuted',
+ HttpClientEvents\RequestSending::class => 'httpClientRequestSending',
+ HttpClientEvents\ResponseReceived::class => 'httpClientResponseReceived',
+ HttpClientEvents\ConnectionFailed::class => 'httpClientConnectionFailed',
+ DatabaseEvents\TransactionBeginning::class => 'transactionBeginning',
+ DatabaseEvents\TransactionCommitted::class => 'transactionCommitted',
+ DatabaseEvents\TransactionRolledBack::class => 'transactionRolledBack',
];
/**
@@ -37,18 +46,11 @@ class EventHandler
* @var array
*/
protected static $queueEventHandlerMap = [
- QueueEvents\JobProcessing::class => 'queueJobProcessing', // Since Laravel 5.2
- QueueEvents\JobProcessed::class => 'queueJobProcessed', // Since Laravel 5.2
- QueueEvents\JobExceptionOccurred::class => 'queueJobExceptionOccurred', // Since Laravel 5.2
+ QueueEvents\JobProcessing::class => 'queueJobProcessing',
+ QueueEvents\JobProcessed::class => 'queueJobProcessed',
+ QueueEvents\JobExceptionOccurred::class => 'queueJobExceptionOccurred',
];
- /**
- * The Laravel container.
- *
- * @var \Illuminate\Contracts\Container\Container
- */
- private $container;
-
/**
* Indicates if we should we add SQL queries as spans.
*
@@ -78,18 +80,18 @@ class EventHandler
private $traceQueueJobsAsTransactions;
/**
- * Holds a reference to the parent queue job span.
+ * Hold the stack of parent spans that need to be put back on the scope.
*
- * @var \Sentry\Tracing\Span|null
+ * @var array
*/
- private $parentQueueJobSpan;
+ private $parentSpanStack = [];
/**
- * Holds a reference to the current queue job span or transaction.
+ * Hold the stack of current spans that need to be finished still.
*
- * @var \Sentry\Tracing\Transaction|\Sentry\Tracing\Span|null
+ * @var array
*/
- private $currentQueueJobSpan;
+ private $currentSpanStack = [];
/**
* The backtrace helper.
@@ -100,78 +102,64 @@ class EventHandler
/**
* EventHandler constructor.
- *
- * @param \Illuminate\Contracts\Container\Container $container
- * @param \Sentry\Laravel\Tracing\BacktraceHelper $backtraceHelper
- * @param array $config
*/
- public function __construct(Container $container, BacktraceHelper $backtraceHelper, array $config)
+ public function __construct(array $config, BacktraceHelper $backtraceHelper)
{
- $this->container = $container;
- $this->backtraceHelper = $backtraceHelper;
-
$this->traceSqlQueries = ($config['sql_queries'] ?? true) === true;
$this->traceSqlQueryOrigins = ($config['sql_origin'] ?? true) === true;
$this->traceQueueJobs = ($config['queue_jobs'] ?? false) === true;
$this->traceQueueJobsAsTransactions = ($config['queue_job_transactions'] ?? false) === true;
+
+ $this->backtraceHelper = $backtraceHelper;
}
/**
* Attach all event handlers.
+ *
+ * @uses self::routeMatchedHandler()
+ * @uses self::queryExecutedHandler()
+ * @uses self::transactionBeginningHandler()
+ * @uses self::transactionCommittedHandler()
+ * @uses self::transactionRolledBackHandler()
+ * @uses self::httpClientRequestSendingHandler()
+ * @uses self::httpClientResponseReceivedHandler()
+ * @uses self::httpClientConnectionFailedHandler()
*/
- public function subscribe(): void
+ public function subscribe(Dispatcher $dispatcher): void
{
- try {
- /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
- $dispatcher = $this->container->make(Dispatcher::class);
-
- foreach (static::$eventHandlerMap as $eventName => $handler) {
- $dispatcher->listen($eventName, [$this, $handler]);
- }
- } catch (BindingResolutionException $e) {
- // If we cannot resolve the event dispatcher we also cannot listen to events
+ foreach (static::$eventHandlerMap as $eventName => $handler) {
+ $dispatcher->listen($eventName, [$this, $handler]);
}
}
/**
* Attach all queue event handlers.
*
- * @param \Illuminate\Queue\QueueManager $queue
+ * @uses self::queueJobProcessingHandler()
+ * @uses self::queueJobProcessedHandler()
+ * @uses self::queueJobExceptionOccurredHandler()
*/
- public function subscribeQueueEvents(QueueManager $queue): void
+ public function subscribeQueueEvents(Dispatcher $dispatcher, QueueManager $queue): void
{
// If both types of queue job tracing is disabled also do not register the events
if (!$this->traceQueueJobs && !$this->traceQueueJobsAsTransactions) {
return;
}
- // The payload create callback was introduced in Laravel 5.7 so we need to guard against older versions
- if (method_exists(Queue::class, 'createPayloadUsing')) {
- Queue::createPayloadUsing(static function (?string $connection, ?string $queue, ?array $payload): ?array {
- $currentSpan = Integration::currentTracingSpan();
+ Queue::createPayloadUsing(static function (?string $connection, ?string $queue, ?array $payload): ?array {
+ $currentSpan = SentrySdk::getCurrentHub()->getSpan();
- if ($currentSpan !== null && $payload !== null) {
- $payload[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] = $currentSpan->toTraceparent();
- }
-
- return $payload;
- });
- }
+ if ($currentSpan !== null && $payload !== null) {
+ $payload[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] = $currentSpan->toTraceparent();
+ $payload[self::QUEUE_PAYLOAD_BAGGAGE_DATA] = $currentSpan->toBaggage();
+ }
- $queue->looping(function () {
- $this->afterQueuedJob();
+ return $payload;
});
- try {
- /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
- $dispatcher = $this->container->make(Dispatcher::class);
-
- foreach (static::$queueEventHandlerMap as $eventName => $handler) {
- $dispatcher->listen($eventName, [$this, $handler]);
- }
- } catch (BindingResolutionException $e) {
- // If we cannot resolve the event dispatcher we also cannot listen to events
+ foreach (static::$queueEventHandlerMap as $eventName => $handler) {
+ $dispatcher->listen($eventName, [$this, $handler]);
}
}
@@ -181,7 +169,7 @@ public function subscribeQueueEvents(QueueManager $queue): void
* @param string $method
* @param array $arguments
*/
- public function __call($method, $arguments)
+ public function __call(string $method, array $arguments)
{
$handlerMethod = "{$method}Handler";
@@ -191,47 +179,32 @@ public function __call($method, $arguments)
try {
call_user_func_array([$this, $handlerMethod], $arguments);
- } catch (Exception $exception) {
- // Ignore
+ } catch (Exception $e) {
+ // Ignore to prevent bubbling up errors in the SDK
}
}
- /**
- * Until Laravel 5.1
- *
- * @param string $query
- * @param array $bindings
- * @param int $time
- * @param string $connectionName
- */
- protected function queryHandler($query, $bindings, $time, $connectionName): void
+ protected function routeMatchedHandler(RoutingEvents\RouteMatched $match): void
{
- $this->recordQuerySpan($query, $time);
- }
+ $transaction = SentrySdk::getCurrentHub()->getTransaction();
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Database\Events\QueryExecuted $query
- */
- protected function queryExecutedHandler(DatabaseEvents\QueryExecuted $query): void
- {
- $this->recordQuerySpan($query->sql, $query->time);
+ if ($transaction === null) {
+ return;
+ }
+
+ [$transactionName, $transactionSource] = Integration::extractNameAndSourceForRoute($match->route);
+
+ $transaction->setName($transactionName);
+ $transaction->getMetadata()->setSource($transactionSource);
}
- /**
- * Helper to add an query breadcrumb.
- *
- * @param string $query
- * @param float|null $time
- */
- private function recordQuerySpan($query, $time): void
+ protected function queryExecutedHandler(DatabaseEvents\QueryExecuted $query): void
{
if (!$this->traceSqlQueries) {
return;
}
- $parentSpan = Integration::currentTracingSpan();
+ $parentSpan = SentrySdk::getCurrentHub()->getSpan();
// If there is no tracing span active there is no need to handle the event
if ($parentSpan === null) {
@@ -240,12 +213,12 @@ private function recordQuerySpan($query, $time): void
$context = new SpanContext();
$context->setOp('db.sql.query');
- $context->setDescription($query);
- $context->setStartTimestamp(microtime(true) - $time / 1000);
- $context->setEndTimestamp($context->getStartTimestamp() + $time / 1000);
+ $context->setDescription($query->sql);
+ $context->setStartTimestamp(microtime(true) - $query->time / 1000);
+ $context->setEndTimestamp($context->getStartTimestamp() + $query->time / 1000);
if ($this->traceSqlQueryOrigins) {
- $queryOrigin = $this->resolveQueryOriginFromBacktrace($context);
+ $queryOrigin = $this->resolveQueryOriginFromBacktrace();
if ($queryOrigin !== null) {
$context->setData(['sql.origin' => $queryOrigin]);
@@ -273,14 +246,79 @@ private function resolveQueryOriginFromBacktrace(): ?string
return "{$filePath}:{$firstAppFrame->getLine()}";
}
- /*
- * Since Laravel 5.2
- *
- * @param \Illuminate\Queue\Events\JobProcessing $event
- */
- protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
+ protected function transactionBeginningHandler(DatabaseEvents\TransactionBeginning $event): void
+ {
+ $parentSpan = SentrySdk::getCurrentHub()->getSpan();
+
+ if ($parentSpan === null) {
+ return;
+ }
+
+ $context = new SpanContext;
+ $context->setOp('db.transaction');
+
+ $this->pushSpan($parentSpan->startChild($context));
+ }
+
+ protected function transactionCommittedHandler(DatabaseEvents\TransactionCommitted $event): void
+ {
+ $span = $this->popSpan();
+
+ if ($span !== null) {
+ $span->finish();
+ $span->setStatus(SpanStatus::ok());
+ }
+ }
+
+ protected function transactionRolledBackHandler(DatabaseEvents\TransactionRolledBack $event): void
+ {
+ $span = $this->popSpan();
+
+ if ($span !== null) {
+ $span->finish();
+ $span->setStatus(SpanStatus::internalError());
+ }
+ }
+
+ protected function httpClientRequestSendingHandler(HttpClientEvents\RequestSending $event): void
+ {
+ $parentSpan = SentrySdk::getCurrentHub()->getSpan();
+
+ if ($parentSpan === null) {
+ return;
+ }
+
+ $context = new SpanContext;
+
+ $context->setOp('http.client');
+ $context->setDescription($event->request->method() . ' ' . $event->request->url());
+
+ $this->pushSpan($parentSpan->startChild($context));
+ }
+
+ protected function httpClientResponseReceivedHandler(HttpClientEvents\ResponseReceived $event): void
+ {
+ $span = $this->popSpan();
+
+ if ($span !== null) {
+ $span->finish();
+ $span->setHttpStatus($event->response->status());
+ }
+ }
+
+ protected function httpClientConnectionFailedHandler(HttpClientEvents\ConnectionFailed $event): void
+ {
+ $span = $this->popSpan();
+
+ if ($span !== null) {
+ $span->finish();
+ $span->setStatus(SpanStatus::internalError());
+ }
+ }
+
+ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event): void
{
- $parentSpan = Integration::currentTracingSpan();
+ $parentSpan = SentrySdk::getCurrentHub()->getSpan();
// If there is no tracing span active and we don't trace jobs as transactions there is no need to handle the event
if ($parentSpan === null && !$this->traceQueueJobsAsTransactions) {
@@ -293,11 +331,10 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
}
if ($parentSpan === null) {
+ $baggage = $event->job->payload()[self::QUEUE_PAYLOAD_BAGGAGE_DATA] ?? null;
$traceParent = $event->job->payload()[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] ?? null;
- $context = $traceParent === null
- ? new TransactionContext
- : TransactionContext::fromSentryTrace($traceParent);
+ $context = TransactionContext::fromHeaders($traceParent ?? '', $baggage ?? '');
// If the parent transaction was not sampled we also stop the queue job from being recorded
if ($context->getParentSampled() === false) {
@@ -307,24 +344,19 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
$context = new SpanContext;
}
+ $resolvedJobName = $event->job->resolveName();
+
$job = [
'job' => $event->job->getName(),
'queue' => $event->job->getQueue(),
+ 'resolved' => $resolvedJobName,
'attempts' => $event->job->attempts(),
'connection' => $event->connectionName,
];
- // Resolve name exists only from Laravel 5.3+
- $resolvedJobName = method_exists($event->job, 'resolveName')
- ? $event->job->resolveName()
- : null;
-
- if ($resolvedJobName !== null) {
- $job['resolved'] = $resolvedJobName;
- }
-
if ($context instanceof TransactionContext) {
- $context->setName($resolvedJobName ?? $event->job->getName());
+ $context->setName($resolvedJobName);
+ $context->setSource(TransactionSource::task());
}
$context->setOp('queue.process');
@@ -333,47 +365,57 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
// When the parent span is null we start a new transaction otherwise we start a child of the current span
if ($parentSpan === null) {
- $this->currentQueueJobSpan = SentrySdk::getCurrentHub()->startTransaction($context);
+ $span = SentrySdk::getCurrentHub()->startTransaction($context);
} else {
- $this->currentQueueJobSpan = $parentSpan->startChild($context);
+ $span = $parentSpan->startChild($context);
}
- $this->parentQueueJobSpan = $parentSpan;
-
- SentrySdk::getCurrentHub()->setSpan($this->currentQueueJobSpan);
+ $this->pushSpan($span);
}
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Queue\Events\JobExceptionOccurred $event
- */
- protected function queueJobExceptionOccurredHandler(QueueEvents\JobExceptionOccurred $event)
+ protected function queueJobExceptionOccurredHandler(QueueEvents\JobExceptionOccurred $event): void
{
$this->afterQueuedJob(SpanStatus::internalError());
}
- /**
- * Since Laravel 5.2
- *
- * @param \Illuminate\Queue\Events\JobProcessed $event
- */
- protected function queueJobProcessedHandler(QueueEvents\JobProcessed $event)
+ protected function queueJobProcessedHandler(QueueEvents\JobProcessed $event): void
{
$this->afterQueuedJob(SpanStatus::ok());
}
private function afterQueuedJob(?SpanStatus $status = null): void
{
- if ($this->currentQueueJobSpan === null) {
+ $span = $this->popSpan();
+
+ if ($span === null) {
return;
}
- $this->currentQueueJobSpan->setStatus($status);
- $this->currentQueueJobSpan->finish();
- $this->currentQueueJobSpan = null;
+ $span->setStatus($status);
+ $span->finish();
+ }
+
+ private function pushSpan(Span $span): void
+ {
+ $hub = SentrySdk::getCurrentHub();
+
+ $this->parentSpanStack[] = $hub->getSpan();
+
+ $hub->setSpan($span);
+
+ $this->currentSpanStack[] = $span;
+ }
+
+ private function popSpan(): ?Span
+ {
+ if (count($this->currentSpanStack) === 0) {
+ return null;
+ }
+
+ $parent = array_pop($this->parentSpanStack);
+
+ SentrySdk::getCurrentHub()->setSpan($parent);
- SentrySdk::getCurrentHub()->setSpan($this->parentQueueJobSpan);
- $this->parentQueueJobSpan = null;
+ return array_pop($this->currentSpanStack);
}
}
diff --git a/src/Sentry/Laravel/Tracing/Integrations/LighthouseIntegration.php b/src/Sentry/Laravel/Tracing/Integrations/LighthouseIntegration.php
index a05f113c..3a36257a 100644
--- a/src/Sentry/Laravel/Tracing/Integrations/LighthouseIntegration.php
+++ b/src/Sentry/Laravel/Tracing/Integrations/LighthouseIntegration.php
@@ -13,6 +13,7 @@
use Sentry\Laravel\Integration;
use Sentry\SentrySdk;
use Sentry\Tracing\SpanContext;
+use Sentry\Tracing\TransactionSource;
class LighthouseIntegration implements IntegrationInterface
{
@@ -56,7 +57,7 @@ public function setupOnce(): void
public function handleStartRequest(StartRequest $startRequest): void
{
- $this->previousSpan = Integration::currentTracingSpan();
+ $this->previousSpan = SentrySdk::getCurrentHub()->getSpan();
if ($this->previousSpan === null) {
return;
@@ -157,7 +158,7 @@ private function updateTransactionName(): void
return;
}
- array_walk($groupedOperations, static function (array &$operations, string $operationType) {
+ array_walk($groupedOperations, static function (&$operations, string $operationType) {
sort($operations, SORT_STRING);
$operations = "{$operationType}{" . implode(',', $operations) . '}';
@@ -168,6 +169,7 @@ private function updateTransactionName(): void
$transactionName = 'lighthouse?' . implode('&', $groupedOperations);
$transaction->setName($transactionName);
+ $transaction->getMetadata()->setSource(TransactionSource::custom());
Integration::setTransaction($transactionName);
}
diff --git a/src/Sentry/Laravel/Tracing/Middleware.php b/src/Sentry/Laravel/Tracing/Middleware.php
index 24eb8341..33d2afe2 100644
--- a/src/Sentry/Laravel/Tracing/Middleware.php
+++ b/src/Sentry/Laravel/Tracing/Middleware.php
@@ -4,14 +4,13 @@
use Closure;
use Illuminate\Http\Request;
-use Illuminate\Routing\Route;
-use Sentry\Laravel\Integration;
use Sentry\SentrySdk;
use Sentry\State\HubInterface;
use Sentry\Tracing\Span;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\TransactionContext;
-use Symfony\Component\HttpFoundation\Response;
+use Sentry\Tracing\TransactionSource;
+use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
class Middleware
{
@@ -44,7 +43,7 @@ class Middleware
*
* @return mixed
*/
- public function handle($request, Closure $next)
+ public function handle(Request $request, Closure $next)
{
if (app()->bound(HubInterface::class)) {
$this->startTransaction($request, app(HubInterface::class));
@@ -56,14 +55,19 @@ public function handle($request, Closure $next)
/**
* Handle the application termination.
*
- * @param \Illuminate\Http\Request $request
- * @param \Symfony\Component\HttpFoundation\Response $response
+ * @param \Illuminate\Http\Request $request
+ * @param mixed $response
*
* @return void
*/
- public function terminate($request, $response): void
+ public function terminate(Request $request, $response): void
{
if ($this->transaction !== null && app()->bound(HubInterface::class)) {
+ // We stop here if a route has not been matched unless we are configured to trace missing routes
+ if (config('sentry.tracing.missing_routes', false) === false && $request->route() === null) {
+ return;
+ }
+
if ($this->appSpan !== null) {
$this->appSpan->finish();
}
@@ -72,11 +76,7 @@ public function terminate($request, $response): void
// If the transaction is not on the scope during finish, the trace.context is wrong
SentrySdk::getCurrentHub()->setSpan($this->transaction);
- if ($request instanceof Request) {
- $this->hydrateRequestData($request);
- }
-
- if ($response instanceof Response) {
+ if ($response instanceof SymfonyResponse) {
$this->hydrateResponseData($response);
}
@@ -90,9 +90,8 @@ public function terminate($request, $response): void
* @param float|null $timestamp The unix timestamp of the booted event, default to `microtime(true)` if not `null`.
*
* @return void
- * @internal This method should only be invoked right after the application has finished "booting":
- * For Laravel this is from the application `booted` callback.
- * For Lumen this is right before returning from the `bootstrap/app.php` file.
+ *
+ * @internal This method should only be invoked right after the application has finished "booting".
*/
public function setBootedTimestamp(?float $timestamp = null): void
{
@@ -102,18 +101,23 @@ public function setBootedTimestamp(?float $timestamp = null): void
private function startTransaction(Request $request, HubInterface $sentry): void
{
$requestStartTime = $request->server('REQUEST_TIME_FLOAT', microtime(true));
- $sentryTraceHeader = $request->header('sentry-trace');
- $context = $sentryTraceHeader
- ? TransactionContext::fromSentryTrace($sentryTraceHeader)
- : new TransactionContext;
+ $context = TransactionContext::fromHeaders(
+ $request->header('sentry-trace', ''),
+ $request->header('baggage', '')
+ );
+
+ $requestPath = '/' . ltrim($request->path(), '/');
$context->setOp('http.server');
+ $context->setName($requestPath);
+ $context->setSource(TransactionSource::url());
+ $context->setStartTimestamp($requestStartTime);
+
$context->setData([
- 'url' => '/' . ltrim($request->path(), '/'),
+ 'url' => $requestPath,
'method' => strtoupper($request->method()),
]);
- $context->setStartTimestamp($requestStartTime);
$this->transaction = $sentry->startTransaction($context);
@@ -122,7 +126,7 @@ private function startTransaction(Request $request, HubInterface $sentry): void
$bootstrapSpan = $this->addAppBootstrapSpan($request);
- $appContextStart = new SpanContext();
+ $appContextStart = new SpanContext;
$appContextStart->setOp('middleware.handle');
$appContextStart->setStartTimestamp($bootstrapSpan ? $bootstrapSpan->getEndTimestamp() : microtime(true));
@@ -143,7 +147,7 @@ private function addAppBootstrapSpan(Request $request): ?Span
return null;
}
- $spanContextStart = new SpanContext();
+ $spanContextStart = new SpanContext;
$spanContextStart->setOp('app.bootstrap');
$spanContextStart->setStartTimestamp($laravelStartTime);
$spanContextStart->setEndTimestamp($this->bootedTimestamp);
@@ -175,56 +179,8 @@ private function addBootDetailTimeSpans(Span $bootstrap): void
$bootstrap->startChild($autoload);
}
- private function hydrateRequestData(Request $request): void
- {
- $route = $request->route();
-
- if ($route instanceof Route) {
- $this->updateTransactionNameIfDefault(
- Integration::extractNameForRoute($route)
- );
-
- $this->transaction->setData([
- 'name' => $route->getName(),
- 'action' => $route->getActionName(),
- 'method' => $request->getMethod(),
- ]);
- } elseif (is_array($route) && count($route) === 3) {
- $this->updateTransactionNameIfDefault(
- Integration::extractNameForLumenRoute($route, $request->path())
- );
-
- $action = $route[1] ?? [];
-
- $this->transaction->setData([
- 'name' => $action['as'] ?? null,
- 'action' => $action['uses'] ?? 'Closure',
- 'method' => $request->getMethod(),
- ]);
- }
-
- $this->updateTransactionNameIfDefault('/' . ltrim($request->path(), '/'));
- }
-
- private function hydrateResponseData(Response $response): void
+ private function hydrateResponseData(SymfonyResponse $response): void
{
$this->transaction->setHttpStatus($response->getStatusCode());
}
-
- private function updateTransactionNameIfDefault(?string $name): void
- {
- // Ignore empty names (and `null`) for caller convenience
- if (empty($name)) {
- return;
- }
-
- // If the transaction already has a name other than the default
- // ignore the new name, this will most occur if the user has set a
- // transaction name themself before the application reaches this point
- if ($this->transaction->getName() !== TransactionContext::DEFAULT_NAME) {
- return;
- }
-
- $this->transaction->setName($name);
- }
}
diff --git a/src/Sentry/Laravel/Tracing/Routing/TracingCallableDispatcherTracing.php b/src/Sentry/Laravel/Tracing/Routing/TracingCallableDispatcherTracing.php
new file mode 100644
index 00000000..7ccb1062
--- /dev/null
+++ b/src/Sentry/Laravel/Tracing/Routing/TracingCallableDispatcherTracing.php
@@ -0,0 +1,24 @@
+dispatcher = $dispatcher;
+ }
+
+ public function dispatch(Route $route, $callable)
+ {
+ return $this->wrapRouteDispatch(function () use ($route, $callable) {
+ return $this->dispatcher->dispatch($route, $callable);
+ }, $route);
+ }
+}
diff --git a/src/Sentry/Laravel/Tracing/Routing/TracingControllerDispatcherTracing.php b/src/Sentry/Laravel/Tracing/Routing/TracingControllerDispatcherTracing.php
new file mode 100644
index 00000000..1cedda2c
--- /dev/null
+++ b/src/Sentry/Laravel/Tracing/Routing/TracingControllerDispatcherTracing.php
@@ -0,0 +1,29 @@
+dispatcher = $dispatcher;
+ }
+
+ public function dispatch(Route $route, $controller, $method)
+ {
+ return $this->wrapRouteDispatch(function () use ($route, $controller, $method) {
+ return $this->dispatcher->dispatch($route, $controller, $method);
+ }, $route);
+ }
+
+ public function getMiddleware($controller, $method)
+ {
+ return $this->dispatcher->getMiddleware($controller, $method);
+ }
+}
diff --git a/src/Sentry/Laravel/Tracing/Routing/TracingRoutingDispatcher.php b/src/Sentry/Laravel/Tracing/Routing/TracingRoutingDispatcher.php
new file mode 100644
index 00000000..98212973
--- /dev/null
+++ b/src/Sentry/Laravel/Tracing/Routing/TracingRoutingDispatcher.php
@@ -0,0 +1,36 @@
+getSpan();
+
+ // When there is no active transaction we can skip doing anything and just immediately return the callable
+ if ($parentSpan === null) {
+ return $dispatch();
+ }
+
+ $context = new SpanContext;
+ $context->setOp('http.route');
+ $context->setDescription($route->getActionName());
+
+ $span = $parentSpan->startChild($context);
+
+ SentrySdk::getCurrentHub()->setSpan($span);
+
+ try {
+ return $dispatch();
+ } finally {
+ $span->finish();
+
+ SentrySdk::getCurrentHub()->setSpan($parentSpan);
+ }
+ }
+}
diff --git a/src/Sentry/Laravel/Tracing/ServiceProvider.php b/src/Sentry/Laravel/Tracing/ServiceProvider.php
index 3235b3c4..cd56d2d4 100644
--- a/src/Sentry/Laravel/Tracing/ServiceProvider.php
+++ b/src/Sentry/Laravel/Tracing/ServiceProvider.php
@@ -2,16 +2,20 @@
namespace Sentry\Laravel\Tracing;
+use Illuminate\Contracts\Container\BindingResolutionException;
+use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Http\Kernel as HttpKernelInterface;
use Illuminate\Contracts\View\Engine;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
-use Illuminate\Queue\QueueManager;
+use Illuminate\Routing\Contracts\CallableDispatcher;
+use Illuminate\Routing\Contracts\ControllerDispatcher;
use Illuminate\View\Engines\EngineResolver;
use Illuminate\View\Factory as ViewFactory;
use InvalidArgumentException;
-use Laravel\Lumen\Application as Lumen;
use Sentry\Laravel\BaseServiceProvider;
+use Sentry\Laravel\Tracing\Routing\TracingCallableDispatcherTracing;
+use Sentry\Laravel\Tracing\Routing\TracingControllerDispatcherTracing;
use Sentry\Serializer\RepresentationSerializer;
class ServiceProvider extends BaseServiceProvider
@@ -29,9 +33,9 @@ public function boot(): void
$this->bindViewEngine($tracingConfig);
- if ($this->app instanceof Lumen) {
- $this->app->middleware(Middleware::class);
- } elseif ($this->app->bound(HttpKernelInterface::class)) {
+ $this->decorateRoutingDispatchers();
+
+ if ($this->app->bound(HttpKernelInterface::class)) {
/** @var \Illuminate\Foundation\Http\Kernel $httpKernel */
$httpKernel = $this->app->make(HttpKernelInterface::class);
@@ -55,27 +59,29 @@ public function register(): void
return new BacktraceHelper($options, new RepresentationSerializer($options));
});
- if (!$this->app instanceof Lumen) {
- $this->app->booted(function () {
- $this->app->make(Middleware::class)->setBootedTimestamp();
- });
- }
+ $this->app->booted(function () {
+ $this->app->make(Middleware::class)->setBootedTimestamp();
+ });
}
private function bindEvents(array $tracingConfig): void
{
$handler = new EventHandler(
- $this->app,
- $this->app->make(BacktraceHelper::class),
- $tracingConfig
+ $tracingConfig,
+ $this->app->make(BacktraceHelper::class)
);
- $handler->subscribe();
+ try {
+ /** @var \Illuminate\Contracts\Events\Dispatcher $dispatcher */
+ $dispatcher = $this->app->make(Dispatcher::class);
- if ($this->app->bound('queue')) {
- $handler->subscribeQueueEvents(
- $this->app->make('queue')
- );
+ $handler->subscribe($dispatcher);
+
+ if ($this->app->bound('queue')) {
+ $handler->subscribeQueueEvents($dispatcher, $this->app->make('queue'));
+ }
+ } catch (BindingResolutionException $e) {
+ // If we cannot resolve the event dispatcher we also cannot listen to events
}
}
@@ -119,4 +125,15 @@ private function wrapViewEngine(Engine $realEngine): Engine
return new ViewEngineDecorator($realEngine, $viewFactory);
}
+
+ private function decorateRoutingDispatchers(): void
+ {
+ $this->app->extend(CallableDispatcher::class, static function (CallableDispatcher $dispatcher) {
+ return new TracingCallableDispatcherTracing($dispatcher);
+ });
+
+ $this->app->extend(ControllerDispatcher::class, static function (ControllerDispatcher $dispatcher) {
+ return new TracingControllerDispatcherTracing($dispatcher);
+ });
+ }
}
diff --git a/src/Sentry/Laravel/Tracing/ViewEngineDecorator.php b/src/Sentry/Laravel/Tracing/ViewEngineDecorator.php
index 79e262b5..a153243f 100644
--- a/src/Sentry/Laravel/Tracing/ViewEngineDecorator.php
+++ b/src/Sentry/Laravel/Tracing/ViewEngineDecorator.php
@@ -4,7 +4,6 @@
use Illuminate\Contracts\View\Engine;
use Illuminate\View\Factory;
-use Sentry\Laravel\Integration;
use Sentry\SentrySdk;
use Sentry\Tracing\SpanContext;
@@ -29,7 +28,7 @@ public function __construct(Engine $engine, Factory $viewFactory)
*/
public function get($path, array $data = []): string
{
- $parentSpan = Integration::currentTracingSpan();
+ $parentSpan = SentrySdk::getCurrentHub()->getSpan();
if ($parentSpan === null) {
return $this->engine->get($path, $data);
diff --git a/test/Sentry/ClientBuilderDecoratorTest.php b/test/Sentry/ClientBuilderDecoratorTest.php
new file mode 100644
index 00000000..dc23ae2f
--- /dev/null
+++ b/test/Sentry/ClientBuilderDecoratorTest.php
@@ -0,0 +1,27 @@
+extend(ClientBuilderInterface::class, function (ClientBuilderInterface $clientBuilder) {
+ $clientBuilder->getOptions()->setEnvironment('from_service_container');
+
+ return $clientBuilder;
+ });
+ }
+
+ public function testClientHasEnvironmentSetFromDecorator(): void
+ {
+ $this->assertEquals(
+ 'from_service_container',
+ $this->getClientFromContainer()->getOptions()->getEnvironment()
+ );
+ }
+}
diff --git a/test/Sentry/CommandInfoInBreadcrumbsTest.php b/test/Sentry/EventHandler/ConsoleEventsTest.php
similarity index 58%
rename from test/Sentry/CommandInfoInBreadcrumbsTest.php
rename to test/Sentry/EventHandler/ConsoleEventsTest.php
index 7947656f..ccad1562 100644
--- a/test/Sentry/CommandInfoInBreadcrumbsTest.php
+++ b/test/Sentry/EventHandler/ConsoleEventsTest.php
@@ -1,19 +1,16 @@
shouldSkip()) {
- $this->markTestSkipped('Laravel version <5.5 does not contain the events tested.');
- }
-
$this->resetApplicationWithConfig([
'sentry.breadcrumbs.command_info' => true,
]);
@@ -28,12 +25,8 @@ public function testCommandInfoAreRecordedWhenEnabled()
$this->assertEquals('--foo=bar', $lastBreadcrumb->getMetadata()['input']);
}
- public function testCommandInfoAreRecordedWhenDisabled()
+ public function testCommandBreadcrumIsNotRecordedWhenDisabled(): void
{
- if ($this->shouldSkip()) {
- $this->markTestSkipped('Laravel version <5.5 does not contain the events tested.');
- }
-
$this->resetApplicationWithConfig([
'sentry.breadcrumbs.command_info' => false,
]);
@@ -45,14 +38,9 @@ public function testCommandInfoAreRecordedWhenDisabled()
$this->assertEmpty($this->getCurrentBreadcrumbs());
}
- private function dispatchCommandStartEvent()
+ private function dispatchCommandStartEvent(): void
{
- $dispatcher = $this->app['events'];
-
- $method = method_exists($dispatcher, 'dispatch') ? 'dispatch' : 'fire';
-
- $this->app['events']->$method(
- CommandStarting::class,
+ $this->dispatchLaravelEvent(
new CommandStarting(
'test:command',
new ArgvInput(['artisan', '--foo=bar']),
@@ -60,9 +48,4 @@ private function dispatchCommandStartEvent()
)
);
}
-
- private function shouldSkip()
- {
- return !class_exists(CommandStarting::class);
- }
}
diff --git a/test/Sentry/EventHandler/DatabaseEventsTest.php b/test/Sentry/EventHandler/DatabaseEventsTest.php
new file mode 100644
index 00000000..0c517298
--- /dev/null
+++ b/test/Sentry/EventHandler/DatabaseEventsTest.php
@@ -0,0 +1,97 @@
+resetApplicationWithConfig([
+ 'sentry.breadcrumbs.sql_queries' => true,
+ ]);
+
+ $this->assertTrue($this->app['config']->get('sentry.breadcrumbs.sql_queries'));
+
+ $this->dispatchLaravelEvent(new QueryExecuted(
+ $query = 'SELECT * FROM breadcrumbs WHERE bindings = ?;',
+ ['1'],
+ 10,
+ $this->getMockedConnection()
+ ));
+
+ $lastBreadcrumb = $this->getLastBreadcrumb();
+
+ $this->assertEquals($query, $lastBreadcrumb->getMessage());
+ }
+
+ public function testSqlBindingsAreRecordedWhenEnabled(): void
+ {
+ $this->resetApplicationWithConfig([
+ 'sentry.breadcrumbs.sql_bindings' => true,
+ ]);
+
+ $this->assertTrue($this->app['config']->get('sentry.breadcrumbs.sql_bindings'));
+
+ $this->dispatchLaravelEvent(new QueryExecuted(
+ $query = 'SELECT * FROM breadcrumbs WHERE bindings = ?;',
+ $bindings = ['1'],
+ 10,
+ $this->getMockedConnection()
+ ));
+
+ $lastBreadcrumb = $this->getLastBreadcrumb();
+
+ $this->assertEquals($query, $lastBreadcrumb->getMessage());
+ $this->assertEquals($bindings, $lastBreadcrumb->getMetadata()['bindings']);
+ }
+
+ public function testSqlQueriesAreRecordedWhenDisabled(): void
+ {
+ $this->resetApplicationWithConfig([
+ 'sentry.breadcrumbs.sql_queries' => false,
+ ]);
+
+ $this->assertFalse($this->app['config']->get('sentry.breadcrumbs.sql_queries'));
+
+ $this->dispatchLaravelEvent(new QueryExecuted(
+ 'SELECT * FROM breadcrumbs WHERE bindings = ?;',
+ ['1'],
+ 10,
+ $this->getMockedConnection()
+ ));
+
+ $this->assertEmpty($this->getCurrentBreadcrumbs());
+ }
+
+ public function testSqlBindingsAreRecordedWhenDisabled(): void
+ {
+ $this->resetApplicationWithConfig([
+ 'sentry.breadcrumbs.sql_bindings' => false,
+ ]);
+
+ $this->assertFalse($this->app['config']->get('sentry.breadcrumbs.sql_bindings'));
+
+ $this->dispatchLaravelEvent(new QueryExecuted(
+ $query = 'SELECT * FROM breadcrumbs WHERE bindings <> ?;',
+ ['1'],
+ 10,
+ $this->getMockedConnection()
+ ));
+
+ $lastBreadcrumb = $this->getLastBreadcrumb();
+
+ $this->assertEquals($query, $lastBreadcrumb->getMessage());
+ $this->assertFalse(isset($lastBreadcrumb->getMetadata()['bindings']));
+ }
+
+ private function getMockedConnection()
+ {
+ return Mockery::mock(Connection::class)
+ ->shouldReceive('getName')->andReturn('test');
+ }
+}
diff --git a/test/Sentry/LaravelLogsInBreadcrumbsTest.php b/test/Sentry/EventHandler/LogEventsTest.php
similarity index 62%
rename from test/Sentry/LaravelLogsInBreadcrumbsTest.php
rename to test/Sentry/EventHandler/LogEventsTest.php
index 6fa09ebd..3f0b19cf 100644
--- a/test/Sentry/LaravelLogsInBreadcrumbsTest.php
+++ b/test/Sentry/EventHandler/LogEventsTest.php
@@ -1,10 +1,13 @@
resetApplicationWithConfig([
'sentry.breadcrumbs.logs' => true,
@@ -12,11 +15,11 @@ public function testLaravelLogsAreRecordedWhenEnabled()
$this->assertTrue($this->app['config']->get('sentry.breadcrumbs.logs'));
- $this->dispatchLaravelEvent('illuminate.log', [
+ $this->dispatchLaravelEvent(new MessageLogged(
$level = 'debug',
$message = 'test message',
- $context = ['1'],
- ]);
+ $context = ['1']
+ ));
$lastBreadcrumb = $this->getLastBreadcrumb();
@@ -25,7 +28,7 @@ public function testLaravelLogsAreRecordedWhenEnabled()
$this->assertEquals($context, $lastBreadcrumb->getMetadata());
}
- public function testLaravelLogsAreRecordedWhenDisabled()
+ public function testLaravelLogsAreRecordedWhenDisabled(): void
{
$this->resetApplicationWithConfig([
'sentry.breadcrumbs.logs' => false,
@@ -33,11 +36,7 @@ public function testLaravelLogsAreRecordedWhenDisabled()
$this->assertFalse($this->app['config']->get('sentry.breadcrumbs.logs'));
- $this->dispatchLaravelEvent('illuminate.log', [
- $level = 'debug',
- $message = 'test message',
- $context = ['1'],
- ]);
+ $this->dispatchLaravelEvent(new MessageLogged('debug', 'test message'));
$this->assertEmpty($this->getCurrentBreadcrumbs());
}
diff --git a/test/Sentry/EventHandlerTest.php b/test/Sentry/EventHandlerTest.php
index 4d1472c8..d5113527 100644
--- a/test/Sentry/EventHandlerTest.php
+++ b/test/Sentry/EventHandlerTest.php
@@ -9,42 +9,41 @@
class EventHandlerTest extends TestCase
{
- use ExpectsException;
-
- public function test_missing_event_handler_throws_exception()
+ public function testMissingEventHandlerThrowsException(): void
{
$handler = new EventHandler($this->app, []);
- $this->safeExpectException(RuntimeException::class);
+ $this->expectException(RuntimeException::class);
+ /** @noinspection PhpUndefinedMethodInspection */
$handler->thisIsNotAHandlerAndShouldThrowAnException();
}
- public function test_all_mapped_event_handlers_exist()
+ public function testAllMappedEventHandlersExist(): void
{
$this->tryAllEventHandlerMethods(
- $this->getStaticPropertyValueFromClass(EventHandler::class, 'eventHandlerMap')
+ $this->getEventHandlerMapFromEventHandler('eventHandlerMap')
);
}
- public function test_all_mapped_auth_event_handlers_exist()
+ public function testAllMappedAuthEventHandlersExist(): void
{
$this->tryAllEventHandlerMethods(
- $this->getStaticPropertyValueFromClass(EventHandler::class, 'authEventHandlerMap')
+ $this->getEventHandlerMapFromEventHandler('authEventHandlerMap')
);
}
- public function test_all_mapped_queue_event_handlers_exist()
+ public function testAllMappedQueueEventHandlersExist(): void
{
$this->tryAllEventHandlerMethods(
- $this->getStaticPropertyValueFromClass(EventHandler::class, 'queueEventHandlerMap')
+ $this->getEventHandlerMapFromEventHandler('queueEventHandlerMap')
);
}
- public function test_all_mapped_octane_event_handlers_exist()
+ public function testAllMappedOctaneEventHandlersExist(): void
{
$this->tryAllEventHandlerMethods(
- $this->getStaticPropertyValueFromClass(EventHandler::class, 'octaneEventHandlerMap')
+ $this->getEventHandlerMapFromEventHandler('octaneEventHandlerMap')
);
}
@@ -61,12 +60,12 @@ private function tryAllEventHandlerMethods(array $methods): void
}
}
- private function getStaticPropertyValueFromClass($className, $attributeName)
+ private function getEventHandlerMapFromEventHandler($eventHandlerMapName)
{
- $class = new ReflectionClass($className);
+ $class = new ReflectionClass(EventHandler::class);
$attributes = $class->getStaticProperties();
- return $attributes[$attributeName];
+ return $attributes[$eventHandlerMapName];
}
}
diff --git a/test/Sentry/ExpectsException.php b/test/Sentry/ExpectsException.php
deleted file mode 100644
index 67f657b7..00000000
--- a/test/Sentry/ExpectsException.php
+++ /dev/null
@@ -1,25 +0,0 @@
-expectException($class);
-
- return;
- }
-
- if (method_exists($this, 'setExpectedException')) {
- $this->setExpectedException($class);
-
- return;
- }
-
- throw new RuntimeException('Could not expect an exception.');
- }
-}
diff --git a/test/Sentry/Integration/ExceptionContextIntegrationTest.php b/test/Sentry/Integration/ExceptionContextIntegrationTest.php
index 664bab35..2adeb583 100644
--- a/test/Sentry/Integration/ExceptionContextIntegrationTest.php
+++ b/test/Sentry/Integration/ExceptionContextIntegrationTest.php
@@ -6,12 +6,11 @@
use Sentry\Event;
use Sentry\EventHint;
use Sentry\Laravel\Integration\ExceptionContextIntegration;
-use Sentry\Laravel\Tests\SentryLaravelTestCase;
-use Sentry\SentrySdk;
+use Sentry\Laravel\Tests\TestCase;
use Sentry\State\Scope;
use function Sentry\withScope;
-class ExceptionContextIntegrationTest extends SentryLaravelTestCase
+class ExceptionContextIntegrationTest extends TestCase
{
public function testExceptionContextIntegrationIsRegistered(): void
{
@@ -58,7 +57,7 @@ public function invokeDataProvider(): iterable
];
}
- private function generateExceptionWithContext($context)
+ private function generateExceptionWithContext($context): Exception
{
return new class($context) extends Exception {
private $context;
diff --git a/test/Sentry/IntegrationTest.php b/test/Sentry/IntegrationTest.php
index c116bfc2..590ca4ca 100644
--- a/test/Sentry/IntegrationTest.php
+++ b/test/Sentry/IntegrationTest.php
@@ -9,9 +9,10 @@
use Sentry\Event;
use Sentry\Laravel\Integration;
use Sentry\State\Scope;
+use Sentry\Tracing\TransactionSource;
use function Sentry\withScope;
-class IntegrationTest extends SentryLaravelTestCase
+class IntegrationTest extends TestCase
{
public function testIntegrationIsRegistered(): void
{
@@ -22,10 +23,6 @@ public function testIntegrationIsRegistered(): void
public function testTransactionIsSetWhenRouteMatchedEventIsFired(): void
{
- if (!class_exists(RouteMatched::class)) {
- $this->markTestSkipped('RouteMatched event class does not exist on this version of Laravel.');
- }
-
Integration::setTransaction(null);
$event = new RouteMatched(
@@ -38,17 +35,6 @@ public function testTransactionIsSetWhenRouteMatchedEventIsFired(): void
$this->assertSame($routeUrl, Integration::getTransaction());
}
- public function testTransactionIsSetWhenRouterMatchedEventIsFired(): void
- {
- Integration::setTransaction(null);
-
- $this->dispatchLaravelEvent('router.matched', [
- new Route('GET', $routeUrl = '/sentry-router-matched-event', []),
- ]);
-
- $this->assertSame($routeUrl, Integration::getTransaction());
- }
-
public function testTransactionIsAppliedToEventWithoutTransaction(): void
{
Integration::setTransaction($transaction = 'some-transaction-name');
@@ -103,36 +89,11 @@ public function testTransactionIsNotAppliedToEventWhenTransactionIsAlreadySet():
});
}
- public function testExtractingNameForRouteWithName(): void
- {
- $route = (new Route('GET', '/foo', []))->name($routeName = 'foo-bar');
-
- $this->assertSame($routeName, Integration::extractNameForRoute($route));
- }
-
- public function testExtractingNameForRouteWithAction(): void
- {
- $route = (new Route('GET', '/foo', [
- 'controller' => $controller = 'SomeController@someAction',
- ]));
-
- $this->assertSame($controller, Integration::extractNameForRoute($route));
- }
-
public function testExtractingNameForRouteWithoutName(): void
{
$route = new Route('GET', $url = '/foo', []);
- $this->assertSame($url, Integration::extractNameForRoute($route));
- }
-
- public function testExtractingNameForRouteWithActionAndName(): void
- {
- $route = (new Route('GET', '/foo', [
- 'controller' => 'SomeController@someAction',
- ]))->name($routeName = 'foo-bar');
-
- $this->assertSame($routeName, Integration::extractNameForRoute($route));
+ $this->assetRouteNameAndSource($route, $url, TransactionSource::route());
}
public function testExtractingNameForRouteWithAutoGeneratedName(): void
@@ -140,105 +101,21 @@ public function testExtractingNameForRouteWithAutoGeneratedName(): void
// We fake a generated name here, Laravel generates them each starting with `generated::`
$route = (new Route('GET', $url = '/foo', []))->name('generated::KoAePbpBofo01ey4');
- $this->assertSame($url, Integration::extractNameForRoute($route));
+ $this->assetRouteNameAndSource($route, $url, TransactionSource::route());
}
public function testExtractingNameForRouteWithIncompleteGroupName(): void
{
$route = (new Route('GET', $url = '/foo', []))->name('group-name.');
- $this->assertSame($url, Integration::extractNameForRoute($route));
- }
-
- public function testExtractingNameForRouteWithStrippedBaseNamespaceFromAction(): void
- {
- Integration::setControllersBaseNamespace('BaseNamespace');
-
- $route = (new Route('GET', '/foo', [
- 'controller' => 'BaseNamespace\\SomeController@someAction',
- ]));
-
- $this->assertSame('SomeController@someAction', Integration::extractNameForRoute($route));
-
- Integration::setControllersBaseNamespace(null);
- }
-
- public function testExtractingNameForLumenRouteWithName(): void
- {
- $route = [0, ['as' => $routeName = 'foo-bar'], []];
-
- $this->assertSame($routeName, Integration::extractNameForLumenRoute($route, '/some-route'));
+ $this->assetRouteNameAndSource($route, $url, TransactionSource::route());
}
- public function testExtractingNameForLumenRouteWithAction(): void
+ private function assetRouteNameAndSource(Route $route, string $expectedName, TransactionSource $expectedSource): void
{
- $route = [0, ['uses' => $controller = 'SomeController@someAction'], []];
-
- $this->assertSame($controller, Integration::extractNameForLumenRoute($route, '/some-route'));
- }
-
- public function testExtractingNameForLumenRouteWithoutName(): void
- {
- $url = '/some-route';
-
- $this->assertSame($url, Integration::extractNameForLumenRoute([0, [], []], $url));
- }
-
- public function testExtractingNameForLumenRouteWithParamInUrl(): void
- {
- $route = [1, [], ['param1' => 'foo']];
-
- $url = '/foo/bar/baz';
-
- $this->assertSame('/{param1}/bar/baz', Integration::extractNameForLumenRoute($route, $url));
- }
-
- public function testExtractingNameForLumenRouteWithParamsInUrl(): void
- {
- $route = [1, [], ['param1' => 'foo', 'param2' => 'bar']];
-
- $url = '/foo/bar/baz';
-
- $this->assertSame('/{param1}/{param2}/baz', Integration::extractNameForLumenRoute($route, $url));
- }
-
- public function testExtractingNameForLumenRouteWithActionAndName(): void
- {
- $route = [0, [
- 'as' => $routeName = 'foo-bar',
- 'uses' => 'SomeController@someAction',
- ], []];
-
- $this->assertSame($routeName, Integration::extractNameForLumenRoute($route, '/some-route'));
- }
-
- public function testExtractingNameForLumenRouteWithAutoGeneratedName(): void
- {
- // We fake a generated name here, Laravel generates them each starting with `generated::`
- $route = [0, ['as' => 'generated::KoAePbpBofo01ey4'], []];
-
- $url = '/some-route';
-
- $this->assertSame($url, Integration::extractNameForLumenRoute($route, $url));
- }
-
- public function testExtractingNameForLumenRouteWithIncompleteGroupName(): void
- {
- $route = [0, ['as' => 'group-name.'], []];
-
- $url = '/some-route';
-
- $this->assertSame($url, Integration::extractNameForLumenRoute($route, $url));
- }
-
- public function testExtractingNameForLumenRouteWithStrippedBaseNamespaceFromAction(): void
- {
- Integration::setControllersBaseNamespace('BaseNamespace');
-
- $route = [0, ['uses' => 'BaseNamespace\\SomeController@someAction'], []];
-
- $this->assertSame('SomeController@someAction', Integration::extractNameForLumenRoute($route, '/some-route'));
+ [$actualName, $actualSource] = Integration::extractNameAndSourceForRoute($route);
- Integration::setControllersBaseNamespace(null);
+ $this->assertSame($expectedName, $actualName);
+ $this->assertSame($expectedSource, $actualSource);
}
}
diff --git a/test/Sentry/IntegrationsOptionTest.php b/test/Sentry/Laravel/LaravelIntegrationsOptionTest.php
similarity index 52%
rename from test/Sentry/IntegrationsOptionTest.php
rename to test/Sentry/Laravel/LaravelIntegrationsOptionTest.php
index eb47b129..fc5ed404 100644
--- a/test/Sentry/IntegrationsOptionTest.php
+++ b/test/Sentry/Laravel/LaravelIntegrationsOptionTest.php
@@ -1,19 +1,18 @@
resetApplicationWithConfig([
'sentry.integrations' => [
@@ -30,10 +29,10 @@ public function testCustomIntegrationIsResolvedFromContainerByAlias()
],
]);
- $this->assertNotNull($this->getHubFromContainer()->getClient()->getIntegration(IntegrationsOptionTestIntegrationStub::class));
+ $this->assertNotNull($this->getClientFromContainer()->getIntegration(IntegrationsOptionTestIntegrationStub::class));
}
- public function testCustomIntegrationIsResolvedFromContainerByClass()
+ public function testCustomIntegrationIsResolvedFromContainerByClass(): void
{
$this->resetApplicationWithConfig([
'sentry.integrations' => [
@@ -41,10 +40,10 @@ public function testCustomIntegrationIsResolvedFromContainerByClass()
],
]);
- $this->assertNotNull($this->getHubFromContainer()->getClient()->getIntegration(IntegrationsOptionTestIntegrationStub::class));
+ $this->assertNotNull($this->getClientFromContainer()->getIntegration(IntegrationsOptionTestIntegrationStub::class));
}
- public function testCustomIntegrationByInstance()
+ public function testCustomIntegrationByInstance(): void
{
$this->resetApplicationWithConfig([
'sentry.integrations' => [
@@ -52,15 +51,12 @@ public function testCustomIntegrationByInstance()
],
]);
- $this->assertNotNull($this->getHubFromContainer()->getClient()->getIntegration(IntegrationsOptionTestIntegrationStub::class));
+ $this->assertNotNull($this->getClientFromContainer()->getIntegration(IntegrationsOptionTestIntegrationStub::class));
}
- /**
- * Throws \ReflectionException in <=5.8 and \Illuminate\Contracts\Container\BindingResolutionException since 6.0
- */
- public function testCustomIntegrationThrowsExceptionIfNotResolvable()
+ public function testCustomIntegrationThrowsExceptionIfNotResolvable(): void
{
- $this->safeExpectException(Exception::class);
+ $this->expectException(BindingResolutionException::class);
$this->resetApplicationWithConfig([
'sentry.integrations' => [
@@ -69,9 +65,9 @@ public function testCustomIntegrationThrowsExceptionIfNotResolvable()
]);
}
- public function testIncorrectIntegrationEntryThrowsException()
+ public function testIncorrectIntegrationEntryThrowsException(): void
{
- $this->safeExpectException(RuntimeException::class);
+ $this->expectException(RuntimeException::class);
$this->resetApplicationWithConfig([
'sentry.integrations' => [
@@ -81,18 +77,16 @@ static function () {
]);
}
- public function testDisabledIntegrationsAreNotPresent()
+ public function testDisabledIntegrationsAreNotPresent(): void
{
- $integrations = $this->getHubFromContainer()->getClient()->getOptions()->getIntegrations();
-
- foreach ($integrations as $integration) {
- $this->ensureIsNotDisabledIntegration($integration);
- }
+ $client = $this->getClientFromContainer();
- $this->assertTrue(true, 'Not all disabled integrations are actually disabled.');
+ $this->assertNull($client->getIntegration(ErrorListenerIntegration::class));
+ $this->assertNull($client->getIntegration(ExceptionListenerIntegration::class));
+ $this->assertNull($client->getIntegration(FatalErrorListenerIntegration::class));
}
- public function testDisabledIntegrationsAreNotPresentWithCustomIntegrations()
+ public function testDisabledIntegrationsAreNotPresentWithCustomIntegrations(): void
{
$this->resetApplicationWithConfig([
'sentry.integrations' => [
@@ -100,10 +94,13 @@ public function testDisabledIntegrationsAreNotPresentWithCustomIntegrations()
],
]);
- $this->assertNotNull($this->getHubFromContainer()->getClient()->getIntegration(IntegrationsOptionTestIntegrationStub::class));
- $this->assertNull($this->getHubFromContainer()->getClient()->getIntegration(ErrorListenerIntegration::class));
- $this->assertNull($this->getHubFromContainer()->getClient()->getIntegration(ExceptionListenerIntegration::class));
- $this->assertNull($this->getHubFromContainer()->getClient()->getIntegration(FatalErrorListenerIntegration::class));
+ $client = $this->getClientFromContainer();
+
+ $this->assertNotNull($client->getIntegration(IntegrationsOptionTestIntegrationStub::class));
+
+ $this->assertNull($client->getIntegration(ErrorListenerIntegration::class));
+ $this->assertNull($client->getIntegration(ExceptionListenerIntegration::class));
+ $this->assertNull($client->getIntegration(FatalErrorListenerIntegration::class));
}
}
diff --git a/test/Sentry/Laravel/LogChannelTest.php b/test/Sentry/LogChannelTest.php
similarity index 54%
rename from test/Sentry/Laravel/LogChannelTest.php
rename to test/Sentry/LogChannelTest.php
index 26727660..c2514438 100644
--- a/test/Sentry/Laravel/LogChannelTest.php
+++ b/test/Sentry/LogChannelTest.php
@@ -1,48 +1,36 @@
skipIfLogManagerNotAvailable();
-
$logChannel = new LogChannel($this->app);
- $logger = $logChannel([]);
+
+ $logger = $logChannel();
$this->assertContainsOnlyInstancesOf(SentryHandler::class, $logger->getHandlers());
}
- public function test_creating_handler_with_action_level_config()
+ public function testCreatingHandlerWithActionLevelConfig(): void
{
- $this->skipIfLogManagerNotAvailable();
-
$logChannel = new LogChannel($this->app);
+
$logger = $logChannel(['action_level' => 'critical']);
$this->assertContainsOnlyInstancesOf(FingersCrossedHandler::class, $logger->getHandlers());
$currentHandler = current($logger->getHandlers());
+
$this->assertInstanceOf(SentryHandler::class, $currentHandler->getHandler());
$loggerWithoutActionLevel = $logChannel(['action_level' => null]);
$this->assertContainsOnlyInstancesOf(SentryHandler::class, $loggerWithoutActionLevel->getHandlers());
}
-
- private function skipIfLogManagerNotAvailable()
- {
- if (class_exists(LogManager::class)) {
- return;
- }
-
- $this->markTestSkipped('Laravel version <=5.5 does not contain the LogManager required for this functionality.');
- }
}
diff --git a/test/Sentry/ServiceClientBuilderDecoratorTest.php b/test/Sentry/ServiceClientBuilderDecoratorTest.php
deleted file mode 100644
index 93342b85..00000000
--- a/test/Sentry/ServiceClientBuilderDecoratorTest.php
+++ /dev/null
@@ -1,35 +0,0 @@
-set('sentry.dsn', 'http://publickey:secretkey@sentry.dev/123');
-
- $app->extend(ClientBuilderInterface::class, function (ClientBuilderInterface $clientBuilder) {
- $clientBuilder->getOptions()->setEnvironment('from_service_container');
-
- return $clientBuilder;
- });
- }
-
- protected function getPackageProviders($app)
- {
- return [
- ServiceProvider::class,
- ];
- }
-
- public function testClientHasCustomSerializer()
- {
- /** @var \Sentry\Options $options */
- $options = $this->app->make('sentry')->getClient()->getOptions();
-
- $this->assertEquals('from_service_container', $options->getEnvironment());
- }
-}
diff --git a/test/Sentry/ServiceProviderTest.php b/test/Sentry/ServiceProviderTest.php
index 218d3121..afd95ba3 100644
--- a/test/Sentry/ServiceProviderTest.php
+++ b/test/Sentry/ServiceProviderTest.php
@@ -2,43 +2,44 @@
namespace Sentry\Laravel\Tests;
+use Orchestra\Testbench\TestCase;
use Sentry\Laravel\Facade;
use Sentry\Laravel\ServiceProvider;
use Sentry\State\HubInterface;
-class ServiceProviderTest extends \Orchestra\Testbench\TestCase
+class ServiceProviderTest extends TestCase
{
- protected function getEnvironmentSetUp($app)
+ protected function getEnvironmentSetUp($app): void
{
- $app['config']->set('sentry.dsn', 'http://publickey:secretkey@sentry.dev/123');
+ $app['config']->set('sentry.dsn', 'https://publickey:secretkey@sentry.dev/123');
$app['config']->set('sentry.error_types', E_ALL ^ E_DEPRECATED ^ E_USER_DEPRECATED);
}
- protected function getPackageProviders($app)
+ protected function getPackageProviders($app): array
{
return [
ServiceProvider::class,
];
}
- protected function getPackageAliases($app)
+ protected function getPackageAliases($app): array
{
return [
'Sentry' => Facade::class,
];
}
- public function testIsBound()
+ public function testIsBound(): void
{
$this->assertTrue(app()->bound('sentry'));
- $this->assertInstanceOf(HubInterface::class, app('sentry'));
$this->assertSame(app('sentry'), Facade::getFacadeRoot());
+ $this->assertInstanceOf(HubInterface::class, app('sentry'));
}
/**
* @depends testIsBound
*/
- public function testEnvironment()
+ public function testEnvironment(): void
{
$this->assertEquals('testing', app('sentry')->getClient()->getOptions()->getEnvironment());
}
@@ -46,12 +47,12 @@ public function testEnvironment()
/**
* @depends testIsBound
*/
- public function testDsnWasSetFromConfig()
+ public function testDsnWasSetFromConfig(): void
{
/** @var \Sentry\Options $options */
$options = app('sentry')->getClient()->getOptions();
- $this->assertEquals('http://sentry.dev', $options->getDsn()->getScheme() . '://' . $options->getDsn()->getHost());
+ $this->assertEquals('https://sentry.dev', $options->getDsn()->getScheme() . '://' . $options->getDsn()->getHost());
$this->assertEquals(123, $options->getDsn()->getProjectId());
$this->assertEquals('publickey', $options->getDsn()->getPublicKey());
$this->assertEquals('secretkey', $options->getDsn()->getSecretKey());
@@ -60,7 +61,7 @@ public function testDsnWasSetFromConfig()
/**
* @depends testIsBound
*/
- public function testErrorTypesWasSetFromConfig()
+ public function testErrorTypesWasSetFromConfig(): void
{
$this->assertEquals(
E_ALL ^ E_DEPRECATED ^ E_USER_DEPRECATED,
diff --git a/test/Sentry/ServiceProviderWithCustomAliasTest.php b/test/Sentry/ServiceProviderWithCustomAliasTest.php
index 2f44f3d7..c47b928e 100644
--- a/test/Sentry/ServiceProviderWithCustomAliasTest.php
+++ b/test/Sentry/ServiceProviderWithCustomAliasTest.php
@@ -2,33 +2,34 @@
namespace Sentry\Laravel\Tests;
+use Orchestra\Testbench\TestCase;
use Sentry\Laravel\Facade;
use Sentry\Laravel\ServiceProvider;
use Sentry\State\HubInterface;
-class ServiceProviderWithCustomAliasTest extends \Orchestra\Testbench\TestCase
+class ServiceProviderWithCustomAliasTest extends TestCase
{
- protected function getEnvironmentSetUp($app)
+ protected function getEnvironmentSetUp($app): void
{
$app['config']->set('custom-sentry.dsn', 'http://publickey:secretkey@sentry.dev/123');
$app['config']->set('custom-sentry.error_types', E_ALL ^ E_DEPRECATED ^ E_USER_DEPRECATED);
}
- protected function getPackageProviders($app)
+ protected function getPackageProviders($app): array
{
return [
CustomSentryServiceProvider::class,
];
}
- protected function getPackageAliases($app)
+ protected function getPackageAliases($app): array
{
return [
'CustomSentry' => CustomSentryFacade::class,
];
}
- public function testIsBound()
+ public function testIsBound(): void
{
$this->assertTrue(app()->bound('custom-sentry'));
$this->assertInstanceOf(HubInterface::class, app('custom-sentry'));
@@ -38,7 +39,7 @@ public function testIsBound()
/**
* @depends testIsBound
*/
- public function testEnvironment()
+ public function testEnvironment(): void
{
$this->assertEquals('testing', app('custom-sentry')->getClient()->getOptions()->getEnvironment());
}
@@ -46,7 +47,7 @@ public function testEnvironment()
/**
* @depends testIsBound
*/
- public function testDsnWasSetFromConfig()
+ public function testDsnWasSetFromConfig(): void
{
/** @var \Sentry\Options $options */
$options = app('custom-sentry')->getClient()->getOptions();
@@ -60,7 +61,7 @@ public function testDsnWasSetFromConfig()
/**
* @depends testIsBound
*/
- public function testErrorTypesWasSetFromConfig()
+ public function testErrorTypesWasSetFromConfig(): void
{
$this->assertEquals(
E_ALL ^ E_DEPRECATED ^ E_USER_DEPRECATED,
@@ -76,7 +77,7 @@ class CustomSentryServiceProvider extends ServiceProvider
class CustomSentryFacade extends Facade
{
- protected static function getFacadeAccessor()
+ protected static function getFacadeAccessor(): string
{
return 'custom-sentry';
}
diff --git a/test/Sentry/ServiceProviderWithEnvironmentFromConfigTest.php b/test/Sentry/ServiceProviderWithEnvironmentFromConfigTest.php
index 6866b425..0c1e0a00 100644
--- a/test/Sentry/ServiceProviderWithEnvironmentFromConfigTest.php
+++ b/test/Sentry/ServiceProviderWithEnvironmentFromConfigTest.php
@@ -2,36 +2,36 @@
namespace Sentry;
-use Sentry\Laravel\Tests\SentryLaravelTestCase;
+use Sentry\Laravel\Tests\TestCase;
-class ServiceProviderWithEnvironmentFromConfigTest extends SentryLaravelTestCase
+class ServiceProviderWithEnvironmentFromConfigTest extends TestCase
{
- public function testSentryEnvironmentDefaultsToLaravelEnvironment()
+ public function testSentryEnvironmentDefaultsToLaravelEnvironment(): void
{
$this->assertEquals('testing', app()->environment());
}
- public function testEmptySentryEnvironmentDefaultsToLaravelEnvironment()
+ public function testEmptySentryEnvironmentDefaultsToLaravelEnvironment(): void
{
$this->resetApplicationWithConfig([
'sentry.environment' => '',
]);
- $this->assertEquals('testing', $this->getHubFromContainer()->getClient()->getOptions()->getEnvironment());
+ $this->assertEquals('testing', $this->getClientFromContainer()->getOptions()->getEnvironment());
$this->resetApplicationWithConfig([
'sentry.environment' => null,
]);
- $this->assertEquals('testing', $this->getHubFromContainer()->getClient()->getOptions()->getEnvironment());
+ $this->assertEquals('testing', $this->getClientFromContainer()->getOptions()->getEnvironment());
}
- public function testSentryEnvironmentDefaultGetsOverriddenByConfig()
+ public function testSentryEnvironmentDefaultGetsOverriddenByConfig(): void
{
$this->resetApplicationWithConfig([
- 'sentry.environment' => 'not_testing',
+ 'sentry.environment' => 'override_env',
]);
- $this->assertEquals('not_testing', $this->getHubFromContainer()->getClient()->getOptions()->getEnvironment());
+ $this->assertEquals('override_env', $this->getClientFromContainer()->getOptions()->getEnvironment());
}
}
diff --git a/test/Sentry/ServiceProviderWithoutDsnTest.php b/test/Sentry/ServiceProviderWithoutDsnTest.php
index 247a3d6f..60c26ae9 100644
--- a/test/Sentry/ServiceProviderWithoutDsnTest.php
+++ b/test/Sentry/ServiceProviderWithoutDsnTest.php
@@ -7,19 +7,19 @@
class ServiceProviderWithoutDsnTest extends \Orchestra\Testbench\TestCase
{
- protected function getEnvironmentSetUp($app)
+ protected function getEnvironmentSetUp($app): void
{
$app['config']->set('sentry.dsn', null);
}
- protected function getPackageProviders($app)
+ protected function getPackageProviders($app): array
{
return [
ServiceProvider::class,
];
}
- public function testIsBound()
+ public function testIsBound(): void
{
$this->assertTrue(app()->bound('sentry'));
}
@@ -27,7 +27,7 @@ public function testIsBound()
/**
* @depends testIsBound
*/
- public function testDsnIsNotSet()
+ public function testDsnIsNotSet(): void
{
$this->assertNull(app('sentry')->getClient()->getOptions()->getDsn());
}
@@ -35,8 +35,8 @@ public function testDsnIsNotSet()
/**
* @depends testIsBound
*/
- public function testDidNotRegisterEvents()
+ public function testDidNotRegisterEvents(): void
{
- $this->assertEquals(false, app('events')->hasListeners('router.matched') && app('events')->hasListeners(RouteMatched::class));
+ $this->assertEquals(false, app('events')->hasListeners(RouteMatched::class));
}
}
diff --git a/test/Sentry/SqlBindingsInBreadcrumbsTest.php b/test/Sentry/SqlBindingsInBreadcrumbsTest.php
deleted file mode 100644
index 65affa33..00000000
--- a/test/Sentry/SqlBindingsInBreadcrumbsTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-resetApplicationWithConfig([
- 'sentry.breadcrumbs.sql_bindings' => true,
- ]);
-
- $this->assertTrue($this->app['config']->get('sentry.breadcrumbs.sql_bindings'));
-
- $this->dispatchLaravelEvent('illuminate.query', [
- $query = 'SELECT * FROM breadcrumbs WHERE bindings = ?;',
- $bindings = ['1'],
- 10,
- 'test',
- ]);
-
- $lastBreadcrumb = $this->getLastBreadcrumb();
-
- $this->assertEquals($query, $lastBreadcrumb->getMessage());
- $this->assertEquals($bindings, $lastBreadcrumb->getMetadata()['bindings']);
- }
-
- public function testSqlBindingsAreRecordedWhenDisabled()
- {
- $this->resetApplicationWithConfig([
- 'sentry.breadcrumbs.sql_bindings' => false,
- ]);
-
- $this->assertFalse($this->app['config']->get('sentry.breadcrumbs.sql_bindings'));
-
- $this->dispatchLaravelEvent('illuminate.query', [
- $query = 'SELECT * FROM breadcrumbs WHERE bindings <> ?;',
- ['1'],
- 10,
- 'test',
- ]);
-
- $lastBreadcrumb = $this->getLastBreadcrumb();
-
- $this->assertEquals($query, $lastBreadcrumb->getMessage());
- $this->assertFalse(isset($lastBreadcrumb->getMetadata()['bindings']));
- }
-}
diff --git a/test/Sentry/SqlBindingsInBreadcrumbsWithOldConfigKeyDisabledTest.php b/test/Sentry/SqlBindingsInBreadcrumbsWithOldConfigKeyDisabledTest.php
deleted file mode 100644
index c60295bf..00000000
--- a/test/Sentry/SqlBindingsInBreadcrumbsWithOldConfigKeyDisabledTest.php
+++ /dev/null
@@ -1,36 +0,0 @@
-all();
-
- $config['sentry']['breadcrumbs.sql_bindings'] = false;
-
- $app['config'] = new Repository($config);
- }
-
- public function testSqlBindingsAreRecordedWhenDisabledByOldConfigKey()
- {
- $this->assertFalse($this->app['config']->get('sentry')['breadcrumbs.sql_bindings']);
-
- $this->dispatchLaravelEvent('illuminate.query', [
- $query = 'SELECT * FROM breadcrumbs WHERE bindings = ?;',
- ['1'],
- 10,
- 'test',
- ]);
-
- $lastBreadcrumb = $this->getLastBreadcrumb();
-
- $this->assertEquals($query, $lastBreadcrumb->getMessage());
- $this->assertFalse(isset($lastBreadcrumb->getMetadata()['bindings']));
- }
-}
diff --git a/test/Sentry/SqlBindingsInBreadcrumbsWithOldConfigKeyEnabledTest.php b/test/Sentry/SqlBindingsInBreadcrumbsWithOldConfigKeyEnabledTest.php
deleted file mode 100644
index 3850ac32..00000000
--- a/test/Sentry/SqlBindingsInBreadcrumbsWithOldConfigKeyEnabledTest.php
+++ /dev/null
@@ -1,36 +0,0 @@
-all();
-
- $config['sentry']['breadcrumbs.sql_bindings'] = true;
-
- $app['config'] = new Repository($config);
- }
-
- public function testSqlBindingsAreRecordedWhenEnabledByOldConfigKey()
- {
- $this->assertTrue($this->app['config']->get('sentry')['breadcrumbs.sql_bindings']);
-
- $this->dispatchLaravelEvent('illuminate.query', [
- $query = 'SELECT * FROM breadcrumbs WHERE bindings = ?;',
- $bindings = ['1'],
- 10,
- 'test',
- ]);
-
- $lastBreadcrumb = $this->getLastBreadcrumb();
-
- $this->assertEquals($query, $lastBreadcrumb->getMessage());
- $this->assertEquals($bindings, $lastBreadcrumb->getMetadata()['bindings']);
- }
-}
diff --git a/test/Sentry/SqlQueriesInBreadcrumbsTest.php b/test/Sentry/SqlQueriesInBreadcrumbsTest.php
deleted file mode 100644
index 2d718e95..00000000
--- a/test/Sentry/SqlQueriesInBreadcrumbsTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-resetApplicationWithConfig([
- 'sentry.breadcrumbs.sql_queries' => true,
- ]);
-
- $this->assertTrue($this->app['config']->get('sentry.breadcrumbs.sql_queries'));
-
- $this->dispatchLaravelEvent('illuminate.query', [
- $query = 'SELECT * FROM breadcrumbs WHERE bindings = ?;',
- ['1'],
- 10,
- 'test',
- ]);
-
- $lastBreadcrumb = $this->getLastBreadcrumb();
-
- $this->assertEquals($query, $lastBreadcrumb->getMessage());
- }
-
- public function testSqlQueriesAreRecordedWhenDisabled()
- {
- $this->resetApplicationWithConfig([
- 'sentry.breadcrumbs.sql_queries' => false,
- ]);
-
- $this->assertFalse($this->app['config']->get('sentry.breadcrumbs.sql_queries'));
-
- $this->dispatchLaravelEvent('illuminate.query', [
- 'SELECT * FROM breadcrumbs WHERE bindings <> ?;',
- ['1'],
- 10,
- 'test',
- ]);
-
- $this->assertEmpty($this->getCurrentBreadcrumbs());
- }
-}
diff --git a/test/Sentry/SentryLaravelTestCase.php b/test/Sentry/TestCase.php
similarity index 81%
rename from test/Sentry/SentryLaravelTestCase.php
rename to test/Sentry/TestCase.php
index eab9b670..c47f99ea 100644
--- a/test/Sentry/SentryLaravelTestCase.php
+++ b/test/Sentry/TestCase.php
@@ -4,6 +4,7 @@
use ReflectionMethod;
use Sentry\Breadcrumb;
+use Sentry\ClientInterface;
use Sentry\State\Scope;
use ReflectionProperty;
use Sentry\Laravel\Tracing;
@@ -11,14 +12,14 @@
use Sentry\Laravel\ServiceProvider;
use Orchestra\Testbench\TestCase as LaravelTestCase;
-abstract class SentryLaravelTestCase extends LaravelTestCase
+abstract class TestCase extends LaravelTestCase
{
protected $setupConfig = [
// Set config here before refreshing the app to set it in the container before Sentry is loaded
// or use the `$this->resetApplicationWithConfig([ /* config */ ]);` helper method
];
- protected function getEnvironmentSetUp($app)
+ protected function getEnvironmentSetUp($app): void
{
$app['config']->set('sentry.dsn', 'http://publickey:secretkey@sentry.dev/123');
@@ -27,7 +28,7 @@ protected function getEnvironmentSetUp($app)
}
}
- protected function getPackageProviders($app)
+ protected function getPackageProviders($app): array
{
return [
ServiceProvider::class,
@@ -35,21 +36,16 @@ protected function getPackageProviders($app)
];
}
- protected function resetApplicationWithConfig(array $config)
+ protected function resetApplicationWithConfig(array $config): void
{
$this->setupConfig = $config;
$this->refreshApplication();
}
- protected function dispatchLaravelEvent($event, array $payload = [])
+ protected function dispatchLaravelEvent($event, array $payload = []): void
{
- $dispatcher = $this->app['events'];
-
- // Laravel 5.4+ uses the dispatch method to dispatch/fire events
- return method_exists($dispatcher, 'dispatch')
- ? $dispatcher->dispatch($event, $payload)
- : $dispatcher->fire($event, $payload);
+ $this->app['events']->dispatch($event, $payload);
}
protected function getHubFromContainer(): HubInterface
@@ -57,6 +53,11 @@ protected function getHubFromContainer(): HubInterface
return $this->app->make('sentry');
}
+ protected function getClientFromContainer(): ClientInterface
+ {
+ return $this->getHubFromContainer()->getClient();
+ }
+
protected function getCurrentScope(): Scope
{
$hub = $this->getHubFromContainer();
diff --git a/test/Sentry/Tracing/EventHandlerTest.php b/test/Sentry/Tracing/EventHandlerTest.php
index f71faac8..8fad3b4f 100644
--- a/test/Sentry/Tracing/EventHandlerTest.php
+++ b/test/Sentry/Tracing/EventHandlerTest.php
@@ -3,35 +3,40 @@
namespace Sentry\Laravel\Tests\Tracing;
use ReflectionClass;
-use Sentry\Laravel\Tests\SentryLaravelTestCase;
+use Sentry\Laravel\Tests\TestCase;
use Sentry\Laravel\Tracing\BacktraceHelper;
use RuntimeException;
-use Sentry\Laravel\Tests\ExpectsException;
use Sentry\Laravel\Tracing\EventHandler;
-class EventHandlerTest extends SentryLaravelTestCase
+class EventHandlerTest extends TestCase
{
- use ExpectsException;
-
- public function test_missing_event_handler_throws_exception()
+ public function testMissingEventHandlerThrowsException(): void
{
- $this->safeExpectException(RuntimeException::class);
+ $this->expectException(RuntimeException::class);
- $handler = new EventHandler($this->app, $this->app->make(BacktraceHelper::class), []);
+ $handler = new EventHandler([], $this->app->make(BacktraceHelper::class));
+ /** @noinspection PhpUndefinedMethodInspection */
$handler->thisIsNotAHandlerAndShouldThrowAnException();
}
- public function test_all_mapped_event_handlers_exist()
+ public function testAllMappedEventHandlersExist(): void
+ {
+ $this->tryAllEventHandlerMethods(
+ $this->getEventHandlerMapFromEventHandler('eventHandlerMap')
+ );
+ }
+
+ public function testAllMappedQueueEventHandlersExist(): void
{
$this->tryAllEventHandlerMethods(
- $this->getStaticPropertyValueFromClass(EventHandler::class, 'eventHandlerMap')
+ $this->getEventHandlerMapFromEventHandler('queueEventHandlerMap')
);
}
private function tryAllEventHandlerMethods(array $methods): void
{
- $handler = new EventHandler($this->app, $this->app->make(BacktraceHelper::class), []);
+ $handler = new EventHandler([], $this->app->make(BacktraceHelper::class));
$methods = array_map(static function ($method) {
return "{$method}Handler";
@@ -42,12 +47,12 @@ private function tryAllEventHandlerMethods(array $methods): void
}
}
- private function getStaticPropertyValueFromClass($className, $attributeName)
+ private function getEventHandlerMapFromEventHandler($eventHandlerMapName)
{
- $class = new ReflectionClass($className);
+ $class = new ReflectionClass(EventHandler::class);
$attributes = $class->getStaticProperties();
- return $attributes[$attributeName];
+ return $attributes[$eventHandlerMapName];
}
}