1919use Sentry \Tracing \SpanStatus ;
2020use Sentry \Tracing \TransactionContext ;
2121use Sentry \Tracing \TransactionSource ;
22+ use Symfony \Component \HttpFoundation \Response ;
2223
2324class EventHandler
2425{
@@ -35,6 +36,8 @@ class EventHandler
3536 protected static $ eventHandlerMap = [
3637 RoutingEvents \RouteMatched::class => 'routeMatched ' ,
3738 DatabaseEvents \QueryExecuted::class => 'queryExecuted ' ,
39+ RoutingEvents \ResponsePrepared::class => 'responsePrepared ' ,
40+ RoutingEvents \PreparingResponse::class => 'responsePreparing ' ,
3841 HttpClientEvents \RequestSending::class => 'httpClientRequestSending ' ,
3942 HttpClientEvents \ResponseReceived::class => 'httpClientResponseReceived ' ,
4043 HttpClientEvents \ConnectionFailed::class => 'httpClientConnectionFailed ' ,
@@ -131,6 +134,8 @@ public function __construct(array $config, BacktraceHelper $backtraceHelper)
131134 *
132135 * @uses self::routeMatchedHandler()
133136 * @uses self::queryExecutedHandler()
137+ * @uses self::responsePreparedHandler()
138+ * @uses self::responsePreparingHandler()
134139 * @uses self::transactionBeginningHandler()
135140 * @uses self::transactionCommittedHandler()
136141 * @uses self::transactionRolledBackHandler()
@@ -258,6 +263,37 @@ private function resolveQueryOriginFromBacktrace(): ?string
258263 return "{$ filePath }: {$ firstAppFrame ->getLine ()}" ;
259264 }
260265
266+ protected function responsePreparedHandler (RoutingEvents \ResponsePrepared $ event ): void
267+ {
268+ $ span = $ this ->popSpan ();
269+
270+ if ($ span !== null ) {
271+ $ span ->finish ();
272+ }
273+ }
274+
275+ protected function responsePreparingHandler (RoutingEvents \PreparingResponse $ event ): void
276+ {
277+ // If the response is already a Response object there is no need to handle the event anymore
278+ // since there isn't going to be any real work going on, the response is already as prepared
279+ // as it can be. So we ignore the event to prevent loggin a very short empty duplicated span
280+ if ($ event ->response instanceof Response) {
281+ return ;
282+ }
283+
284+ $ parentSpan = SentrySdk::getCurrentHub ()->getSpan ();
285+
286+ // If there is no tracing span active there is no need to handle the event
287+ if ($ parentSpan === null ) {
288+ return ;
289+ }
290+
291+ $ context = new SpanContext ;
292+ $ context ->setOp ('http.route.response ' );
293+
294+ $ this ->pushSpan ($ parentSpan ->startChild ($ context ));
295+ }
296+
261297 protected function transactionBeginningHandler (DatabaseEvents \TransactionBeginning $ event ): void
262298 {
263299 $ parentSpan = SentrySdk::getCurrentHub ()->getSpan ();
0 commit comments