feat: comprehensive INFO/WARN/ERROR logging across all controller modules

Add structured operational logging at INFO, WARN, and ERROR levels to
every controller module. Standardize custom prefixes ([GEO], [SCHED],
[SYNC]) to use [INFO/WARN/ERROR] [module] format. Fix misleveled logs
(WARN->ERROR for data loss scenarios, WARN->INFO for routine operations).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 19:58:27 +01:00
parent 95c821deb2
commit 8e61cd7ec4
44 changed files with 326 additions and 44 deletions
+14 -13
View File
@@ -75,7 +75,7 @@ func New(logger *log.Logger) *Scheduler {
// If the scheduler is already started, the job's goroutine is launched immediately.
func (s *Scheduler) Every(name string, interval time.Duration, fn JobFunc) {
if interval <= 0 {
s.logger.Printf("[ERROR] Periodic job %s has invalid interval %s — job not registered", name, interval)
s.logger.Printf("[ERROR] [scheduler] Periodic job %s has invalid interval %s — job not registered", name, interval)
return
}
@@ -88,7 +88,7 @@ func (s *Scheduler) Every(name string, interval time.Duration, fn JobFunc) {
Interval: interval,
}
s.jobs = append(s.jobs, job)
s.logger.Printf("[SCHED] Registered periodic job: %s (every %s)", name, interval)
s.logger.Printf("[INFO] [scheduler] Registered periodic job: %s (every %s)", name, interval)
s.dbg("periodic job registered: name=%q interval=%s totalJobs=%d", name, interval, len(s.jobs))
if s.started {
@@ -105,7 +105,7 @@ func (s *Scheduler) Daily(name string, timeStr string, fn JobFunc) {
// Validate time format
if _, _, err := parseDailyTime(timeStr); err != nil {
s.logger.Printf("[ERROR] Daily job %s has invalid schedule %q: %v — job not started", name, timeStr, err)
s.logger.Printf("[ERROR] [scheduler] Daily job %s has invalid schedule %q: %v — job not started", name, timeStr, err)
return
}
@@ -117,7 +117,7 @@ func (s *Scheduler) Daily(name string, timeStr string, fn JobFunc) {
s.jobs = append(s.jobs, job)
nextRun := nextDailyRun(timeStr)
s.logger.Printf("[SCHED] Daily job %s scheduled for %s", name, nextRun.Format("2006-01-02 15:04 MST"))
s.logger.Printf("[INFO] [scheduler] Daily job %s scheduled for %s", name, nextRun.Format("2006-01-02 15:04 MST"))
s.dbg("daily job registered: name=%q schedule=%q nextRun=%s totalJobs=%d", name, timeStr, nextRun.Format(time.RFC3339), len(s.jobs))
if s.started {
@@ -131,7 +131,7 @@ func (s *Scheduler) Start(ctx context.Context) {
s.mu.Lock()
if s.cancel != nil {
s.mu.Unlock()
s.logger.Println("[WARN] Scheduler already started — ignoring duplicate Start()")
s.logger.Println("[WARN] [scheduler] Scheduler already started — ignoring duplicate Start()")
return
}
s.ctx, s.cancel = context.WithCancel(ctx)
@@ -147,7 +147,7 @@ func (s *Scheduler) Start(ctx context.Context) {
}
}
s.logger.Printf("[SCHED] Scheduler started with %d jobs", len(s.jobs))
s.logger.Printf("[INFO] [scheduler] Starting scheduler with %d jobs", len(s.jobs))
s.dbg("scheduler started: periodic=%d daily=%d", func() int {
n := 0
for _, j := range s.jobs {
@@ -173,6 +173,7 @@ func (s *Scheduler) Stop() {
s.mu.Lock()
cancel := s.cancel
s.mu.Unlock()
s.logger.Printf("[INFO] [scheduler] Stopping scheduler")
if cancel != nil {
cancel()
}
@@ -185,9 +186,9 @@ func (s *Scheduler) Stop() {
select {
case <-done:
s.logger.Println("[SCHED] All jobs stopped")
s.logger.Println("[INFO] [scheduler] All jobs stopped")
case <-time.After(30 * time.Second):
s.logger.Println("[WARN] Scheduler stop timed out after 30s — some jobs may still be running")
s.logger.Println("[WARN] [scheduler] Scheduler stop timed out after 30s — some jobs may still be running")
}
}
@@ -251,7 +252,7 @@ func (s *Scheduler) executeJob(job *Job, quiet bool) {
s.mu.Lock()
if job.Running {
s.mu.Unlock()
s.logger.Printf("[WARN] Job %s still running, skipping", job.Name)
s.logger.Printf("[WARN] [scheduler] Job %s still running, skipping", job.Name)
return
}
job.Running = true
@@ -270,12 +271,12 @@ func (s *Scheduler) executeJob(job *Job, quiet bool) {
job.LastErr = fmt.Errorf("panic: %v", r)
job.LastRun = time.Now()
s.mu.Unlock()
s.logger.Printf("[ERROR] Job %s panicked: %v", job.Name, r)
s.logger.Printf("[ERROR] [scheduler] Job %s panicked: %v", job.Name, r)
}
}()
if !quiet {
s.logger.Printf("[SCHED] Running job: %s", job.Name)
s.logger.Printf("[INFO] [scheduler] Running job: %s", job.Name)
}
s.dbg("job %s: execution starting", job.Name)
@@ -289,10 +290,10 @@ func (s *Scheduler) executeJob(job *Job, quiet bool) {
s.mu.Unlock()
if err != nil {
s.logger.Printf("[WARN] Job %s failed: %v (took %s)", job.Name, err, elapsed.Round(time.Millisecond))
s.logger.Printf("[ERROR] [scheduler] Job %s failed: %v (took %s)", job.Name, err, elapsed.Round(time.Millisecond))
s.dbg("job %s: failed after %s: %v", job.Name, elapsed.Round(time.Millisecond), err)
} else if !quiet {
s.logger.Printf("[SCHED] Job %s completed (took %s)", job.Name, elapsed.Round(time.Millisecond))
s.logger.Printf("[INFO] [scheduler] Job %s completed (took %s)", job.Name, elapsed.Round(time.Millisecond))
}
s.dbg("job %s: finished in %s (err=%v)", job.Name, elapsed.Round(time.Millisecond), err)
}