2
2
3
3
namespace Sentry \Laravel \Features ;
4
4
5
+ use Illuminate \Console \Application as ConsoleApplication ;
5
6
use Illuminate \Console \Scheduling \Event as SchedulingEvent ;
6
7
use Illuminate \Contracts \Cache \Factory as Cache ;
7
8
use Illuminate \Contracts \Foundation \Application ;
9
+ use Illuminate \Support \Str ;
10
+ use RuntimeException ;
8
11
use Sentry \CheckIn ;
9
12
use Sentry \CheckInStatus ;
10
13
use Sentry \Event as SentryEvent ;
14
+ use Sentry \MonitorConfig ;
15
+ use Sentry \MonitorSchedule ;
11
16
use Sentry \SentrySdk ;
12
17
13
18
class ConsoleIntegration extends Feature
@@ -31,65 +36,94 @@ public function setup(Cache $cache): void
31
36
{
32
37
$ this ->cache = $ cache ;
33
38
34
- $ startCheckIn = function (string $ mutex , string $ slug , bool $ useCache , int $ useCacheTtlInMinutes ) {
35
- $ this ->startCheckIn ($ mutex , $ slug , $ useCache , $ useCacheTtlInMinutes );
39
+ $ startCheckIn = function (? string $ slug , SchedulingEvent $ scheduled , ? int $ checkInMargin , ? int $ maxRuntime , bool $ updateMonitorConfig ) {
40
+ $ this ->startCheckIn ($ slug , $ scheduled , $ checkInMargin , $ maxRuntime , $ updateMonitorConfig );
36
41
};
37
- $ finishCheckIn = function (string $ mutex , string $ slug , CheckInStatus $ status, bool $ useCache ) {
38
- $ this ->finishCheckIn ($ mutex , $ slug , $ status , $ useCache );
42
+ $ finishCheckIn = function (? string $ slug , SchedulingEvent $ scheduled , CheckInStatus $ status ) {
43
+ $ this ->finishCheckIn ($ slug , $ scheduled , $ status );
39
44
};
40
45
41
- SchedulingEvent::macro ('sentryMonitor ' , function (string $ monitorSlug ) use ($ startCheckIn , $ finishCheckIn ) {
46
+ SchedulingEvent::macro ('sentryMonitor ' , function (
47
+ ?string $ monitorSlug = null ,
48
+ ?int $ checkInMargin = null ,
49
+ ?int $ maxRuntime = null ,
50
+ bool $ updateMonitorConfig = true
51
+ ) use ($ startCheckIn , $ finishCheckIn ) {
42
52
/** @var SchedulingEvent $this */
53
+ if ($ monitorSlug === null && $ this ->command === null ) {
54
+ throw new RuntimeException ('The command string is null, please set a slug manually for this scheduled command using the `sentryMonitor( \'your-monitor-slug \')` macro. ' );
55
+ }
56
+
43
57
return $ this
44
- ->before (function () use ($ startCheckIn , $ monitorSlug ) {
58
+ ->before (function () use ($ startCheckIn , $ monitorSlug, $ checkInMargin , $ maxRuntime , $ updateMonitorConfig ) {
45
59
/** @var SchedulingEvent $this */
46
- $ startCheckIn ($ this -> mutexName () , $ monitorSlug , $ this -> runInBackground , $ this -> expiresAt );
60
+ $ startCheckIn ($ monitorSlug , $ this , $ checkInMargin , $ maxRuntime , $ updateMonitorConfig );
47
61
})
48
62
->onSuccess (function () use ($ finishCheckIn , $ monitorSlug ) {
49
63
/** @var SchedulingEvent $this */
50
- $ finishCheckIn ($ this -> mutexName () , $ monitorSlug , CheckInStatus::ok (), $ this -> runInBackground );
64
+ $ finishCheckIn ($ monitorSlug , $ this , CheckInStatus::ok ());
51
65
})
52
66
->onFailure (function () use ($ finishCheckIn , $ monitorSlug ) {
53
67
/** @var SchedulingEvent $this */
54
- $ finishCheckIn ($ this -> mutexName () , $ monitorSlug , CheckInStatus::error (), $ this -> runInBackground );
68
+ $ finishCheckIn ($ monitorSlug , $ this , CheckInStatus::error ());
55
69
});
56
70
});
57
71
}
58
72
59
73
public function setupInactive (): void
60
74
{
61
- SchedulingEvent::macro ('sentryMonitor ' , function (string $ monitorSlug ) {
62
- // When there is no Sentry DSN set there is nothing for us to do, but we still want to allow the user to setup the macro
75
+ // This is an exact copy of the macro above, but without doing anything so that even when no DSN is configured the user can still use the macro
76
+ SchedulingEvent::macro ('sentryMonitor ' , function (
77
+ ?string $ monitorSlug = null ,
78
+ ?int $ checkInMargin = null ,
79
+ ?int $ maxRuntime = null ,
80
+ bool $ updateMonitorConfig = true
81
+ ) {
63
82
return $ this ;
64
83
});
65
84
}
66
85
67
- private function startCheckIn (string $ mutex , string $ slug , bool $ useCache , int $ useCacheTtlInMinutes ): void
86
+ private function startCheckIn (? string $ slug , SchedulingEvent $ scheduled , ? int $ checkInMargin , ? int $ maxRuntime , bool $ updateMonitorConfig ): void
68
87
{
69
- $ checkIn = $ this ->createCheckIn ($ slug , CheckInStatus::inProgress ());
88
+ $ checkInSlug = $ slug ?? $ this ->makeSlugForScheduled ($ scheduled );
89
+
90
+ $ checkIn = $ this ->createCheckIn ($ checkInSlug , CheckInStatus::inProgress ());
70
91
71
- $ cacheKey = $ this ->buildCacheKey ($ mutex , $ slug );
92
+ if ($ updateMonitorConfig || $ slug === null ) {
93
+ $ checkIn ->setMonitorConfig (new MonitorConfig (
94
+ MonitorSchedule::crontab ($ scheduled ->getExpression ()),
95
+ $ checkInMargin ,
96
+ $ maxRuntime ,
97
+ $ scheduled ->timezone
98
+ ));
99
+ }
100
+
101
+ $ cacheKey = $ this ->buildCacheKey ($ scheduled ->mutexName (), $ checkInSlug );
72
102
73
103
$ this ->checkInStore [$ cacheKey ] = $ checkIn ;
74
104
75
- if ($ useCache ) {
76
- $ this ->cache ->store ()->put ($ cacheKey , $ checkIn ->getId (), $ useCacheTtlInMinutes * 60 );
105
+ if ($ scheduled -> runInBackground ) {
106
+ $ this ->cache ->store ()->put ($ cacheKey , $ checkIn ->getId (), $ scheduled -> expiresAt * 60 );
77
107
}
78
108
79
109
$ this ->sendCheckIn ($ checkIn );
80
110
}
81
111
82
- private function finishCheckIn (string $ mutex , string $ slug , CheckInStatus $ status, bool $ useCache ): void
112
+ private function finishCheckIn (? string $ slug , SchedulingEvent $ scheduled , CheckInStatus $ status ): void
83
113
{
84
- $ cacheKey = $ this ->buildCacheKey ($ mutex , $ slug );
114
+ $ mutex = $ scheduled ->mutexName ();
115
+
116
+ $ checkInSlug = $ slug ?? $ this ->makeSlugForScheduled ($ scheduled );
117
+
118
+ $ cacheKey = $ this ->buildCacheKey ($ mutex , $ checkInSlug );
85
119
86
120
$ checkIn = $ this ->checkInStore [$ cacheKey ] ?? null ;
87
121
88
- if ($ checkIn === null && $ useCache ) {
122
+ if ($ checkIn === null && $ scheduled -> runInBackground ) {
89
123
$ checkInId = $ this ->cache ->store ()->get ($ cacheKey );
90
124
91
125
if ($ checkInId !== null ) {
92
- $ checkIn = $ this ->createCheckIn ($ slug , $ status , $ checkInId );
126
+ $ checkIn = $ this ->createCheckIn ($ checkInSlug , $ status , $ checkInId );
93
127
}
94
128
}
95
129
@@ -101,7 +135,7 @@ private function finishCheckIn(string $mutex, string $slug, CheckInStatus $statu
101
135
// We don't need to keep the checkIn ID stored since we finished executing the command
102
136
unset($ this ->checkInStore [$ mutex ]);
103
137
104
- if ($ useCache ) {
138
+ if ($ scheduled -> runInBackground ) {
105
139
$ this ->cache ->store ()->forget ($ cacheKey );
106
140
}
107
141
@@ -136,4 +170,21 @@ private function buildCacheKey(string $mutex, string $slug): string
136
170
// We use the mutex name as part of the cache key to avoid collisions between the same commands with the same schedule but with different slugs
137
171
return 'sentry:checkIn: ' . sha1 ("{$ mutex }: {$ slug }" );
138
172
}
173
+
174
+ private function makeSlugForScheduled (SchedulingEvent $ scheduled ): string
175
+ {
176
+ $ generatedSlug = Str::slug (
177
+ Str::replace (
178
+ // `:` is commonly used in the command name, so we replace it with `-` to avoid it being stripped out by the slug function
179
+ ': ' ,
180
+ '- ' ,
181
+ trim (
182
+ // The command string always starts with the PHP binary, so we remove it since it's not relevant to the slug
183
+ Str::after ($ scheduled ->command , ConsoleApplication::phpBinary ())
184
+ )
185
+ )
186
+ );
187
+
188
+ return "scheduled_ {$ generatedSlug }" ;
189
+ }
139
190
}
0 commit comments