Spring Boot starter: @Scheduled and Quartz
The SilenceWatch Spring Boot starter monitors your application’s scheduled tasks without you declaring anything: it discovers every @Scheduled method and every Quartz job, declares them with their real schedule, and sends a heartbeat around every run. It is licensed under Apache 2.0, deliberately, so it can be embedded in any application.
Installation
Section titled “Installation”<dependency> <groupId>com.silencewatch</groupId> <artifactId>silencewatch-spring-boot-starter</artifactId> <version>0.1.0</version></dependency>implementation 'com.silencewatch:silencewatch-spring-boot-starter:0.1.0'silencewatch: api-key: ${SILENCEWATCH_API_KEY}Create the API key in your project’s Settings › API keys. That is the whole setup.
What it does at startup
Section titled “What it does at startup”On ApplicationReadyEvent, in one HTTP call:
- Spring: it reads the tasks Spring itself scheduled, so the schedules are the resolved ones:
@Scheduled(cron = "${backup.cron}")is declared with the expression the property actually held. - Quartz: if Quartz is present, it iterates the jobs and reads their triggers. A job with several triggers is declared from the one that fires most often, since that is the deadline a missed run breaches first.
- It declares everything to
POST /api/v1/checks/syncand keeps the returned ping keys in memory.
Each job gets a stable identity that ties it to its history across restarts, redeployments and renames: com.acme.jobs.BackupJob#run for Spring, group.jobName for Quartz. A job that disappears from the code is flagged orphaned, never deleted.
What it does at run time
Section titled “What it does at run time”A Spring AOP advisor (plain Spring AOP, no AspectJ) wraps every @Scheduled method; a JobListener does the same for Quartz:
POST /p/<key>/start when the run beginsPOST /p/<key>/0?duration_ms=1234 when it returnsPOST /p/<key>/fail?duration_ms=1234 when it throwsConfiguration
Section titled “Configuration”silencewatch: enabled: true # false removes everything, see below api-key: ${SILENCEWATCH_API_KEY} base-url: https://app.silencewatch.com # your own instance when self-hosting environment: production # part of a check's identity default-grace: 5m # for jobs that declare none auto-register: true # false to manage checks by hand report-start: true # false to send only the outcome timeout: 2s # per call, deliberately short queue-capacity: 1000 # bounded, oldest dropped when full timezone: Europe/Paris # for cron jobs whose trigger carries none discover-spring-tasks: true discover-quartz-jobs: trueRules it will not break
Section titled “Rules it will not break”These are what make it safe to put in a production application:
- It never fails your job. Every network or HTTP error is swallowed and logged at
WARN. The interceptor calls your method exactly once, returns its result untouched, and rethrows its exception unchanged. - It never blocks your job. Sending a heartbeat is a queue offer handled by a low-priority daemon thread. An unreachable SilenceWatch costs your job nothing.
- It is bounded. The queue holds at most
queue-capacityheartbeats; past that the oldest are dropped, because the freshest state is the one worth reporting. Warnings are limited to one a minute. - It degrades silently. No API key, no network, no server: the application starts and runs exactly as it would without the dependency.
silencewatch.enabled: falseremoves the mechanism entirely: no discovery, no proxying, no threads, no beans.- No heavy dependencies.
spring-boot-starterplus the JDK HTTP client. No JSON library, so no Jackson version is forced on your application.
Limits
Section titled “Limits”- Sub-30-second jobs are declared with a 30-second period, the shortest accepted, and a warning names them.
- Quartz’s
Wcannot be evaluated by the server, so those jobs are skipped with a warning rather than failing the whole declaration. - Programmatically scheduled tasks (
SchedulingConfigurer, lambdas) have no stable identity and are ignored. Declare those checks by hand, or give them a@Scheduledmethod. spring.aop.auto=falsedisables the proxying Spring Boot normally provides: jobs are declared but no heartbeat can be sent. The starter says so at startup.