Skip to content

Commit 2c7a6d9

Browse files
authored
Merge a101335 into 2dfcf9e
2 parents 2dfcf9e + a101335 commit 2c7a6d9

File tree

10 files changed

+356
-24
lines changed

10 files changed

+356
-24
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
## Unreleased
44

5+
## 2.0.0-beta1
6+
7+
**Breaking Change**: This version uses the [envelope endpoint](https://develop.sentry.dev/sdk/envelopes/). If you are
8+
using an on-premise installation it requires Sentry version `>= v20.6.0` to work. If you are using
9+
[sentry.io](https://sentry.io) nothing will change and no action is needed.
10+
11+
- Using `3.0.0-beta1` of Sentry PHP SDK
12+
- Add support for Tracing, enable it by setting `traces_sample_rate` in the config to a value > 1
13+
514
## 1.8.0
615

716
- Add `send_default_pii` option by default to published config file (#340)

composer.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
"require": {
1515
"php": "^7.1",
1616
"illuminate/support": "5.0 - 5.8 | ^6.0 | ^7.0",
17-
"sentry/sdk": "^2.1"
17+
"sentry/sdk": "3.0.0-beta1"
1818
},
19+
"minimum-stability": "dev",
20+
"prefer-stable": true,
1921
"require-dev": {
2022
"phpunit/phpunit": "^8.0",
2123
"laravel/framework": "^6.0",
@@ -53,7 +55,8 @@
5355
},
5456
"laravel": {
5557
"providers": [
56-
"Sentry\\Laravel\\ServiceProvider"
58+
"Sentry\\Laravel\\ServiceProvider",
59+
"Sentry\\Laravel\\Tracing\\ServiceProvider"
5760
],
5861
"aliases": {
5962
"Sentry": "Sentry\\Laravel\\Facade"

config/sentry.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@
2727
// @see: https://docs.sentry.io/error-reporting/configuration/?platform=php#send-default-pii
2828
'send_default_pii' => false,
2929

30+
'traces_sample_rate' => 0,
3031
];

src/Sentry/Laravel/EventHandler.php

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
use Illuminate\Queue\QueueManager;
1717
use Illuminate\Routing\Events\RouteMatched;
1818
use Illuminate\Routing\Route;
19-
use Illuminate\Support\Str;
2019
use RuntimeException;
2120
use Sentry\Breadcrumb;
2221
use Sentry\SentrySdk;
2322
use Sentry\State\Scope;
23+
use Sentry\Tracing\SpanContext;
24+
use Sentry\Tracing\Transaction;
2425

2526
class EventHandler
2627
{
@@ -194,26 +195,16 @@ public function __call($method, $arguments)
194195
*/
195196
protected function routerMatchedHandler(Route $route)
196197
{
197-
$routeName = null;
198+
$routeName = Integration::extractNameForRoute($route) ?? '<unlabeled transaction>';
198199

199-
if ($route->getName()) {
200-
// someaction (route name/alias)
201-
$routeName = $route->getName();
200+
$transaction = SentrySdk::getCurrentHub()->getTransaction();
202201

203-
// Laravel 7 route caching generates a route names if the user didn't specify one
204-
// theirselfs to optimize route matching. These route names are useless to the
205-
// developer so if we encounter a generated route name we discard the value
206-
if (Str::startsWith($routeName, 'generated::')) {
207-
$routeName = null;
208-
}
209-
}
210-
211-
if (empty($routeName) && $route->getActionName()) {
212-
// SomeController@someAction (controller action)
213-
$routeName = $route->getActionName();
214-
} elseif (empty($routeName) || $routeName === 'Closure') {
215-
// /someaction // Fallback to the url
216-
$routeName = $route->uri();
202+
if ($transaction instanceof Transaction) {
203+
$transaction->setName($routeName);
204+
$transaction->setData([
205+
'action' => $route->getActionName(),
206+
'name' => $route->getName()
207+
]);
217208
}
218209

219210
Integration::addBreadcrumb(new Breadcrumb(
@@ -287,6 +278,16 @@ private function addQueryBreadcrumb($query, $bindings, $time, $connectionName)
287278
$data['bindings'] = $bindings;
288279
}
289280

281+
$transaction = SentrySdk::getCurrentHub()->getTransaction();
282+
if (null !== $transaction) {
283+
$context = new SpanContext();
284+
$context->op = 'sql.query';
285+
$context->description = $query;
286+
$context->startTimestamp = microtime(true) - $time / 1000;
287+
$context->endTimestamp = $context->startTimestamp + $time / 1000;
288+
$transaction->startChild($context);
289+
}
290+
290291
Integration::addBreadcrumb(new Breadcrumb(
291292
Breadcrumb::LEVEL_INFO,
292293
Breadcrumb::TYPE_DEFAULT,

src/Sentry/Laravel/Integration.php

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Sentry\Laravel;
44

5+
use Illuminate\Routing\Route;
6+
use Illuminate\Support\Str;
57
use Sentry\FlushableClientInterface;
68
use Sentry\SentrySdk;
9+
use Sentry\Tracing\Span;
710
use function Sentry\addBreadcrumb;
811
use function Sentry\configureScope;
912
use Sentry\Breadcrumb;
@@ -30,7 +33,9 @@ public function setupOnce(): void
3033
return $event;
3134
}
3235

33-
$event->setTransaction($self->getTransaction());
36+
if (null === $event->getTransaction()) {
37+
$event->setTransaction($self->getTransaction());
38+
}
3439

3540
return $event;
3641
});
@@ -71,7 +76,7 @@ public static function configureScope(callable $callback): void
7176
/**
7277
* @return null|string
7378
*/
74-
public static function getTransaction()
79+
public static function getTransaction(): ?string
7580
{
7681
return self::$transaction;
7782
}
@@ -98,4 +103,78 @@ public static function flushEvents(): void
98103
$client->flush();
99104
}
100105
}
106+
107+
/**
108+
* Extract the readable name for a route.
109+
*
110+
* @param \Illuminate\Routing\Route $route
111+
*
112+
* @return string|null
113+
*/
114+
public static function extractNameForRoute(Route $route): ?string
115+
{
116+
$routeName = null;
117+
118+
if (empty($routeName) && $route->getName()) {
119+
// someaction (route name/alias)
120+
$routeName = $route->getName();
121+
122+
// Laravel 7 route caching generates a route names if the user didn't specify one
123+
// theirselfs to optimize route matching. These route names are useless to the
124+
// developer so if we encounter a generated route name we discard the value
125+
if (Str::startsWith($routeName, 'generated::')) {
126+
$routeName = null;
127+
}
128+
129+
// If the route name ends with a `.` we assume an incomplete group name prefix
130+
// we discard this value since it will most likely not mean anything to the
131+
// developer and will be duplicated by other unnamed routes in the group
132+
if (Str::endsWith($routeName, '.')) {
133+
$routeName = null;
134+
}
135+
}
136+
137+
if (empty($routeName) && $route->getActionName()) {
138+
// SomeController@someAction (controller action)
139+
$routeName = ltrim($route->getActionName(), '\\');
140+
}
141+
142+
if (empty($routeName) || $routeName === 'Closure') {
143+
// /someaction // Fallback to the url
144+
$routeName = '/' . ltrim($route->uri(), '/');
145+
}
146+
147+
return $routeName;
148+
}
149+
150+
/**
151+
* Retrieve the meta tags with tracing information to link this request to front-end requests.
152+
*
153+
* @return string
154+
*/
155+
public static function sentryTracingMeta(): string
156+
{
157+
$span = self::currentTracingSpan();
158+
159+
if ($span === null) {
160+
return '';
161+
}
162+
163+
$content = sprintf('<meta name="sentry-trace" content="%s"/>', $span->toTraceparent());
164+
// $content .= sprintf('<meta name="sentry-trace-data" content="%s"/>', $span->getDescription());
165+
166+
return $content;
167+
}
168+
169+
/**
170+
* Get the current active tracing span from the scope.
171+
*
172+
* @return \Sentry\Tracing\Span|null
173+
*
174+
* @internal This is used internally as an easy way to retrieve the current active tracing span.
175+
*/
176+
public static function currentTracingSpan(): ?Span
177+
{
178+
return SentrySdk::getCurrentHub()->getSpan();
179+
}
101180
}

src/Sentry/Laravel/ServiceProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Sentry\Laravel;
44

5+
use Illuminate\Contracts\Http\Kernel as HttpKernelInterface;
56
use Sentry\SentrySdk;
67
use Sentry\State\Hub;
78
use Sentry\ClientBuilder;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
namespace Sentry\Laravel\Tracing;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use Sentry\SentrySdk;
8+
use Sentry\State\Hub;
9+
use Sentry\Tracing\SpanContext;
10+
use Sentry\Tracing\TransactionContext;
11+
12+
class Middleware
13+
{
14+
/**
15+
* The current active transaction.
16+
*
17+
* @var \Sentry\Tracing\Transaction|null
18+
*/
19+
protected $transaction;
20+
21+
/**
22+
* Handle an incoming request.
23+
*
24+
* @param \Illuminate\Http\Request $request
25+
* @param \Closure $next
26+
*
27+
* @return mixed
28+
*/
29+
public function handle($request, Closure $next)
30+
{
31+
if (app()->bound('sentry')) {
32+
$this->startTransaction($request, app('sentry'));
33+
}
34+
35+
return $next($request);
36+
}
37+
38+
/**
39+
* Handle the application termination.
40+
*
41+
* @param \Illuminate\Http\Request $request
42+
* @param \Illuminate\Http\Response $response
43+
*
44+
* @return void
45+
*/
46+
public function terminate($request, $response): void
47+
{
48+
if ($this->transaction !== null && app()->bound('sentry')) {
49+
$this->transaction->finish();
50+
}
51+
}
52+
53+
private function startTransaction(Request $request, Hub $sentry): void
54+
{
55+
$path = '/' . ltrim($request->path(), '/');
56+
$fallbackTime = microtime(true);
57+
$sentryTraceHeader = $request->header('sentry-trace');
58+
59+
$context = $sentryTraceHeader
60+
? TransactionContext::fromTraceparent($sentryTraceHeader)
61+
: new TransactionContext;
62+
63+
$context->op = 'http.server';
64+
$context->name = $path;
65+
$context->data = [
66+
'url' => $path,
67+
'method' => strtoupper($request->method()),
68+
];
69+
$context->startTimestamp = $request->server('REQUEST_TIME_FLOAT', $fallbackTime);
70+
71+
$this->transaction = $sentry->startTransaction($context);
72+
73+
// Setting the Transaction on the Hub
74+
SentrySdk::getCurrentHub()->setSpan($this->transaction);
75+
76+
if (!$this->addBootTimeSpans()) {
77+
// @TODO: We might want to move this together with the `RouteMatches` listener to some central place and or do this from the `EventHandler`
78+
app()->booted(function () use ($request, $fallbackTime): void {
79+
$spanContextStart = new SpanContext();
80+
$spanContextStart->op = 'app.bootstrap';
81+
$spanContextStart->startTimestamp = defined('LARAVEL_START') ? LARAVEL_START : $request->server('REQUEST_TIME_FLOAT', $fallbackTime);
82+
$spanContextStart->endTimestamp = microtime(true);
83+
$this->transaction->startChild($spanContextStart);
84+
});
85+
}
86+
}
87+
88+
private function addBootTimeSpans(): bool
89+
{
90+
if (!defined('LARAVEL_START') || !LARAVEL_START) {
91+
return false;
92+
}
93+
94+
if (!defined('SENTRY_AUTOLOAD') || !SENTRY_AUTOLOAD) {
95+
return false;
96+
}
97+
98+
if (!defined('SENTRY_BOOTSTRAP') || !SENTRY_BOOTSTRAP) {
99+
return false;
100+
}
101+
102+
$spanContextStart = new SpanContext();
103+
$spanContextStart->op = 'autoload';
104+
$spanContextStart->startTimestamp = LARAVEL_START;
105+
$spanContextStart->endTimestamp = SENTRY_AUTOLOAD;
106+
$this->transaction->startChild($spanContextStart);
107+
108+
$spanContextStart = new SpanContext();
109+
$spanContextStart->op = 'bootstrap';
110+
$spanContextStart->startTimestamp = SENTRY_AUTOLOAD;
111+
$spanContextStart->endTimestamp = SENTRY_BOOTSTRAP;
112+
$this->transaction->startChild($spanContextStart);
113+
114+
return true;
115+
}
116+
}

0 commit comments

Comments
 (0)