Skip to content

Commit 0a8f04a

Browse files
authored
Fix missing breadcrumbs for jobs throwing an exception (#633)
1 parent efb95db commit 0a8f04a

File tree

3 files changed

+138
-2
lines changed

3 files changed

+138
-2
lines changed

src/Sentry/Laravel/EventHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,8 @@ private function configureUserScopeFromModel($authUser): void
409409

410410
protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event): void
411411
{
412+
$this->cleanupScopeForTaskWithinLongRunningProcessWhen($this->pushedQueueScopeCount > 0);
413+
412414
$this->prepareScopeForTaskWithinLongRunningProcess();
413415

414416
++$this->pushedQueueScopeCount;
@@ -440,8 +442,6 @@ protected function queueJobProcessingHandler(QueueEvents\JobProcessing $event):
440442

441443
protected function queueJobExceptionOccurredHandler(QueueEvents\JobExceptionOccurred $event): void
442444
{
443-
$this->cleanupScopeForTaskWithinLongRunningProcessWhen($this->pushedQueueScopeCount > 0);
444-
445445
$this->afterTaskWithinLongRunningProcess();
446446
}
447447

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
3+
namespace Sentry\Laravel\Tests\EventHandler;
4+
5+
use Exception;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Sentry\Breadcrumb;
8+
use Sentry\Laravel\Tests\TestCase;
9+
use function Sentry\addBreadcrumb;
10+
use function Sentry\captureException;
11+
12+
class QueueEventsTest extends TestCase
13+
{
14+
public function testQueueJobPushesAndPopsScopeWithBreadcrumbs(): void
15+
{
16+
dispatch(new QueueEventsTestJobWithBreadcrumb);
17+
18+
$this->assertCount(0, $this->getCurrentBreadcrumbs());
19+
}
20+
21+
public function testQueueJobThatReportsPushesAndPopsScopeWithBreadcrumbs(): void
22+
{
23+
dispatch(new QueueEventsTestJobThatReportsAnExceptionWithBreadcrumb);
24+
25+
$this->assertCount(0, $this->getCurrentBreadcrumbs());
26+
27+
$this->assertNotNull($this->getLastEvent());
28+
29+
$event = $this->getLastEvent();
30+
31+
$this->assertCount(2, $event->getBreadcrumbs());
32+
}
33+
34+
public function testQueueJobThatThrowsLeavesPushedScopeWithBreadcrumbs(): void
35+
{
36+
try {
37+
dispatch(new QueueEventsTestJobThatThrowsAnUnhandledExceptionWithBreadcrumb);
38+
} catch (Exception $e) {
39+
// No action required, expected to throw
40+
}
41+
42+
// We still expect to find the breadcrumbs from the job here so they are attached to reported exceptions
43+
44+
$this->assertCount(2, $this->getCurrentBreadcrumbs());
45+
46+
$firstBreadcrumb = $this->getCurrentBreadcrumbs()[0];
47+
$this->assertEquals('queue.job', $firstBreadcrumb->getCategory());
48+
49+
$secondBreadcrumb = $this->getCurrentBreadcrumbs()[1];
50+
$this->assertEquals('test', $secondBreadcrumb->getCategory());
51+
}
52+
53+
public function testQueueJobsThatThrowPopsAndPushesScopeWithBreadcrumbsBeforeNewJob(): void
54+
{
55+
try {
56+
dispatch(new QueueEventsTestJobThatThrowsAnUnhandledExceptionWithBreadcrumb('test #1'));
57+
} catch (Exception $e) {
58+
// No action required, expected to throw
59+
}
60+
61+
try {
62+
dispatch(new QueueEventsTestJobThatThrowsAnUnhandledExceptionWithBreadcrumb('test #2'));
63+
} catch (Exception $e) {
64+
// No action required, expected to throw
65+
}
66+
67+
// We only expect to find the breadcrumbs from the second job here
68+
69+
$this->assertCount(2, $this->getCurrentBreadcrumbs());
70+
71+
$firstBreadcrumb = $this->getCurrentBreadcrumbs()[0];
72+
$this->assertEquals('queue.job', $firstBreadcrumb->getCategory());
73+
74+
$secondBreadcrumb = $this->getCurrentBreadcrumbs()[1];
75+
$this->assertEquals('test #2', $secondBreadcrumb->getMessage());
76+
}
77+
78+
public function testQueueJobsWithBreadcrumbSetInBetweenKeepsNonJobBreadcrumbsOnCurrentScope(): void
79+
{
80+
dispatch(new QueueEventsTestJobWithBreadcrumb);
81+
82+
addBreadcrumb(new Breadcrumb(Breadcrumb::LEVEL_INFO, Breadcrumb::LEVEL_DEBUG, 'test2', 'test2'));
83+
84+
dispatch(new QueueEventsTestJobWithBreadcrumb);
85+
86+
$this->assertCount(1, $this->getCurrentBreadcrumbs());
87+
}
88+
}
89+
90+
function queueEventsTestAddTestBreadcrumb($message = null): void
91+
{
92+
addBreadcrumb(
93+
new Breadcrumb(
94+
Breadcrumb::LEVEL_INFO,
95+
Breadcrumb::LEVEL_DEBUG,
96+
'test',
97+
$message ?? 'test'
98+
)
99+
);
100+
}
101+
102+
class QueueEventsTestJobWithBreadcrumb implements ShouldQueue
103+
{
104+
public function handle(): void
105+
{
106+
queueEventsTestAddTestBreadcrumb();
107+
}
108+
}
109+
110+
class QueueEventsTestJobThatReportsAnExceptionWithBreadcrumb implements ShouldQueue
111+
{
112+
public function handle(): void
113+
{
114+
queueEventsTestAddTestBreadcrumb();
115+
116+
captureException(new Exception('This is a test exception'));
117+
}
118+
}
119+
120+
class QueueEventsTestJobThatThrowsAnUnhandledExceptionWithBreadcrumb implements ShouldQueue
121+
{
122+
private $message;
123+
124+
public function __construct($message = null)
125+
{
126+
$this->message = $message;
127+
}
128+
129+
public function handle(): void
130+
{
131+
queueEventsTestAddTestBreadcrumb($this->message);
132+
133+
throw new Exception('This is a test exception');
134+
}
135+
}

test/Sentry/TestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ protected function getCurrentScope(): Scope
8686
return $method->invoke($hub);
8787
}
8888

89+
/** @return array<array-key, \Sentry\Breadcrumb> */
8990
protected function getCurrentBreadcrumbs(): array
9091
{
9192
$scope = $this->getCurrentScope();

0 commit comments

Comments
 (0)