Skip to content

Improve terminating callbacks visibility #707

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

Merged
merged 1 commit into from
Jun 12, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions src/Sentry/Laravel/Tracing/Middleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Sentry\Laravel\Tracing;

use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Http\Request;
use Sentry\SentrySdk;
use Sentry\State\HubInterface;
Expand Down Expand Up @@ -35,6 +36,23 @@ class Middleware
*/
private $bootedTimestamp;

/**
* The Laravel application instance.
*
* @var Application|null
*/
private $app;

/**
* Construct the Sentry tracing middleware.
*
* @param Application|null $app
*/
public function __construct(?Application $app)
{
$this->app = $app;
}

/**
* Handle an incoming request.
*
Expand Down Expand Up @@ -74,17 +92,26 @@ public function terminate(Request $request, $response): void

if ($this->appSpan !== null) {
$this->appSpan->finish();
$this->appSpan = null;
}

// Make sure we set the transaction and not have a child span in the Sentry SDK
// If the transaction is not on the scope during finish, the trace.context is wrong
SentrySdk::getCurrentHub()->setSpan($this->transaction);

if ($response instanceof SymfonyResponse) {
$this->hydrateResponseData($response);
}

$this->transaction->finish();
if ($this->app === null) {
$this->finishTransaction();
} else {
// We need to finish the transaction after the response has been sent to the client
// so we register a terminating callback to do so, this allows us to also capture
// spans that are created during the termination of the application like queue
// dispatched using dispatch(...)->afterResponse(). This middleware is called
// before the terminating callbacks so we are 99.9% sure to be the last one
// to run except if another terminating callback is registered after ours.
$this->app->terminating(function () {
$this->finishTransaction();
});
}
}

/**
Expand Down Expand Up @@ -193,4 +220,14 @@ private function hydrateResponseData(SymfonyResponse $response): void
{
$this->transaction->setHttpStatus($response->getStatusCode());
}

private function finishTransaction(): void
{
// Make sure we set the transaction and not have a child span in the Sentry SDK
// If the transaction is not on the scope during finish, the trace.context is wrong
SentrySdk::getCurrentHub()->setSpan($this->transaction);

$this->transaction->finish();
$this->transaction = null;
}
}