Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve tracing #580

Merged
merged 11 commits into from Oct 12, 2022
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Drop support for Laravel Lumen (#579)
- Set the tracing transaction name on the `Illuminate\Routing\Events\RouteMatched` instead of at the end of the request (#580)
- Remove `Sentry\Integration::extractNameForRoute()`, it's alternative `Sentry\Integration::extractNameAndSourceForRoute()` is marked as `@internal` (#580)

## 2.14.0

- Fix not listening to queue events because `QueueManager` is registered as `queue` in the container and not by it's class name (#568)
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -78,6 +78,7 @@ try {

## Laravel Version Compatibility

- Laravel Lumen is supported until `2.14.x`
- 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
Expand Down
3 changes: 3 additions & 0 deletions composer.json
Expand Up @@ -28,6 +28,9 @@
"symfony/psr-http-message-bridge": "^1.0 | ^2.0",
"nyholm/psr7": "^1.0"
},
"conflict": {
"laravel/lumen-framework": "*"
},
"autoload": {
"psr-0": {
"Sentry\\Laravel\\": "src/"
Expand Down
90 changes: 13 additions & 77 deletions src/Sentry/Laravel/Integration.php
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Support\Str;
use Sentry\SentrySdk;
use Sentry\Tracing\Span;
use Sentry\Tracing\Transaction;
use Sentry\Tracing\TransactionSource;
use function Sentry\addBreadcrumb;
use function Sentry\configureScope;
Expand Down Expand Up @@ -117,21 +118,6 @@ public static function flushEvents(): void
}
}

/**
* Extract the readable name for a route.
*
* @param \Illuminate\Routing\Route $route
*
* @return string
*
* @internal This helper is used in various places to extra meaninful info from a Laravel Route object.
* @deprecated This will be removed in version 3.0, use `extractNameAndSourceForRoute` instead.
*/
public static function extractNameForRoute(Route $route): string
{
return self::extractNameAndSourceForRoute($route)[0];
}

/**
* Extract the readable name for a route and the transaction source for where that route name came from.
*
Expand Down Expand Up @@ -167,68 +153,6 @@ public static function extractNameAndSourceForRoute(Route $route): array
return [$routeName, $source];
}

/**
* 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 string
*
* @internal This helper is used in various places to extra meaninful info from a Lumen route data.
* @deprecated This will be removed in version 3.0, use `extractNameAndSourceForLumenRoute` instead.
*/
public static function extractNameForLumenRoute(array $routeData, string $path): string
{
return self::extractNameAndSourceForLumenRoute($routeData, $path)[0];
}

/**
* Extract the readable name for a Lumen route and the transaction source for where that route name came from.
*
* @param array $routeData The array of route data
* @param string $path The path of the request
*
* @return array{0: string, 1: \Sentry\Tracing\TransactionSource}
*
* @internal This helper is used in various places to extra meaninful info from a Lumen route data.
*/
public static function extractNameAndSourceForLumenRoute(array $routeData, string $path): array
{
$source = null;
$routeName = null;

$route = $routeData[1] ?? [];

// some.action (route name/alias)
if (!empty($route['as'])) {
$source = TransactionSource::component();
$routeName = self::extractNameForNamedRoute($route['as']);
}

// Some\Controller@someAction (controller action)
if (empty($routeName) && !empty($route['uses'])) {
$source = TransactionSource::component();
$routeName = self::extractNameForActionRoute($route['uses']);
}

// /some/{action} // Fallback to the route uri (with parameter placeholders)
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
);

$source = TransactionSource::url();
$routeName = '/' . ltrim($routeUri, '/');
}

return [$routeName, $source];
}

/**
* Take a route name and return it only if it's a usable route name.
*
Expand Down Expand Up @@ -314,6 +238,18 @@ public static function sentryBaggageMeta(): string
return $content;
}

/**
* Get the current active tracing span from the scope.
*
* @return \Sentry\Tracing\Transaction|null
*
* @internal This is used internally as an easy way to retrieve the current active transaction.
*/
public static function currentTransaction(): ?Transaction
{
return SentrySdk::getCurrentHub()->getTransaction();
}

/**
* Get the current active tracing span from the scope.
*
Expand Down
10 changes: 1 addition & 9 deletions src/Sentry/Laravel/ServiceProvider.php
Expand Up @@ -6,7 +6,6 @@
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;
Expand Down Expand Up @@ -48,10 +47,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);

Expand All @@ -78,10 +74,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();
Expand Down
55 changes: 49 additions & 6 deletions src/Sentry/Laravel/Tracing/EventHandler.php
Expand Up @@ -10,6 +10,8 @@
use Illuminate\Queue\Events as QueueEvents;
use Illuminate\Queue\Queue;
use Illuminate\Queue\QueueManager;
use Illuminate\Routing\Events as RoutingEvents;
use Illuminate\Routing\Route;
use RuntimeException;
use Sentry\Laravel\Integration;
use Sentry\SentrySdk;
Expand All @@ -30,6 +32,9 @@ class EventHandler
* @var array
*/
protected static $eventHandlerMap = [
'router.matched' => 'routerMatched', // Until Laravel 5.1
RoutingEvents\RouteMatched::class => 'routeMatched', // Since Laravel 5.2

'illuminate.query' => 'query', // Until Laravel 5.1
DatabaseEvents\QueryExecuted::class => 'queryExecuted', // Since Laravel 5.2
];
Expand Down Expand Up @@ -122,6 +127,11 @@ public function __construct(Container $container, BacktraceHelper $backtraceHelp

/**
* Attach all event handlers.
*
* @uses self::routeMatchedHandler()
* @uses self::routerMatchedHandler()
* @uses self::queryHandler()
* @uses self::queryExecutedHandler()
*/
public function subscribe(): void
{
Expand All @@ -141,6 +151,10 @@ public function subscribe(): void
* Attach all queue event handlers.
*
* @param \Illuminate\Queue\QueueManager $queue
*
* @uses self::queueJobProcessedHandler()
* @uses self::queueJobProcessingHandler()
* @uses self::queueJobExceptionOccurredHandler()
*/
public function subscribeQueueEvents(QueueManager $queue): void
{
Expand Down Expand Up @@ -185,7 +199,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";

Expand All @@ -200,6 +214,35 @@ public function __call($method, $arguments)
}
}

/**
* Until Laravel 5.1
*
* @param Route $route
*/
protected function routerMatchedHandler(Route $route): void
{
$transaction = Integration::currentTransaction();

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

[$transactionName, $transactionSource] = Integration::extractNameAndSourceForRoute($route);

$transaction->setName($transactionName);
$transaction->getMetadata()->setSource($transactionSource);
}

/**
* Since Laravel 5.2
*
* @param \Illuminate\Routing\Events\RouteMatched $match
*/
protected function routeMatchedHandler(RoutingEvents\RouteMatched $match): void
{
$this->routerMatchedHandler($match->route);
}

/**
* Until Laravel 5.1
*
Expand Down Expand Up @@ -249,7 +292,7 @@ private function recordQuerySpan($query, $time): void
$context->setEndTimestamp($context->getStartTimestamp() + $time / 1000);

if ($this->traceSqlQueryOrigins) {
$queryOrigin = $this->resolveQueryOriginFromBacktrace($context);
$queryOrigin = $this->resolveQueryOriginFromBacktrace();

if ($queryOrigin !== null) {
$context->setData(['sql.origin' => $queryOrigin]);
Expand Down Expand Up @@ -277,12 +320,12 @@ 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 queueJobProcessingHandler(QueueEvents\JobProcessing $event): void
{
$parentSpan = Integration::currentTracingSpan();

Expand Down Expand Up @@ -352,7 +395,7 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
*
* @param \Illuminate\Queue\Events\JobExceptionOccurred $event
*/
protected function queueJobExceptionOccurredHandler(QueueEvents\JobExceptionOccurred $event)
protected function queueJobExceptionOccurredHandler(QueueEvents\JobExceptionOccurred $event): void
{
$this->afterQueuedJob(SpanStatus::internalError());
}
Expand All @@ -362,7 +405,7 @@ protected function queueJobExceptionOccurredHandler(QueueEvents\JobExceptionOccu
*
* @param \Illuminate\Queue\Events\JobProcessed $event
*/
protected function queueJobProcessedHandler(QueueEvents\JobProcessed $event)
protected function queueJobProcessedHandler(QueueEvents\JobProcessed $event): void
{
$this->afterQueuedJob(SpanStatus::ok());
}
Expand Down