CronPup

← Guides

Monitoring Go scheduled jobs (robfig/cron and plain cron)

Go services often schedule work in-process with github.com/robfig/cron/v3, or ship a small binary that system cron runs. Either way, a job that stops running looks just like a job that has nothing to do. A heartbeat makes the difference visible: the job pings when it finishes, and you're alerted when the ping is late or reports a failure.

A helper

package heartbeat

import (
	"log"
	"net/http"
	"time"
)

var client = &http.Client{Timeout: 5 * time.Second}

func ping(url string) {
	resp, err := client.Get(url)
	if err != nil {
		log.Printf("heartbeat ping failed: %v", err) // never let monitoring break the job
		return
	}
	resp.Body.Close()
}

// Run reports start, then success or failure (including panics) of fn.
func Run(url string, fn func() error) (err error) {
	ping(url + "/start")
	defer func() {
		if r := recover(); r != nil {
			ping(url + "/fail")
			panic(r)
		}
		if err != nil {
			ping(url + "/fail")
		} else {
			ping(url)
		}
	}()
	return fn()
}

With robfig/cron

loc, _ := time.LoadLocation("Europe/Berlin")
c := cron.New(cron.WithLocation(loc))
c.AddFunc("0 3 * * *", func() {
	if err := heartbeat.Run(os.Getenv("EXPORT_PING_URL"), runExport); err != nil {
		log.Printf("export failed: %v", err)
	}
})
c.Start()

Give the CronPup monitor the same expression, 0 3 * * *, and timezone, Europe/Berlin. Add a one-minute heartbeat too, so you hear about a dead process within minutes.

A binary run by system cron

No code changes needed: wrap the command with cronpup-run, which reports start, the exit code and the last lines of output:

0 3 * * *  cronpup-run https://cronpup.com/p/EXPORT-UUID /usr/local/bin/export --full

Monitor your Go jobs. 10 monitors are free.