diff --git a/cmd/uptop/main.go b/cmd/uptop/main.go index 900cb03..b58cb8d 100644 --- a/cmd/uptop/main.go +++ b/cmd/uptop/main.go @@ -6,7 +6,7 @@ import ( "errors" "flag" "fmt" - "log" + "log/slog" "net" "net/url" "os" @@ -40,7 +40,9 @@ var ( ) func main() { - log.SetOutput(os.Stderr) + slog.SetDefault(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ + Level: slog.LevelInfo, + }))) if len(os.Args) >= 2 { switch os.Args[1] { @@ -110,7 +112,7 @@ func parseTrustedProxies(raw string) []*net.IPNet { } _, ipnet, err := net.ParseCIDR(part) if err != nil { - fmt.Fprintf(os.Stderr, "WARNING: ignoring invalid UPTOP_TRUSTED_PROXIES entry %q: %v\n", part, err) + slog.Warn("ignoring invalid UPTOP_TRUSTED_PROXIES entry", "entry", part, "err", err) //nolint:gosec // structured slog, not format string continue } cidrs = append(cidrs, ipnet) @@ -127,21 +129,21 @@ func openStore(dbType, dsn string) store.Store { ss, err = store.NewSQLiteStore(dsn) } if err != nil { - fmt.Fprintf(os.Stderr, "database error: %v\n", err) + slog.Error("database connection failed", "err", err) os.Exit(1) } if encKey := os.Getenv("UPTOP_ENCRYPTION_KEY"); encKey != "" { enc, err := store.NewEncryptor(encKey) if err != nil { - fmt.Fprintf(os.Stderr, "encryption key error: %v\n", err) + slog.Error("encryption key invalid", "err", err) os.Exit(1) } ss.SetEncryptor(enc) } else { - fmt.Println("WARNING: No UPTOP_ENCRYPTION_KEY set. Alert credentials stored unencrypted.") + slog.Warn("no UPTOP_ENCRYPTION_KEY set, alert credentials stored unencrypted") } if err := ss.Init(context.Background()); err != nil { - fmt.Fprintf(os.Stderr, "database init error: %v\n", err) + slog.Error("database init failed", "err", err) os.Exit(1) } return ss @@ -166,7 +168,7 @@ func runApply(args []string) { f, err := config.LoadFile(*filePath) if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) + slog.Error("config load failed", "err", err) os.Exit(1) } @@ -175,7 +177,7 @@ func runApply(args []string) { Prune: *prune, }) if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) + slog.Error("config apply failed", "err", err) os.Exit(1) } @@ -193,12 +195,12 @@ func runExport(args []string) { f, err := config.Export(context.Background(), s) if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) + slog.Error("export failed", "err", err) os.Exit(1) } if err := config.WriteFile(f, *outPath); err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) + slog.Error("export write failed", "err", err) os.Exit(1) } } @@ -216,7 +218,7 @@ func runMigrateSecrets(args []string) { } enc, err := store.NewEncryptor(encKey) if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) + slog.Error("encryption key invalid", "err", err) os.Exit(1) } @@ -227,17 +229,17 @@ func runMigrateSecrets(args []string) { ss, err = store.NewSQLiteStore(*dsn) } if err != nil { - fmt.Fprintf(os.Stderr, "database error: %v\n", err) + slog.Error("database connection failed", "err", err) os.Exit(1) } if err := ss.Init(context.Background()); err != nil { - fmt.Fprintf(os.Stderr, "database init error: %v\n", err) + slog.Error("database init failed", "err", err) os.Exit(1) } alerts, err := ss.GetAllAlerts(context.Background()) if err != nil { - fmt.Fprintf(os.Stderr, "error loading alerts: %v\n", err) + slog.Error("failed to load alerts", "err", err) os.Exit(1) } @@ -245,7 +247,7 @@ func runMigrateSecrets(args []string) { migrated := 0 for _, a := range alerts { if err := ss.UpdateAlert(context.Background(), a.ID, a.Name, a.Type, a.Settings); err != nil { - fmt.Fprintf(os.Stderr, "error migrating alert %q: %v\n", a.Name, err) + slog.Error("alert migration failed", "alert", a.Name, "err", err) os.Exit(1) } migrated++ @@ -278,7 +280,7 @@ func runServe(args []string) { }() if cfg.AllowPrivateTargets { - fmt.Println("WARNING: Private target blocking disabled. Monitor URLs can reach internal networks.") + slog.Warn("private target blocking disabled, monitor URLs can reach internal networks") } if err := cluster.RunProbe(ctx, cluster.ProbeConfig{ @@ -290,7 +292,7 @@ func runServe(args []string) { Interval: 30, AllowPrivateTargets: cfg.AllowPrivateTargets, }); err != nil { - fmt.Fprintf(os.Stderr, "Probe error: %v\n", err) + slog.Error("probe failed", "err", err) } return } @@ -307,13 +309,13 @@ func runServe(args []string) { var dbErr error if *flagDBType == "postgres" { ss, dbErr = store.NewPostgresStore(*flagDSN) - fmt.Printf("Using PostgreSQL: %s\n", redactDSN(*flagDSN)) + slog.Info("database connected", "type", "postgres", "dsn", redactDSN(*flagDSN)) } else { ss, dbErr = store.NewSQLiteStore(*flagDSN) - fmt.Printf("Using SQLite: %s\n", *flagDSN) + slog.Info("database connected", "type", "sqlite", "dsn", *flagDSN) } if dbErr != nil { - fmt.Fprintf(os.Stderr, "database connection error: %v\n", dbErr) + slog.Error("database connection failed", "err", dbErr) os.Exit(1) } defer ss.Close() @@ -321,18 +323,18 @@ func runServe(args []string) { if cfg.EncryptionKey != "" { enc, err := store.NewEncryptor(cfg.EncryptionKey) if err != nil { - fmt.Fprintf(os.Stderr, "encryption key error: %v\n", err) + slog.Error("encryption key invalid", "err", err) os.Exit(1) } ss.SetEncryptor(enc) } else { - fmt.Println("WARNING: No UPTOP_ENCRYPTION_KEY set. Alert credentials stored unencrypted.") + slog.Warn("no UPTOP_ENCRYPTION_KEY set, alert credentials stored unencrypted") } kc := newKeyCache(ss) var s store.Store = &userInvalidatingStore{Store: ss, kc: kc} if err := s.Init(context.Background()); err != nil { - fmt.Fprintf(os.Stderr, "database init error: %v\n", err) + slog.Error("database init failed", "err", err) os.Exit(1) } if *demo { @@ -344,19 +346,19 @@ func runServe(args []string) { if *importKuma != "" { kb, err := importer.LoadKumaFile(*importKuma) if err != nil { - fmt.Fprintf(os.Stderr, "kuma import error: %v\n", err) + slog.Error("kuma import failed", "err", err) os.Exit(1) } backup := importer.ConvertKuma(kb) if err := s.ImportData(context.Background(), backup); err != nil { - fmt.Fprintf(os.Stderr, "import failed: %v\n", err) + slog.Error("import failed", "err", err) os.Exit(1) } fmt.Printf("Imported %d monitors and %d alerts from Uptime Kuma v%s\n", len(backup.Sites), len(backup.Alerts), kb.Version) } if cfg.AllowPrivateTargets { - fmt.Println("WARNING: Private target blocking disabled. Monitor URLs can reach internal networks.") + slog.Warn("private target blocking disabled, monitor URLs can reach internal networks") } eng := monitor.NewEngineWithOpts(s, cfg.AllowPrivateTargets) @@ -391,7 +393,7 @@ func runServe(args []string) { if localTUI { p := tea.NewProgram(tui.InitialModel(true, s, eng, version), tea.WithAltScreen(), tea.WithMouseCellMotion()) if _, err := p.Run(); err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) + slog.Error("TUI failed", "err", err) } } else { fmt.Println("uptop running in HEADLESS mode") @@ -408,12 +410,12 @@ func runServe(args []string) { defer shutdownCancel() if httpSrv != nil { if err := httpSrv.Shutdown(shutdownCtx); err != nil { - log.Printf("HTTP shutdown error: %v", err) + slog.Error("HTTP shutdown failed", "err", err) } } if sshSrv != nil { if err := sshSrv.Shutdown(shutdownCtx); err != nil { - log.Printf("SSH shutdown error: %v", err) + slog.Error("SSH shutdown failed", "err", err) } } } @@ -432,12 +434,12 @@ func startSSHServer(port int, db store.Store, eng *monitor.Engine, kc *keyCache) ), ) if err != nil { - fmt.Fprintf(os.Stderr, "SSH server error: %v\n", err) + slog.Error("SSH server failed", "err", err) return nil } go func() { if err := s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) { - log.Printf("SSH server error: %v", err) + slog.Error("SSH server failed", "err", err) } }() return s @@ -452,11 +454,11 @@ func seedDemoData(s store.Store) { fmt.Println("Seeding demo data...") if err := s.AddAlert(ctx, "Discord Ops", "discord", map[string]string{"url": "https://discord.com/api/webhooks/demo/token"}); err != nil { - log.Printf("demo seed: add alert: %v", err) + slog.Error("demo seed failed", "step", "add alert", "err", err) return } if err := s.AddAlert(ctx, "Slack Infra", "slack", map[string]string{"url": "https://hooks.slack.com/services/DEMO/WEBHOOK"}); err != nil { - log.Printf("demo seed: add alert: %v", err) + slog.Error("demo seed failed", "step", "add alert", "err", err) return } if err := s.AddAlert(ctx, "Email Oncall", "email", map[string]string{ @@ -464,7 +466,7 @@ func seedDemoData(s store.Store) { "user": "oncall@example.com", "pass": "replace-me", "from": "oncall@example.com", "to": "team@example.com", }); err != nil { - log.Printf("demo seed: add alert: %v", err) + slog.Error("demo seed failed", "step", "add alert", "err", err) return } @@ -488,7 +490,7 @@ func seedDemoData(s store.Store) { } for _, site := range demoSites { if err := s.AddSite(ctx, site); err != nil { - log.Printf("demo seed: add site %q: %v", site.Name, err) + slog.Error("demo seed failed", "step", "add site", "site", site.Name, "err", err) } } } @@ -511,7 +513,7 @@ func (c *keyCache) refresh() { // Keep the previous key set: a transient DB error must not lock every // admin out. Revocation still fails closed because Invalidate clears // the set immediately. - log.Printf("SSH key cache refresh failed: %v", err) + slog.Error("SSH key cache refresh failed", "err", err) return } keys := make([]ssh.PublicKey, 0, len(users)) @@ -620,7 +622,7 @@ func seedKeysFromEnv(s store.Store) { existing, err := s.GetAllUsers(ctx) if err != nil { - fmt.Fprintf(os.Stderr, "warning: could not check existing users: %v\n", err) + slog.Warn("could not check existing users", "err", err) return } @@ -637,7 +639,7 @@ func seedKeysFromEnv(s store.Store) { username := usernameFromKey(key, i, len(existing)+added) if err := s.AddUser(ctx, username, key, "admin"); err != nil { - fmt.Fprintf(os.Stderr, "warning: failed to seed user %q: %v\n", username, err) + slog.Warn("failed to seed user", "user", username, "err", err) //nolint:gosec // structured slog, not format string continue } fmt.Printf("Seeded admin user %q from %s\n", username, seedSource(i, len(keys), os.Getenv("UPTOP_ADMIN_KEY") != "")) diff --git a/internal/cluster/probe.go b/internal/cluster/probe.go index fa11be8..b5112dc 100644 --- a/internal/cluster/probe.go +++ b/internal/cluster/probe.go @@ -6,7 +6,7 @@ import ( "crypto/tls" "encoding/json" "fmt" - "log" + "log/slog" "net/http" "net/url" "sync" @@ -47,7 +47,7 @@ func RunProbe(ctx context.Context, cfg ProbeConfig) error { } if err := probeRegister(ctx, apiClient, cfg); err != nil { - log.Printf("Probe: initial registration failed: %v (will retry)", err) + slog.Error("probe initial registration failed", "err", err) } for { @@ -59,7 +59,7 @@ func RunProbe(ctx context.Context, cfg ProbeConfig) error { sites, err := probeFetchAssignments(ctx, apiClient, cfg) if err != nil { - log.Printf("Probe: failed to fetch assignments: %v", err) + slog.Error("probe failed to fetch assignments", "err", err) sleepCtx(ctx, 10*time.Second) continue } @@ -73,7 +73,7 @@ func RunProbe(ctx context.Context, cfg ProbeConfig) error { if len(results) > 0 { if err := probeReportResults(ctx, apiClient, cfg, results); err != nil { - log.Printf("Probe: failed to report results: %v", err) + slog.Error("probe failed to report results", "err", err) } } @@ -189,7 +189,7 @@ func probeReportResults(ctx context.Context, client *http.Client, cfg ProbeConfi if resp.StatusCode != 200 { return fmt.Errorf("results returned %d", resp.StatusCode) } - fmt.Printf("Probe: reported %d check results\n", len(results)) + slog.Info("probe reported check results", "count", len(results)) return nil } diff --git a/internal/server/server.go b/internal/server/server.go index 6c43046..1ebc7b0 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" "html/template" - "log" + "log/slog" "net" "net/http" "sort" @@ -64,11 +64,11 @@ func Start(cfg ServerConfig, s store.Store, eng *monitor.Engine) *http.Server { func (s *Server) Start() *http.Server { if s.cfg.ClusterKey == "" { - fmt.Println("WARNING: No UPTOP_CLUSTER_SECRET set. Cluster API endpoints are unauthenticated.") + slog.Warn("no UPTOP_CLUSTER_SECRET set, cluster API endpoints are unauthenticated") } if s.cfg.ClusterMode != "" && s.cfg.ClusterMode != "leader" && s.cfg.TLSCert == "" { - fmt.Println("WARNING: Cluster mode active without TLS. Secrets transmitted in cleartext.") + slog.Warn("cluster mode active without TLS, secrets transmitted in cleartext") } handler := s.routes() @@ -84,14 +84,14 @@ func (s *Server) Start() *http.Server { } go func() { if s.cfg.TLSCert != "" && s.cfg.TLSKey != "" { - fmt.Printf("HTTPS Server listening on %s\n", addr) + slog.Info("HTTPS server listening", "addr", addr) if err := httpSrv.ListenAndServeTLS(s.cfg.TLSCert, s.cfg.TLSKey); err != nil && err != http.ErrServerClosed { - log.Printf("HTTPS server error: %v", err) + slog.Error("HTTPS server failed", "err", err) } } else { - fmt.Printf("HTTP Server listening on %s\n", addr) + slog.Info("HTTP server listening", "addr", addr) if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Printf("HTTP server error: %v", err) + slog.Error("HTTP server failed", "err", err) } } }() @@ -139,7 +139,7 @@ func (s *Server) handlePush(w http.ResponseWriter, r *http.Request) { if token == "" { if qt := r.URL.Query().Get("token"); qt != "" { token = qt - log.Printf("DEPRECATED: push token in query string — use Authorization: Bearer header instead") + slog.Warn("push token in query string is deprecated, use Authorization: Bearer header") } } if token == "" { @@ -174,7 +174,7 @@ func (s *Server) handleExport(w http.ResponseWriter, r *http.Request) { } data, err := s.store.ExportData(r.Context()) if err != nil { - log.Printf("Export failed: %v", err) + slog.Error("export failed", "err", err) http.Error(w, "Export failed", http.StatusInternalServerError) return } @@ -202,7 +202,7 @@ func (s *Server) handleImport(w http.ResponseWriter, r *http.Request) { return } if err := s.store.ImportData(r.Context(), data); err != nil { - log.Printf("Import failed: %v", err) + slog.Error("import failed", "err", err) http.Error(w, "Import failed", http.StatusInternalServerError) return } @@ -221,13 +221,13 @@ func (s *Server) handleKumaImport(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestBody) var kb importer.KumaBackup if err := json.NewDecoder(r.Body).Decode(&kb); err != nil { - log.Printf("Invalid Kuma JSON: %v", err) + slog.Error("invalid Kuma JSON", "err", err) http.Error(w, "Invalid Kuma JSON", http.StatusBadRequest) return } backup := importer.ConvertKuma(&kb) if err := s.store.ImportData(r.Context(), backup); err != nil { - log.Printf("Kuma import failed: %v", err) + slog.Error("Kuma import failed", "err", err) http.Error(w, "Import failed", http.StatusInternalServerError) return } @@ -261,7 +261,7 @@ func (s *Server) handleProbeRegister(w http.ResponseWriter, r *http.Request) { if err := s.store.RegisterNode(r.Context(), models.ProbeNode{ ID: req.ID, Name: req.Name, Region: req.Region, Version: req.Version, }); err != nil { - log.Printf("Probe register failed: %v", err) + slog.Error("probe registration failed", "err", err) http.Error(w, "Registration failed", http.StatusInternalServerError) return } @@ -340,7 +340,7 @@ func (s *Server) handleProbeResults(w http.ResponseWriter, r *http.Request) { s.eng.IngestProbeResult(req.NodeID, result.SiteID, result.LatencyNs, result.IsUp, result.ErrorReason) } if err := s.store.UpdateNodeLastSeen(r.Context(), req.NodeID); err != nil { - log.Printf("Failed to update node last seen: %v", err) + slog.Error("node last-seen update failed", "err", err) } _ = json.NewEncoder(w).Encode(map[string]bool{"ok": true}) //nolint:errcheck } @@ -444,7 +444,7 @@ func loggingMiddleware(trusted []*net.IPNet, next http.Handler) http.Handler { sw := &statusWriter{ResponseWriter: w, code: 200} next.ServeHTTP(sw, r) path := strings.ReplaceAll(strings.ReplaceAll(r.URL.Path, "\n", ""), "\r", "") - log.Printf("%s %s %d %s %s", r.Method, path, sw.code, time.Since(start).Round(time.Millisecond), clientIP(r, trusted)) //nolint:gosec // path sanitized above + slog.Info("http request", "method", r.Method, "path", path, "status", sw.code, "duration", time.Since(start).Round(time.Millisecond), "ip", clientIP(r, trusted)) //nolint:gosec // structured slog, not format string }) } @@ -485,7 +485,7 @@ func renderStatusPage(w http.ResponseWriter, title string, eng *monitor.Engine) Sites []models.Site }{Title: title, Sites: sites} if err := statusTpl.Execute(w, data); err != nil { - log.Printf("Failed to render status page: %v", err) + slog.Error("status page render failed", "err", err) } } diff --git a/internal/store/postgres.go b/internal/store/postgres.go index 00511dd..125d3ea 100644 --- a/internal/store/postgres.go +++ b/internal/store/postgres.go @@ -2,7 +2,7 @@ package store import ( "database/sql" - "log" + "log/slog" _ "github.com/lib/pq" ) @@ -133,39 +133,39 @@ func (d *PostgresDialect) ResetSequenceOnEmpty(db *sql.DB, table string) {} func (d *PostgresDialect) ImportWipe(tx *sql.Tx) { if _, err := tx.Exec("TRUNCATE TABLE sites RESTART IDENTITY CASCADE"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "sites", "err", err) } if _, err := tx.Exec("TRUNCATE TABLE alerts RESTART IDENTITY CASCADE"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "alerts", "err", err) } if _, err := tx.Exec("TRUNCATE TABLE users RESTART IDENTITY CASCADE"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "users", "err", err) } if _, err := tx.Exec("TRUNCATE TABLE maintenance_windows RESTART IDENTITY CASCADE"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "maintenance_windows", "err", err) } if _, err := tx.Exec("TRUNCATE TABLE check_history RESTART IDENTITY CASCADE"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "check_history", "err", err) } if _, err := tx.Exec("TRUNCATE TABLE state_changes RESTART IDENTITY CASCADE"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "state_changes", "err", err) } if _, err := tx.Exec("TRUNCATE TABLE alert_health RESTART IDENTITY CASCADE"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "alert_health", "err", err) } } func (d *PostgresDialect) ImportResetSequences(tx *sql.Tx) { if _, err := tx.Exec("SELECT setval('sites_id_seq', (SELECT COALESCE(MAX(id), 1) FROM sites))"); err != nil { - log.Printf("sequence reset error: %v", err) + slog.Debug("sequence reset failed", "table", "sites", "err", err) } if _, err := tx.Exec("SELECT setval('alerts_id_seq', (SELECT COALESCE(MAX(id), 1) FROM alerts))"); err != nil { - log.Printf("sequence reset error: %v", err) + slog.Debug("sequence reset failed", "table", "alerts", "err", err) } if _, err := tx.Exec("SELECT setval('users_id_seq', (SELECT COALESCE(MAX(id), 1) FROM users))"); err != nil { - log.Printf("sequence reset error: %v", err) + slog.Debug("sequence reset failed", "table", "users", "err", err) } if _, err := tx.Exec("SELECT setval('maintenance_windows_id_seq', (SELECT COALESCE(MAX(id), 1) FROM maintenance_windows))"); err != nil { - log.Printf("sequence reset error: %v", err) + slog.Debug("sequence reset failed", "table", "maintenance_windows", "err", err) } } diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go index 53b1489..5f57b50 100644 --- a/internal/store/sqlite.go +++ b/internal/store/sqlite.go @@ -3,7 +3,7 @@ package store import ( "database/sql" "fmt" - "log" + "log/slog" _ "modernc.org/sqlite" ) @@ -141,44 +141,44 @@ func (d *SQLiteDialect) ResetSequenceOnEmpty(db *sql.DB, table string) { _ = db.QueryRow("SELECT COUNT(*) FROM " + table).Scan(&count) //nolint:errcheck if count == 0 { if _, err := db.Exec("DELETE FROM sqlite_sequence WHERE name=?", table); err != nil { - log.Printf("sequence cleanup error: %v", err) + slog.Debug("sequence cleanup failed", "table", table, "err", err) } } } func (d *SQLiteDialect) ImportWipe(tx *sql.Tx) { if _, err := tx.Exec("DELETE FROM sites"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "sites", "err", err) } if _, err := tx.Exec("DELETE FROM sqlite_sequence WHERE name='sites'"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "sqlite_sequence(sites)", "err", err) } if _, err := tx.Exec("DELETE FROM alerts"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "alerts", "err", err) } if _, err := tx.Exec("DELETE FROM sqlite_sequence WHERE name='alerts'"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "sqlite_sequence(alerts)", "err", err) } if _, err := tx.Exec("DELETE FROM users"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "users", "err", err) } if _, err := tx.Exec("DELETE FROM sqlite_sequence WHERE name='users'"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "sqlite_sequence(users)", "err", err) } if _, err := tx.Exec("DELETE FROM maintenance_windows"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "maintenance_windows", "err", err) } if _, err := tx.Exec("DELETE FROM sqlite_sequence WHERE name='maintenance_windows'"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "sqlite_sequence(maintenance_windows)", "err", err) } if _, err := tx.Exec("DELETE FROM check_history"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "check_history", "err", err) } if _, err := tx.Exec("DELETE FROM state_changes"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "state_changes", "err", err) } if _, err := tx.Exec("DELETE FROM alert_health"); err != nil { - log.Printf("import wipe error: %v", err) + slog.Debug("import wipe failed", "table", "alert_health", "err", err) } }