CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/golang-github-com-go-co-op-gocron-v2

A Golang job scheduling library that lets you run Go functions at pre-determined intervals using cron expressions, fixed durations, daily, weekly, monthly, or one-time schedules with support for distributed deployments.

Overview
Eval results
Files

observability.mddocs/guides/

Observability

Guide to monitoring and debugging gocron applications.

Overview

Gocron provides multiple observability mechanisms:

  1. Logging: Built-in and custom loggers
  2. Metrics: Job and scheduler metrics
  3. Events: Lifecycle event listeners
  4. Monitoring: Comprehensive monitoring interface

Logging

Built-In Logger

Default logger outputs to stdout:

s, _ := gocron.NewScheduler()
// Uses default logger

Custom Logger

Implement Logger interface:

type Logger interface {
    Debug(msg string, args ...any)
    Error(msg string, args ...any)
    Info(msg string, args ...any)
    Warn(msg string, args ...any)
}

type myLogger struct{}

func (l *myLogger) Debug(msg string, args ...any) {
    log.Printf("[DEBUG] "+msg, args...)
}

// ... implement other methods

s, _ := gocron.NewScheduler(
    gocron.WithLogger(&myLogger{}),
)

Detailed guide: Logging

Metrics

Track job execution metrics:

type myMonitor struct{}

func (m *myMonitor) RecordJobTiming(
    start time.Time,
    duration time.Duration,
    jobID uuid.UUID,
    jobName string,
    tags []string,
) {
    // Send to Prometheus, StatsD, etc.
    prometheus.JobDuration.Observe(duration.Seconds())
}

Detailed guide: Metrics

Lifecycle Monitoring

Comprehensive monitoring with SchedulerMonitor:

type myMonitor struct{}

func (m *myMonitor) JobScheduled(jobID uuid.UUID, job gocron.Job) {
    log.Printf("Scheduled: %s at %v", job.Name(), job.NextRun())
}

func (m *myMonitor) JobStarted(jobID uuid.UUID, job gocron.Job) {
    log.Printf("Started: %s", job.Name())
}

func (m *myMonitor) JobCompleted(jobID uuid.UUID, job gocron.Job, err error) {
    if err != nil {
        log.Printf("Failed: %s - %v", job.Name(), err)
    } else {
        log.Printf("Completed: %s", job.Name())
    }
}

s, _ := gocron.NewScheduler(
    gocron.WithSchedulerMonitor(&myMonitor{}),
)

Detailed guide: Lifecycle Monitoring

Event Listeners

Job-level event hooks:

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(myFunc),
    gocron.WithEventListeners(
        gocron.BeforeJobRuns(func(jobID uuid.UUID, jobName string) {
            log.Printf("Starting: %s", jobName)
        }),
        gocron.AfterJobRuns(func(jobID uuid.UUID, jobName string) {
            log.Printf("Completed: %s", jobName)
        }),
        gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) {
            log.Printf("Failed: %s - %v", jobName, err)
            // Alert, retry, etc.
        }),
    ),
)

Error Handling

Job Errors

Errors returned by task functions:

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(func() error {
        if err := doWork(); err != nil {
            return fmt.Errorf("work failed: %w", err)
        }
        return nil
    }),
    gocron.WithEventListeners(
        gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) {
            log.Printf("Job error: %v", err)
            // Handle error
        }),
    ),
)

Scheduler Errors

Check errors during operations:

j, err := s.NewJob(
    gocron.CronJob("invalid", false),
    gocron.NewTask(myFunc),
)
if err != nil {
    if errors.Is(err, gocron.ErrCronJobParse) {
        log.Printf("Invalid cron: %v", err)
    }
}

Health Checks

Implement health endpoints:

http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
    jobs := s.Jobs()

    healthy := true
    for _, j := range jobs {
        lastRun, _ := j.LastRun()
        nextRun, _ := j.NextRun()

        // Check if job is running as expected
        if time.Since(lastRun) > 10*time.Minute {
            healthy = false
            break
        }
    }

    if healthy {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("healthy"))
    } else {
        w.WriteHeader(http.StatusServiceUnavailable)
        w.Write([]byte("unhealthy"))
    }
})

Debugging

Job Status

// List all jobs
jobs := s.Jobs()
for _, j := range jobs {
    log.Printf("Job: %s", j.Name())
    log.Printf("  ID: %s", j.ID())
    log.Printf("  Next: %v", j.NextRun())
    log.Printf("  Last: %v", j.LastRun())
    log.Printf("  Tags: %v", j.Tags())
}

Scheduler Status

// Check running status
jobs := s.Jobs()
log.Printf("Total jobs: %d", len(jobs))
log.Printf("Jobs waiting: %d", s.JobsWaitingInQueue())

Integration Examples

Prometheus

var (
    jobDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name: "gocron_job_duration_seconds",
            Help: "Job execution duration",
        },
        []string{"job_name"},
    )
)

func init() {
    prometheus.MustRegister(jobDuration)
}

type prometheusMonitor struct{}

func (m *prometheusMonitor) RecordJobTiming(
    start time.Time,
    duration time.Duration,
    jobID uuid.UUID,
    jobName string,
    tags []string,
) {
    jobDuration.WithLabelValues(jobName).Observe(duration.Seconds())
}

StatsD

type statsdMonitor struct {
    client *statsd.Client
}

func (m *statsdMonitor) RecordJobTiming(
    start time.Time,
    duration time.Duration,
    jobID uuid.UUID,
    jobName string,
    tags []string,
) {
    m.client.Timing("gocron.job.duration", duration, tags, 1.0)
}

Troubleshooting

Common issues and solutions.

Detailed guide: Troubleshooting

See Also

Install with Tessl CLI

npx tessl i tessl/golang-github-com-go-co-op-gocron-v2@2.19.1

docs

index.md

tile.json