Skip to content

Commit b9bb0e3

Browse files
cleptricstayallive
andauthored
feat: Add support for Dynamic Sampling (#572)
Co-authored-by: Alex Bouma <[email protected]>
1 parent 7513cd4 commit b9bb0e3

File tree

7 files changed

+96
-25
lines changed

7 files changed

+96
-25
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"require": {
2424
"php": "^7.2 | ^8.0",
2525
"illuminate/support": "5.0 - 5.8 | ^6.0 | ^7.0 | ^8.0 | ^9.0",
26-
"sentry/sentry": "^3.3",
26+
"sentry/sentry": "^3.9",
2727
"sentry/sdk": "^3.1",
2828
"symfony/psr-http-message-bridge": "^1.0 | ^2.0",
2929
"nyholm/psr7": "^1.0"

src/Sentry/Laravel/Console/TestCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public function log($level, $message, array $context = []): void
140140
$transactionContext = new TransactionContext();
141141
$transactionContext->setSampled(true);
142142
$transactionContext->setName('Sentry Test Transaction');
143+
$transactionContext->setSource(TransactionSource::custom());
143144
$transactionContext->setOp('sentry.test');
144145

145146
$transaction = $hub->startTransaction($transactionContext);

src/Sentry/Laravel/EventHandler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ public function __call($method, $arguments)
283283
*/
284284
protected function routerMatchedHandler(Route $route)
285285
{
286-
$routeName = Integration::extractNameForRoute($route);
286+
[$routeName] = Integration::extractNameAndSourceForRoute($route);
287287

288288
Integration::addBreadcrumb(new Breadcrumb(
289289
Breadcrumb::LEVEL_INFO,

src/Sentry/Laravel/Integration.php

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Support\Str;
77
use Sentry\SentrySdk;
88
use Sentry\Tracing\Span;
9+
use Sentry\Tracing\TransactionSource;
910
use function Sentry\addBreadcrumb;
1011
use function Sentry\configureScope;
1112
use Sentry\Breadcrumb;
@@ -122,27 +123,48 @@ public static function flushEvents(): void
122123
* @param \Illuminate\Routing\Route $route
123124
*
124125
* @return string
126+
*
127+
* @internal This helper is used in various places to extra meaninful info from a Laravel Route object.
128+
* @deprecated This will be removed in version 3.0, use `extractNameAndSourceForRoute` instead.
125129
*/
126130
public static function extractNameForRoute(Route $route): string
127131
{
132+
return self::extractNameAndSourceForRoute($route)[0];
133+
}
134+
135+
/**
136+
* Extract the readable name for a route and the transaction source for where that route name came from.
137+
*
138+
* @param \Illuminate\Routing\Route $route
139+
*
140+
* @return array{0: string, 1: \Sentry\Tracing\TransactionSource}
141+
*
142+
* @internal This helper is used in various places to extra meaninful info from a Laravel Route object.
143+
*/
144+
public static function extractNameAndSourceForRoute(Route $route): array
145+
{
146+
$source = null;
128147
$routeName = null;
129148

130-
// someaction (route name/alias)
149+
// some.action (route name/alias)
131150
if ($route->getName()) {
151+
$source = TransactionSource::component();
132152
$routeName = self::extractNameForNamedRoute($route->getName());
133153
}
134154

135155
// Some\Controller@someAction (controller action)
136156
if (empty($routeName) && $route->getActionName()) {
157+
$source = TransactionSource::component();
137158
$routeName = self::extractNameForActionRoute($route->getActionName());
138159
}
139160

140-
// /someaction // Fallback to the url
161+
// /some/{action} // Fallback to the route uri (with parameter placeholders)
141162
if (empty($routeName) || $routeName === 'Closure') {
163+
$source = TransactionSource::route();
142164
$routeName = '/' . ltrim($route->uri(), '/');
143165
}
144166

145-
return $routeName;
167+
return [$routeName, $source];
146168
}
147169

148170
/**
@@ -152,24 +174,45 @@ public static function extractNameForRoute(Route $route): string
152174
* @param string $path The path of the request
153175
*
154176
* @return string
177+
*
178+
* @internal This helper is used in various places to extra meaninful info from a Lumen route data.
179+
* @deprecated This will be removed in version 3.0, use `extractNameAndSourceForLumenRoute` instead.
155180
*/
156181
public static function extractNameForLumenRoute(array $routeData, string $path): string
157182
{
183+
return self::extractNameAndSourceForLumenRoute($routeData, $path)[0];
184+
}
185+
186+
/**
187+
* Extract the readable name for a Lumen route and the transaction source for where that route name came from.
188+
*
189+
* @param array $routeData The array of route data
190+
* @param string $path The path of the request
191+
*
192+
* @return array{0: string, 1: \Sentry\Tracing\TransactionSource}
193+
*
194+
* @internal This helper is used in various places to extra meaninful info from a Lumen route data.
195+
*/
196+
public static function extractNameAndSourceForLumenRoute(array $routeData, string $path): array
197+
{
198+
$source = null;
158199
$routeName = null;
159200

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

162-
// someaction (route name/alias)
203+
// some.action (route name/alias)
163204
if (!empty($route['as'])) {
205+
$source = TransactionSource::component();
164206
$routeName = self::extractNameForNamedRoute($route['as']);
165207
}
166208

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

172-
// /someaction // Fallback to the url
215+
// /some/{action} // Fallback to the route uri (with parameter placeholders)
173216
if (empty($routeName) || $routeName === 'Closure') {
174217
$routeUri = array_reduce(
175218
array_keys($routeData[2]),
@@ -179,10 +222,11 @@ static function ($carry, $key) use ($routeData) {
179222
$path
180223
);
181224

225+
$source = TransactionSource::url();
182226
$routeName = '/' . ltrim($routeUri, '/');
183227
}
184228

185-
return $routeName;
229+
return [$routeName, $source];
186230
}
187231

188232
/**
@@ -247,7 +291,25 @@ public static function sentryTracingMeta(): string
247291
}
248292

249293
$content = sprintf('<meta name="sentry-trace" content="%s"/>', $span->toTraceparent());
250-
// $content .= sprintf('<meta name="sentry-trace-data" content="%s"/>', $span->getDescription());
294+
295+
return $content;
296+
}
297+
298+
/**
299+
* Retrieve the meta tags with baggage information to link this request to front-end requests.
300+
* This propagates the Dynamic Sampling Context.
301+
*
302+
* @return string
303+
*/
304+
public static function sentryBaggageMeta(): string
305+
{
306+
$span = self::currentTracingSpan();
307+
308+
if ($span === null) {
309+
return '';
310+
}
311+
312+
$content = sprintf('<meta name="baggage" content="%s"/>', $span->toBaggage());
251313

252314
return $content;
253315
}

src/Sentry/Laravel/Tracing/EventHandler.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
use Sentry\Tracing\SpanContext;
1717
use Sentry\Tracing\SpanStatus;
1818
use Sentry\Tracing\TransactionContext;
19+
use Sentry\Tracing\TransactionSource;
1920

2021
class EventHandler
2122
{
2223
public const QUEUE_PAYLOAD_TRACE_PARENT_DATA = 'sentry_trace_parent_data';
2324

25+
public const QUEUE_PAYLOAD_BAGGAGE_DATA = 'sentry_baggage_data';
26+
2427
/**
2528
* Map event handlers to events.
2629
*
@@ -153,6 +156,7 @@ public function subscribeQueueEvents(QueueManager $queue): void
153156

154157
if ($currentSpan !== null && $payload !== null) {
155158
$payload[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] = $currentSpan->toTraceparent();
159+
$payload[self::QUEUE_PAYLOAD_BAGGAGE_DATA] = $currentSpan->toBaggage();
156160
}
157161

158162
return $payload;
@@ -293,11 +297,10 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
293297
}
294298

295299
if ($parentSpan === null) {
300+
$baggage = $event->job->payload()[self::QUEUE_PAYLOAD_BAGGAGE_DATA] ?? null;
296301
$traceParent = $event->job->payload()[self::QUEUE_PAYLOAD_TRACE_PARENT_DATA] ?? null;
297302

298-
$context = $traceParent === null
299-
? new TransactionContext
300-
: TransactionContext::fromSentryTrace($traceParent);
303+
$context = TransactionContext::fromHeaders($traceParent ?? '', $baggage ?? '');
301304

302305
// If the parent transaction was not sampled we also stop the queue job from being recorded
303306
if ($context->getParentSampled() === false) {
@@ -325,6 +328,7 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event)
325328

326329
if ($context instanceof TransactionContext) {
327330
$context->setName($resolvedJobName ?? $event->job->getName());
331+
$context->setSource(TransactionSource::task());
328332
}
329333

330334
$context->setOp('queue.process');

src/Sentry/Laravel/Tracing/Integrations/LighthouseIntegration.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Sentry\Laravel\Integration;
1414
use Sentry\SentrySdk;
1515
use Sentry\Tracing\SpanContext;
16+
use Sentry\Tracing\TransactionSource;
1617

1718
class LighthouseIntegration implements IntegrationInterface
1819
{
@@ -157,7 +158,7 @@ private function updateTransactionName(): void
157158
return;
158159
}
159160

160-
array_walk($groupedOperations, static function (array &$operations, string $operationType) {
161+
array_walk($groupedOperations, static function (&$operations, string $operationType) {
161162
sort($operations, SORT_STRING);
162163

163164
$operations = "{$operationType}{" . implode(',', $operations) . '}';
@@ -168,6 +169,7 @@ private function updateTransactionName(): void
168169
$transactionName = 'lighthouse?' . implode('&', $groupedOperations);
169170

170171
$transaction->setName($transactionName);
172+
$transaction->getMetadata()->setSource(TransactionSource::custom());
171173

172174
Integration::setTransaction($transactionName);
173175
}

src/Sentry/Laravel/Tracing/Middleware.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Sentry\Tracing\Span;
1212
use Sentry\Tracing\SpanContext;
1313
use Sentry\Tracing\TransactionContext;
14+
use Sentry\Tracing\TransactionSource;
1415
use Symfony\Component\HttpFoundation\Response;
1516

1617
class Middleware
@@ -102,11 +103,11 @@ public function setBootedTimestamp(?float $timestamp = null): void
102103
private function startTransaction(Request $request, HubInterface $sentry): void
103104
{
104105
$requestStartTime = $request->server('REQUEST_TIME_FLOAT', microtime(true));
105-
$sentryTraceHeader = $request->header('sentry-trace');
106106

107-
$context = $sentryTraceHeader
108-
? TransactionContext::fromSentryTrace($sentryTraceHeader)
109-
: new TransactionContext;
107+
$context = TransactionContext::fromHeaders(
108+
$request->header('sentry-trace', ''),
109+
$request->header('baggage', '')
110+
);
110111

111112
$context->setOp('http.server');
112113
$context->setData([
@@ -180,19 +181,19 @@ private function hydrateRequestData(Request $request): void
180181
$route = $request->route();
181182

182183
if ($route instanceof Route) {
183-
$this->updateTransactionNameIfDefault(
184-
Integration::extractNameForRoute($route)
185-
);
184+
[$transactionName, $transactionSource] = Integration::extractNameAndSourceForRoute($route);
185+
186+
$this->updateTransactionNameIfDefault($transactionName, $transactionSource);
186187

187188
$this->transaction->setData([
188189
'name' => $route->getName(),
189190
'action' => $route->getActionName(),
190191
'method' => $request->getMethod(),
191192
]);
192193
} elseif (is_array($route) && count($route) === 3) {
193-
$this->updateTransactionNameIfDefault(
194-
Integration::extractNameForLumenRoute($route, $request->path())
195-
);
194+
[$transactionName, $transactionSource] = Integration::extractNameAndSourceForLumenRoute($route, $request->path());
195+
196+
$this->updateTransactionNameIfDefault($transactionName, $transactionSource);
196197

197198
$action = $route[1] ?? [];
198199

@@ -203,15 +204,15 @@ private function hydrateRequestData(Request $request): void
203204
]);
204205
}
205206

206-
$this->updateTransactionNameIfDefault('/' . ltrim($request->path(), '/'));
207+
$this->updateTransactionNameIfDefault('/' . ltrim($request->path(), '/'), TransactionSource::url());
207208
}
208209

209210
private function hydrateResponseData(Response $response): void
210211
{
211212
$this->transaction->setHttpStatus($response->getStatusCode());
212213
}
213214

214-
private function updateTransactionNameIfDefault(?string $name): void
215+
private function updateTransactionNameIfDefault(?string $name, ?TransactionSource $source): void
215216
{
216217
// Ignore empty names (and `null`) for caller convenience
217218
if (empty($name)) {
@@ -226,5 +227,6 @@ private function updateTransactionNameIfDefault(?string $name): void
226227
}
227228

228229
$this->transaction->setName($name);
230+
$this->transaction->getMetadata()->setSource($source ?? TransactionSource::custom());
229231
}
230232
}

0 commit comments

Comments
 (0)