diff --git a/composer.json b/composer.json
index cd152ece..e95d2a4e 100644
--- a/composer.json
+++ b/composer.json
@@ -23,7 +23,7 @@
"require": {
"php": "^7.2 | ^8.0",
"illuminate/support": "5.0 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0",
- "sentry/sentry": "^3.3",
+ "sentry/sentry": "^3.9",
"sentry/sdk": "^3.1",
"symfony/psr-http-message-bridge": "^1.0 | ^2.0",
"nyholm/psr7": "^1.0"
diff --git a/src/Sentry/Laravel/Console/TestCommand.php b/src/Sentry/Laravel/Console/TestCommand.php
index 0623d5fd..7cf52127 100644
--- a/src/Sentry/Laravel/Console/TestCommand.php
+++ b/src/Sentry/Laravel/Console/TestCommand.php
@@ -140,6 +140,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 f9f32bfe..8db2bb1d 100644
--- a/src/Sentry/Laravel/EventHandler.php
+++ b/src/Sentry/Laravel/EventHandler.php
@@ -283,7 +283,7 @@ public function __call($method, $arguments)
*/
protected function routerMatchedHandler(Route $route)
{
- $routeName = Integration::extractNameForRoute($route);
+ [$routeName] = Integration::extractNameAndSourceForRoute($route);
Integration::addBreadcrumb(new Breadcrumb(
Breadcrumb::LEVEL_INFO,
diff --git a/src/Sentry/Laravel/Integration.php b/src/Sentry/Laravel/Integration.php
index f400aaf5..905d3622 100644
--- a/src/Sentry/Laravel/Integration.php
+++ b/src/Sentry/Laravel/Integration.php
@@ -6,6 +6,7 @@
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;
@@ -122,27 +123,48 @@ public static function flushEvents(): void
* @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.
+ *
+ * @param \Illuminate\Routing\Route $route
+ *
+ * @return array{0: string, 1: \Sentry\Tracing\TransactionSource}
+ *
+ * @internal This helper is used in various places to extra meaninful info from a Laravel Route object.
+ */
+ public static function extractNameAndSourceForRoute(Route $route): array
+ {
+ $source = null;
$routeName = null;
- // someaction (route name/alias)
+ // some.action (route name/alias)
if ($route->getName()) {
+ $source = TransactionSource::component();
$routeName = self::extractNameForNamedRoute($route->getName());
}
// Some\Controller@someAction (controller action)
if (empty($routeName) && $route->getActionName()) {
+ $source = TransactionSource::component();
$routeName = self::extractNameForActionRoute($route->getActionName());
}
- // /someaction // Fallback to the url
+ // /some/{action} // Fallback to the route uri (with parameter placeholders)
if (empty($routeName) || $routeName === 'Closure') {
+ $source = TransactionSource::route();
$routeName = '/' . ltrim($route->uri(), '/');
}
- return $routeName;
+ return [$routeName, $source];
}
/**
@@ -152,24 +174,45 @@ public static function extractNameForRoute(Route $route): string
* @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] ?? [];
- // someaction (route name/alias)
+ // 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']);
}
- // /someaction // Fallback to the url
+ // /some/{action} // Fallback to the route uri (with parameter placeholders)
if (empty($routeName) || $routeName === 'Closure') {
$routeUri = array_reduce(
array_keys($routeData[2]),
@@ -179,10 +222,11 @@ static function ($carry, $key) use ($routeData) {
$path
);
+ $source = TransactionSource::url();
$routeName = '/' . ltrim($routeUri, '/');
}
- return $routeName;
+ return [$routeName, $source];
}
/**
@@ -247,7 +291,25 @@ public static function sentryTracingMeta(): string
}
$content = sprintf('', $span->toTraceparent());
- // $content .= sprintf('', $span->getDescription());
+
+ return $content;
+ }
+
+ /**
+ * Retrieve the meta tags with baggage information to link this request to front-end requests.
+ * This propagates the Dynamic Sampling Context.
+ *
+ * @return string
+ */
+ public static function sentryBaggageMeta(): string
+ {
+ $span = self::currentTracingSpan();
+
+ if ($span === null) {
+ return '';
+ }
+
+ $content = sprintf('', $span->toBaggage());
return $content;
}
diff --git a/src/Sentry/Laravel/Tracing/EventHandler.php b/src/Sentry/Laravel/Tracing/EventHandler.php
index 054dc62e..1fafb3bd 100644
--- a/src/Sentry/Laravel/Tracing/EventHandler.php
+++ b/src/Sentry/Laravel/Tracing/EventHandler.php
@@ -16,11 +16,14 @@
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\SpanStatus;
use Sentry\Tracing\TransactionContext;
+use Sentry\Tracing\TransactionSource;
class EventHandler
{
public const QUEUE_PAYLOAD_TRACE_PARENT_DATA = 'sentry_trace_parent_data';
+ public const QUEUE_PAYLOAD_BAGGAGE_DATA = 'sentry_baggage_data';
+
/**
* Map event handlers to events.
*
@@ -153,6 +156,7 @@ public function subscribeQueueEvents(QueueManager $queue): void
if ($currentSpan !== null && $payload !== null) {
$payload[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] = $currentSpan->toTraceparent();
+ $payload[self::QUEUE_PAYLOAD_BAGGAGE_DATA] = $currentSpan->toBaggage();
}
return $payload;
@@ -293,11 +297,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) {
@@ -325,6 +328,7 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
if ($context instanceof TransactionContext) {
$context->setName($resolvedJobName ?? $event->job->getName());
+ $context->setSource(TransactionSource::task());
}
$context->setOp('queue.process');
diff --git a/src/Sentry/Laravel/Tracing/Integrations/LighthouseIntegration.php b/src/Sentry/Laravel/Tracing/Integrations/LighthouseIntegration.php
index a05f113c..dc443a35 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
{
@@ -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..589d1353 100644
--- a/src/Sentry/Laravel/Tracing/Middleware.php
+++ b/src/Sentry/Laravel/Tracing/Middleware.php
@@ -11,6 +11,7 @@
use Sentry\Tracing\Span;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\TransactionContext;
+use Sentry\Tracing\TransactionSource;
use Symfony\Component\HttpFoundation\Response;
class Middleware
@@ -102,11 +103,11 @@ 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', '')
+ );
$context->setOp('http.server');
$context->setData([
@@ -180,9 +181,9 @@ private function hydrateRequestData(Request $request): void
$route = $request->route();
if ($route instanceof Route) {
- $this->updateTransactionNameIfDefault(
- Integration::extractNameForRoute($route)
- );
+ [$transactionName, $transactionSource] = Integration::extractNameAndSourceForRoute($route);
+
+ $this->updateTransactionNameIfDefault($transactionName, $transactionSource);
$this->transaction->setData([
'name' => $route->getName(),
@@ -190,9 +191,9 @@ private function hydrateRequestData(Request $request): void
'method' => $request->getMethod(),
]);
} elseif (is_array($route) && count($route) === 3) {
- $this->updateTransactionNameIfDefault(
- Integration::extractNameForLumenRoute($route, $request->path())
- );
+ [$transactionName, $transactionSource] = Integration::extractNameAndSourceForLumenRoute($route, $request->path());
+
+ $this->updateTransactionNameIfDefault($transactionName, $transactionSource);
$action = $route[1] ?? [];
@@ -203,7 +204,7 @@ private function hydrateRequestData(Request $request): void
]);
}
- $this->updateTransactionNameIfDefault('/' . ltrim($request->path(), '/'));
+ $this->updateTransactionNameIfDefault('/' . ltrim($request->path(), '/'), TransactionSource::url());
}
private function hydrateResponseData(Response $response): void
@@ -211,7 +212,7 @@ private function hydrateResponseData(Response $response): void
$this->transaction->setHttpStatus($response->getStatusCode());
}
- private function updateTransactionNameIfDefault(?string $name): void
+ private function updateTransactionNameIfDefault(?string $name, ?TransactionSource $source): void
{
// Ignore empty names (and `null`) for caller convenience
if (empty($name)) {
@@ -226,5 +227,6 @@ private function updateTransactionNameIfDefault(?string $name): void
}
$this->transaction->setName($name);
+ $this->transaction->getMetadata()->setSource($source ?? TransactionSource::custom());
}
}