|
|
@@ -3,6 +3,8 @@
|
|
|
package cron
|
|
|
|
|
|
import (
|
|
|
+ "log"
|
|
|
+ "runtime"
|
|
|
"sort"
|
|
|
"time"
|
|
|
)
|
|
|
@@ -16,6 +18,7 @@ type Cron struct {
|
|
|
add chan *Entry
|
|
|
snapshot chan []*Entry
|
|
|
running bool
|
|
|
+ ErrorLog *log.Logger
|
|
|
}
|
|
|
|
|
|
// Job is an interface for submitted cron jobs.
|
|
|
@@ -74,6 +77,7 @@ func New() *Cron {
|
|
|
stop: make(chan struct{}),
|
|
|
snapshot: make(chan []*Entry),
|
|
|
running: false,
|
|
|
+ ErrorLog: nil,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -127,6 +131,18 @@ func (c *Cron) Start() {
|
|
|
go c.run()
|
|
|
}
|
|
|
|
|
|
+func (c *Cron) runWithRecovery(j Job) {
|
|
|
+ defer func() {
|
|
|
+ if r := recover(); r != nil {
|
|
|
+ const size = 64 << 10
|
|
|
+ buf := make([]byte, size)
|
|
|
+ buf = buf[:runtime.Stack(buf, false)]
|
|
|
+ c.logf("cron: panic running job: %v\n%s", r, buf)
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ j.Run()
|
|
|
+}
|
|
|
+
|
|
|
// Run the scheduler.. this is private just due to the need to synchronize
|
|
|
// access to the 'running' state variable.
|
|
|
func (c *Cron) run() {
|
|
|
@@ -156,7 +172,7 @@ func (c *Cron) run() {
|
|
|
if e.Next != effective {
|
|
|
break
|
|
|
}
|
|
|
- go e.Job.Run()
|
|
|
+ go c.runWithRecovery(e.Job)
|
|
|
e.Prev = e.Next
|
|
|
e.Next = e.Schedule.Next(effective)
|
|
|
}
|
|
|
@@ -178,6 +194,15 @@ func (c *Cron) run() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// Logs an error to stderr or to the configured error log
|
|
|
+func (c *Cron) logf(format string, args ...interface{}) {
|
|
|
+ if c.ErrorLog != nil {
|
|
|
+ c.ErrorLog.Printf(format, args...)
|
|
|
+ } else {
|
|
|
+ log.Printf(format, args...)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
|
|
|
func (c *Cron) Stop() {
|
|
|
if !c.running {
|