Skip to content

Commit de3d2e0

Browse files
authored
Improve terminating callbacks visibility (#707)
1 parent 728c145 commit de3d2e0

File tree

1 file changed

+42
-5
lines changed

1 file changed

+42
-5
lines changed

src/Sentry/Laravel/Tracing/Middleware.php

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Sentry\Laravel\Tracing;
44

55
use Closure;
6+
use Illuminate\Contracts\Foundation\Application;
67
use Illuminate\Http\Request;
78
use Sentry\SentrySdk;
89
use Sentry\State\HubInterface;
@@ -35,6 +36,23 @@ class Middleware
3536
*/
3637
private $bootedTimestamp;
3738

39+
/**
40+
* The Laravel application instance.
41+
*
42+
* @var Application|null
43+
*/
44+
private $app;
45+
46+
/**
47+
* Construct the Sentry tracing middleware.
48+
*
49+
* @param Application|null $app
50+
*/
51+
public function __construct(?Application $app)
52+
{
53+
$this->app = $app;
54+
}
55+
3856
/**
3957
* Handle an incoming request.
4058
*
@@ -74,17 +92,26 @@ public function terminate(Request $request, $response): void
7492

7593
if ($this->appSpan !== null) {
7694
$this->appSpan->finish();
95+
$this->appSpan = null;
7796
}
7897

79-
// Make sure we set the transaction and not have a child span in the Sentry SDK
80-
// If the transaction is not on the scope during finish, the trace.context is wrong
81-
SentrySdk::getCurrentHub()->setSpan($this->transaction);
82-
8398
if ($response instanceof SymfonyResponse) {
8499
$this->hydrateResponseData($response);
85100
}
86101

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

90117
/**
@@ -193,4 +220,14 @@ private function hydrateResponseData(SymfonyResponse $response): void
193220
{
194221
$this->transaction->setHttpStatus($response->getStatusCode());
195222
}
223+
224+
private function finishTransaction(): void
225+
{
226+
// Make sure we set the transaction and not have a child span in the Sentry SDK
227+
// If the transaction is not on the scope during finish, the trace.context is wrong
228+
SentrySdk::getCurrentHub()->setSpan($this->transaction);
229+
230+
$this->transaction->finish();
231+
$this->transaction = null;
232+
}
196233
}

0 commit comments

Comments
 (0)