Skip to content

Commit

Permalink
Merge a101335 into 2dfcf9e
Browse files Browse the repository at this point in the history
  • Loading branch information
HazAT authored Sep 3, 2020
2 parents 2dfcf9e + a101335 commit 2c7a6d9
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 24 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## Unreleased

## 2.0.0-beta1

**Breaking Change**: This version uses the [envelope endpoint](https://develop.sentry.dev/sdk/envelopes/). If you are
using an on-premise installation it requires Sentry version `>= v20.6.0` to work. If you are using
[sentry.io](https://sentry.io) nothing will change and no action is needed.

- Using `3.0.0-beta1` of Sentry PHP SDK
- Add support for Tracing, enable it by setting `traces_sample_rate` in the config to a value > 1

## 1.8.0

- Add `send_default_pii` option by default to published config file (#340)
Expand Down
7 changes: 5 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
"require": {
"php": "^7.1",
"illuminate/support": "5.0 - 5.8 | ^6.0 | ^7.0",
"sentry/sdk": "^2.1"
"sentry/sdk": "3.0.0-beta1"
},
"minimum-stability": "dev",
"prefer-stable": true,
"require-dev": {
"phpunit/phpunit": "^8.0",
"laravel/framework": "^6.0",
Expand Down Expand Up @@ -53,7 +55,8 @@
},
"laravel": {
"providers": [
"Sentry\\Laravel\\ServiceProvider"
"Sentry\\Laravel\\ServiceProvider",
"Sentry\\Laravel\\Tracing\\ServiceProvider"
],
"aliases": {
"Sentry": "Sentry\\Laravel\\Facade"
Expand Down
1 change: 1 addition & 0 deletions config/sentry.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@
// @see: https://docs.sentry.io/error-reporting/configuration/?platform=php#send-default-pii
'send_default_pii' => false,

'traces_sample_rate' => 0,
];
39 changes: 20 additions & 19 deletions src/Sentry/Laravel/EventHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
use Illuminate\Queue\QueueManager;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Routing\Route;
use Illuminate\Support\Str;
use RuntimeException;
use Sentry\Breadcrumb;
use Sentry\SentrySdk;
use Sentry\State\Scope;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\Transaction;

class EventHandler
{
Expand Down Expand Up @@ -194,26 +195,16 @@ public function __call($method, $arguments)
*/
protected function routerMatchedHandler(Route $route)
{
$routeName = null;
$routeName = Integration::extractNameForRoute($route) ?? '<unlabeled transaction>';

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

// 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::startsWith($routeName, 'generated::')) {
$routeName = null;
}
}

if (empty($routeName) && $route->getActionName()) {
// SomeController@someAction (controller action)
$routeName = $route->getActionName();
} elseif (empty($routeName) || $routeName === 'Closure') {
// /someaction // Fallback to the url
$routeName = $route->uri();
if ($transaction instanceof Transaction) {
$transaction->setName($routeName);
$transaction->setData([
'action' => $route->getActionName(),
'name' => $route->getName()
]);
}

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

$transaction = SentrySdk::getCurrentHub()->getTransaction();
if (null !== $transaction) {
$context = new SpanContext();
$context->op = 'sql.query';
$context->description = $query;
$context->startTimestamp = microtime(true) - $time / 1000;
$context->endTimestamp = $context->startTimestamp + $time / 1000;
$transaction->startChild($context);
}

Integration::addBreadcrumb(new Breadcrumb(
Breadcrumb::LEVEL_INFO,
Breadcrumb::TYPE_DEFAULT,
Expand Down
83 changes: 81 additions & 2 deletions src/Sentry/Laravel/Integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace Sentry\Laravel;

use Illuminate\Routing\Route;
use Illuminate\Support\Str;
use Sentry\FlushableClientInterface;
use Sentry\SentrySdk;
use Sentry\Tracing\Span;
use function Sentry\addBreadcrumb;
use function Sentry\configureScope;
use Sentry\Breadcrumb;
Expand All @@ -30,7 +33,9 @@ public function setupOnce(): void
return $event;
}

$event->setTransaction($self->getTransaction());
if (null === $event->getTransaction()) {
$event->setTransaction($self->getTransaction());
}

return $event;
});
Expand Down Expand Up @@ -71,7 +76,7 @@ public static function configureScope(callable $callback): void
/**
* @return null|string
*/
public static function getTransaction()
public static function getTransaction(): ?string
{
return self::$transaction;
}
Expand All @@ -98,4 +103,78 @@ public static function flushEvents(): void
$client->flush();
}
}

/**
* Extract the readable name for a route.
*
* @param \Illuminate\Routing\Route $route
*
* @return string|null
*/
public static function extractNameForRoute(Route $route): ?string
{
$routeName = null;

if (empty($routeName) && $route->getName()) {
// someaction (route name/alias)
$routeName = $route->getName();

// 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::startsWith($routeName, 'generated::')) {
$routeName = 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($routeName, '.')) {
$routeName = null;
}
}

if (empty($routeName) && $route->getActionName()) {
// SomeController@someAction (controller action)
$routeName = ltrim($route->getActionName(), '\\');
}

if (empty($routeName) || $routeName === 'Closure') {
// /someaction // Fallback to the url
$routeName = '/' . ltrim($route->uri(), '/');
}

return $routeName;
}

/**
* Retrieve the meta tags with tracing information to link this request to front-end requests.
*
* @return string
*/
public static function sentryTracingMeta(): string
{
$span = self::currentTracingSpan();

if ($span === null) {
return '';
}

$content = sprintf('<meta name="sentry-trace" content="%s"/>', $span->toTraceparent());
// $content .= sprintf('<meta name="sentry-trace-data" content="%s"/>', $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();
}
}
1 change: 1 addition & 0 deletions src/Sentry/Laravel/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Sentry\Laravel;

use Illuminate\Contracts\Http\Kernel as HttpKernelInterface;
use Sentry\SentrySdk;
use Sentry\State\Hub;
use Sentry\ClientBuilder;
Expand Down
116 changes: 116 additions & 0 deletions src/Sentry/Laravel/Tracing/Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace Sentry\Laravel\Tracing;

use Closure;
use Illuminate\Http\Request;
use Sentry\SentrySdk;
use Sentry\State\Hub;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\TransactionContext;

class Middleware
{
/**
* The current active transaction.
*
* @var \Sentry\Tracing\Transaction|null
*/
protected $transaction;

/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if (app()->bound('sentry')) {
$this->startTransaction($request, app('sentry'));
}

return $next($request);
}

/**
* Handle the application termination.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Response $response
*
* @return void
*/
public function terminate($request, $response): void
{
if ($this->transaction !== null && app()->bound('sentry')) {
$this->transaction->finish();
}
}

private function startTransaction(Request $request, Hub $sentry): void
{
$path = '/' . ltrim($request->path(), '/');
$fallbackTime = microtime(true);
$sentryTraceHeader = $request->header('sentry-trace');

$context = $sentryTraceHeader
? TransactionContext::fromTraceparent($sentryTraceHeader)
: new TransactionContext;

$context->op = 'http.server';
$context->name = $path;
$context->data = [
'url' => $path,
'method' => strtoupper($request->method()),
];
$context->startTimestamp = $request->server('REQUEST_TIME_FLOAT', $fallbackTime);

$this->transaction = $sentry->startTransaction($context);

// Setting the Transaction on the Hub
SentrySdk::getCurrentHub()->setSpan($this->transaction);

if (!$this->addBootTimeSpans()) {
// @TODO: We might want to move this together with the `RouteMatches` listener to some central place and or do this from the `EventHandler`
app()->booted(function () use ($request, $fallbackTime): void {
$spanContextStart = new SpanContext();
$spanContextStart->op = 'app.bootstrap';
$spanContextStart->startTimestamp = defined('LARAVEL_START') ? LARAVEL_START : $request->server('REQUEST_TIME_FLOAT', $fallbackTime);
$spanContextStart->endTimestamp = microtime(true);
$this->transaction->startChild($spanContextStart);
});
}
}

private function addBootTimeSpans(): bool
{
if (!defined('LARAVEL_START') || !LARAVEL_START) {
return false;
}

if (!defined('SENTRY_AUTOLOAD') || !SENTRY_AUTOLOAD) {
return false;
}

if (!defined('SENTRY_BOOTSTRAP') || !SENTRY_BOOTSTRAP) {
return false;
}

$spanContextStart = new SpanContext();
$spanContextStart->op = 'autoload';
$spanContextStart->startTimestamp = LARAVEL_START;
$spanContextStart->endTimestamp = SENTRY_AUTOLOAD;
$this->transaction->startChild($spanContextStart);

$spanContextStart = new SpanContext();
$spanContextStart->op = 'bootstrap';
$spanContextStart->startTimestamp = SENTRY_AUTOLOAD;
$spanContextStart->endTimestamp = SENTRY_BOOTSTRAP;
$this->transaction->startChild($spanContextStart);

return true;
}
}
Loading

0 comments on commit 2c7a6d9

Please sign in to comment.