Skip to content

Commit dfd482d

Browse files
authored
Added support for cron check-ins (#182)
1 parent 24e8d8c commit dfd482d

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

Helper/Data.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ class Data extends AbstractHelper
8282
'ignore_js_errors' => ['type' => 'array'],
8383
'disable_default_integrations' => ['type' => 'array'],
8484
'clean_stacktrace' => ['type' => 'bool'],
85+
'cron_monitoring_enabled' => ['type' => 'bool'],
86+
'track_crons' => ['type' => 'array'],
8587
];
8688

8789
/**
@@ -118,6 +120,22 @@ public function getDSN()
118120
return $this->collectModuleConfig()['dsn'];
119121
}
120122

123+
/**
124+
* Whether cron monitoring (check-in events) is enabled.
125+
*/
126+
public function isCronMonitoringEnabled(): bool
127+
{
128+
return $this->collectModuleConfig()['cron_monitoring_enabled'] ?? false;
129+
}
130+
131+
/**
132+
* Get a list of crons to track.
133+
*/
134+
public function getTrackCrons(): array
135+
{
136+
return $this->collectModuleConfig()['track_crons'] ?? [];
137+
}
138+
121139
/**
122140
* Whether tracing is enabled.
123141
*/

Model/SentryCron.php

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
namespace JustBetter\Sentry\Model;
4+
5+
use JustBetter\Sentry\Helper\Data;
6+
use Magento\Cron\Model\Schedule;
7+
use Magento\Customer\Model\Session;
8+
use Sentry\CheckInStatus;
9+
use Sentry\MonitorConfig;
10+
use Sentry\MonitorSchedule;
11+
12+
class SentryCron
13+
{
14+
/**
15+
* @var array
16+
*/
17+
protected array $runningCheckins = [];
18+
19+
/**
20+
* SentryLog constructor.
21+
*
22+
* @param Data $data
23+
* @param Session $customerSession
24+
*/
25+
public function __construct(
26+
protected Data $data,
27+
protected Session $customerSession,
28+
) {
29+
}
30+
31+
/**
32+
* Send the status of a cron schedule to Sentry.
33+
*
34+
* @param Schedule $schedule
35+
*/
36+
public function sendScheduleStatus(Schedule $schedule)
37+
{
38+
if (!$this->data->isActive() ||
39+
!$this->data->isCronMonitoringEnabled() ||
40+
!in_array(
41+
$schedule->getJobCode(),
42+
$this->data->getTrackCrons()
43+
)
44+
) {
45+
return;
46+
}
47+
48+
$status = $schedule->getStatus();
49+
if (!in_array($status, [
50+
Schedule::STATUS_RUNNING,
51+
Schedule::STATUS_SUCCESS,
52+
Schedule::STATUS_ERROR,
53+
])) {
54+
return;
55+
}
56+
57+
/** @var array|null $cronExpressionArr */
58+
$cronExpressionArr = $schedule->getCronExprArr();
59+
$monitorConfig = null;
60+
if (!empty($cronExpressionArr)) {
61+
$cronExpression = implode(' ', $cronExpressionArr);
62+
$monitorConfig = new MonitorConfig(MonitorSchedule::crontab($cronExpression));
63+
}
64+
65+
if ($status === Schedule::STATUS_RUNNING) {
66+
if (!isset($this->runningCheckins[$schedule->getId()])) {
67+
$this->startCheckin($schedule, $monitorConfig);
68+
}
69+
70+
return;
71+
} else {
72+
$this->finishCheckin($schedule, $monitorConfig);
73+
}
74+
}
75+
76+
/**
77+
* Start the check-in for a given schedule.
78+
*
79+
* @param Schedule $schedule
80+
* @param MonitorConfig|null $monitorConfig
81+
*/
82+
public function startCheckin(Schedule $schedule, ?MonitorConfig $monitorConfig = null)
83+
{
84+
$this->runningCheckins[$schedule->getId()] = [
85+
'started_at' => microtime(true),
86+
'check_in_id' => \Sentry\captureCheckIn(
87+
slug: $schedule->getJobCode(),
88+
status: CheckInStatus::inProgress(),
89+
monitorConfig: $monitorConfig,
90+
),
91+
];
92+
}
93+
94+
/**
95+
* Finish the check-in for a given schedule.
96+
*
97+
* @param Schedule $schedule
98+
* @param MonitorConfig|null $monitorConfig
99+
*/
100+
public function finishCheckin(Schedule $schedule, ?MonitorConfig $monitorConfig = null)
101+
{
102+
if (!isset($this->runningCheckins[$schedule->getId()])) {
103+
return;
104+
}
105+
106+
\Sentry\captureCheckIn(
107+
slug: $schedule->getJobCode(),
108+
status: $schedule->getStatus() === Schedule::STATUS_SUCCESS ? CheckInStatus::ok() : CheckInStatus::error(),
109+
duration: microtime(true) - $this->runningCheckins[$schedule->getId()]['started_at'],
110+
monitorConfig: $monitorConfig,
111+
checkInId: $this->runningCheckins[$schedule->getId()]['check_in_id'],
112+
);
113+
114+
unset($this->runningCheckins[$schedule->getId()]);
115+
}
116+
}

Plugin/CronScheduleCheckIn.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace JustBetter\Sentry\Plugin;
4+
5+
use JustBetter\Sentry\Model\SentryCron;
6+
use Magento\Cron\Model\Schedule;
7+
8+
class CronScheduleCheckIn
9+
{
10+
/**
11+
* CronScheduleCheckIn constructor.
12+
*
13+
* @param SentryCron $sentryCron
14+
*/
15+
public function __construct(
16+
private SentryCron $sentryCron
17+
) {
18+
}
19+
20+
/**
21+
* Wrap launch, start watching for exceptions.
22+
*
23+
* @param Schedule $subject
24+
* @param callable $proceed
25+
* @param mixed $field
26+
* @param mixed $value
27+
*
28+
* @return \Magento\Framework\App\ResponseInterface
29+
*/
30+
public function aroundSetData(Schedule $subject, callable $proceed, $field, $value = null)
31+
{
32+
$result = $proceed($field, $value);
33+
if ($field !== 'status') {
34+
return $result;
35+
}
36+
37+
$this->sentryCron->sendScheduleStatus($subject);
38+
39+
return $result;
40+
}
41+
}

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ Next to that there are some configuration options under Stores > Configuration >
7676
| `profiles_sample_rate` | `0` (disabled) | if this option is larger than 0 (zero), the module will create a profile of the request. Please note that you have to install [Excimer](https://www.mediawiki.org/wiki/Excimer) on your server to use profiling. [Sentry documentation](https://docs.sentry.io/platforms/php/profiling/). You have to enable tracing too. |
7777
| `ignore_js_errors` | `[]` | Array of JavaScript error messages which should not be sent to Sentry. (See also `ignoreErrors` in [Sentry documentation](https://docs.sentry.io/clients/javascript/config/)) |
7878
| `disable_default_integrations` | `[]` | Provide a list of FQCN of default integrations you do not want to use. [List of default integrations](https://github.com/getsentry/sentry-php/tree/master/src/Integration).|
79+
| `cron_monitoring_enabled` | `false` | Wether to enable [cron check ins](https://docs.sentry.io/platforms/php/crons/#upserting-cron-monitors) |
80+
| `track_crons` | `[]` | Cron handles of crons to track with cron monitoring, [Sentry only supports 6 check-ins per minute](https://docs.sentry.io/platforms/php/crons/#rate-limits) Magento does many more. |
7981

8082
### Configuration for Adobe Cloud
8183
Since Adobe Cloud doesn't allow you to add manually add content to the `env.php` file, the configuration can be done

etc/di.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,9 @@
4949
<type name="Magento\Framework\DB\Logger\LoggerProxy">
5050
<plugin name="sentry-profiling" type="JustBetter\Sentry\Plugin\Profiling\DbQueryLoggerPlugin" disabled="false" />
5151
</type>
52+
53+
<!-- Cron Check-Ins -->
54+
<type name="Magento\Cron\Model\Schedule">
55+
<plugin name="sentry-cron-checkin" type="JustBetter\Sentry\Plugin\CronScheduleCheckIn" disabled="false"/>
56+
</type>
5257
</config>

0 commit comments

Comments
 (0)