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

logging.mddocs/guides/observability/

Logging

Guide to configuring logging in gocron.

Overview

Gocron uses a logger interface for internal logging. You can use the default logger or provide a custom implementation.

Default Logger

By default, gocron uses a logger that outputs to stdout:

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

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)
}

Custom Logger

Basic Implementation

type myLogger struct{}

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

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

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

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

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

Integration with Popular Loggers

Zap

import "go.uber.org/zap"

type zapLogger struct {
    logger *zap.SugaredLogger
}

func (l *zapLogger) Debug(msg string, args ...any) {
    l.logger.Debugf(msg, args...)
}

func (l *zapLogger) Error(msg string, args ...any) {
    l.logger.Errorf(msg, args...)
}

func (l *zapLogger) Info(msg string, args ...any) {
    l.logger.Infof(msg, args...)
}

func (l *zapLogger) Warn(msg string, args ...any) {
    l.logger.Warnf(msg, args...)
}

zapLog, _ := zap.NewProduction()
defer zapLog.Sync()

s, _ := gocron.NewScheduler(
    gocron.WithLogger(&zapLogger{logger: zapLog.Sugar()}),
)

Logrus

import "github.com/sirupsen/logrus"

type logrusLogger struct {
    logger *logrus.Logger
}

func (l *logrusLogger) Debug(msg string, args ...any) {
    l.logger.Debugf(msg, args...)
}

func (l *logrusLogger) Error(msg string, args ...any) {
    l.logger.Errorf(msg, args...)
}

func (l *logrusLogger) Info(msg string, args ...any) {
    l.logger.Infof(msg, args...)
}

func (l *logrusLogger) Warn(msg string, args ...any) {
    l.logger.Warnf(msg, args...)
}

logrusLog := logrus.New()
logrusLog.SetLevel(logrus.InfoLevel)

s, _ := gocron.NewScheduler(
    gocron.WithLogger(&logrusLogger{logger: logrusLog}),
)

Zerolog

import "github.com/rs/zerolog"

type zerologLogger struct {
    logger zerolog.Logger
}

func (l *zerologLogger) Debug(msg string, args ...any) {
    l.logger.Debug().Msgf(msg, args...)
}

func (l *zerologLogger) Error(msg string, args ...any) {
    l.logger.Error().Msgf(msg, args...)
}

func (l *zerologLogger) Info(msg string, args ...any) {
    l.logger.Info().Msgf(msg, args...)
}

func (l *zerologLogger) Warn(msg string, args ...any) {
    l.logger.Warn().Msgf(msg, args...)
}

zerologLog := zerolog.New(os.Stdout).With().Timestamp().Logger()

s, _ := gocron.NewScheduler(
    gocron.WithLogger(&zerologLogger{logger: zerologLog}),
)

Log Levels

Control verbosity by implementing selective logging:

type levelLogger struct {
    level string // "debug", "info", "warn", "error"
}

func (l *levelLogger) Debug(msg string, args ...any) {
    if l.level == "debug" {
        log.Printf("[DEBUG] "+msg, args...)
    }
}

func (l *levelLogger) Info(msg string, args ...any) {
    if l.level == "debug" || l.level == "info" {
        log.Printf("[INFO] "+msg, args...)
    }
}

// ... similar for Warn and Error

s, _ := gocron.NewScheduler(
    gocron.WithLogger(&levelLogger{level: "info"}),
)

Structured Logging

Add context with structured fields:

type structuredLogger struct {
    logger *zap.SugaredLogger
}

func (l *structuredLogger) Debug(msg string, args ...any) {
    l.logger.With("component", "gocron").Debugf(msg, args...)
}

func (l *structuredLogger) Error(msg string, args ...any) {
    l.logger.With(
        "component", "gocron",
        "timestamp", time.Now(),
    ).Errorf(msg, args...)
}

// ... similar for Info and Warn

What Gets Logged

Gocron logs the following events:

  • Debug: Detailed execution flow
  • Info: Job scheduling, execution start/complete
  • Warn: Potential issues (concurrency limits, delays)
  • Error: Execution failures, system errors

Disabling Logs

Create a no-op logger to disable logging:

type noopLogger struct{}

func (l *noopLogger) Debug(msg string, args ...any) {}
func (l *noopLogger) Error(msg string, args ...any) {}
func (l *noopLogger) Info(msg string, args ...any) {}
func (l *noopLogger) Warn(msg string, args ...any) {}

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

Per-Job Logging

Add job-specific context:

j, _ := s.NewJob(
    gocron.DurationJob(time.Minute),
    gocron.NewTask(func() {
        log.Printf("[%s] Starting work", j.Name())
        doWork()
        log.Printf("[%s] Completed work", j.Name())
    }),
    gocron.WithName("my-job"),
)

Log Rotation

Integrate with log rotation systems:

import "gopkg.in/natefinch/lumberjack.v2"

logFile := &lumberjack.Logger{
    Filename:   "/var/log/gocron.log",
    MaxSize:    100, // MB
    MaxBackups: 3,
    MaxAge:     28, // days
    Compress:   true,
}

log.SetOutput(logFile)

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

Best Practices

1. Use Appropriate Log Levels

// Debug for development
l.Debug("Job scheduled: %v", nextRun)

// Info for normal operations
l.Info("Job started: %s", jobName)

// Warn for issues that don't prevent operation
l.Warn("Queue growing: %d jobs waiting", queueSize)

// Error for failures
l.Error("Job failed: %v", err)

2. Include Context

l.Info("Job completed: name=%s, duration=%v, status=%s",
    job.Name(), duration, status)

3. Avoid Logging in Hot Paths

// Bad: logs every iteration
for i := 0; i < 1000000; i++ {
    l.Debug("Processing item %d", i)
}

// Good: log summary
l.Info("Processed %d items", 1000000)

See Also

  • Observability Guide - Overview
  • Lifecycle Monitoring Guide - Event monitoring
  • API: Scheduler Creation - WithLogger option

Install with Tessl CLI

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

docs

index.md

tile.json