Skip to content

Monitor Spring Boot scheduled tasks

A @Scheduled method fails quietly. The exception is logged and the next run tries again, or the scheduler thread is stuck behind a slow task, or the app instance that runs it was scaled to zero. A heartbeat check catches all of these: the task reports to Pingstack after it succeeds, and Pingstack alerts you when that report doesn’t arrive on time.

1. Create a heartbeat check that matches the schedule

Section titled “1. Create a heartbeat check that matches the schedule”

Spring cron expressions have six fields, starting with seconds:

@Scheduled(cron = "0 0 2 * * *", zone = "UTC")

Pingstack uses standard five-field cron. Drop the seconds field:

Spring Pingstack
0 0 2 * * * 0 2 * * *
0 30 6 * * MON-FRI 30 6 * * 1-5
0 0 */4 * * * 0 */4 * * *
fixedRate = 900000 Interval of 15 minutes

Create a heartbeat check with the converted schedule. Without zone, Spring uses the server’s timezone; set zone explicitly and choose the same timezone in Pingstack. See schedules and timezones.

Set a grace period longer than the task’s usual runtime. Attach a notification group.

Copy the location’s ping URL from the check in Pingstack and provide it as an environment variable, such as PINGSTACK_NIGHTLY_URL, through your platform’s secret settings. Map it to a property in application.yml:

pingstack:
nightly-url: ${PINGSTACK_NIGHTLY_URL:}
@Component
public class NightlyReport {
private static final Logger log = LoggerFactory.getLogger(NightlyReport.class);
private final RestClient http = RestClient.create();
private final ReportService reports;
@Value("${pingstack.nightly-url}")
private String pingUrl;
public NightlyReport(ReportService reports) {
this.reports = reports;
}
@Scheduled(cron = "0 0 2 * * *", zone = "UTC")
public void run() {
reports.sendNightly();
reportToPingstack();
}
private void reportToPingstack() {
if (pingUrl == null || pingUrl.isBlank()) return;
try {
http.get().uri(pingUrl).retrieve().toBodilessEntity();
} catch (RestClientException e) {
log.warn("Pingstack heartbeat failed: {}", e.getClass().getSimpleName());
}
}
}

The report runs only after sendNightly() returns, so a run that throws sends nothing and Pingstack alerts you when the deadline passes. Make sure scheduling is turned on with @EnableScheduling on a configuration class.

RestClient needs Spring Framework 6.1 or later. On older versions, use RestTemplate the same way, and set a timeout on whichever client you use.

Every instance runs @Scheduled methods, so each one reports, which hides an instance that stopped. Run the task on one instance with a lock such as ShedLock, or move it to a dedicated worker.

If you use Quartz instead, report from the end of the job’s execute method in the same way.

Call the method once, for example from a test profile or an admin endpoint you already have, with the same configuration. Open the check in Pingstack and confirm the location received a heartbeat and shows a next deadline.

Append a count as a numeric value and set warning and critical thresholds on the check:

int sent = reports.sendNightly();
http.get().uri(pingUrl + "?value=" + sent).retrieve().toBodilessEntity();
  • Single scheduler thread. By default, Spring runs scheduled tasks on one thread, so a slow task delays the others. Raise spring.task.scheduling.pool.size if tasks queue up.
  • Scaled to zero. A platform that stops idle instances stops their schedules too.
  • Timezone mismatch. The check and the zone attribute must agree.

If alerts still don’t match what you expect, see troubleshooting monitoring.