fix: standardize log prefixes, remove duplicates, add missing module tags
Second-pass logging cleanup: consistent [LEVEL] [module] format across all 41 files. Remove stale prefixes ([CF], [SYNC], [SCHED], [API], [STORAGE], [HEALTH], [ROLLBACK]). Remove 5 duplicate log lines. Gate ungated DEBUG lines. Fix wrong log levels (restore start WARN→INFO). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package backup
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
@@ -105,6 +106,7 @@ func DiscoverAppData(provider StackDataProvider, discoveredDBs []DiscoveredDB) [
|
||||
result = append(result, info)
|
||||
}
|
||||
|
||||
log.Printf("[INFO] [backup] Discovered app data: %d apps", len(result))
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ type BackupStatus struct {
|
||||
// NewManager creates a new backup manager.
|
||||
func NewManager(cfg *config.Config, pinger *monitor.Pinger, sett *settings.Settings, logger *log.Logger) *Manager {
|
||||
if cfg.Paths.SystemDataPath == "" {
|
||||
logger.Printf("[WARN] SystemDataPath is empty in config — SSD-only apps will not have correct backup paths")
|
||||
logger.Printf("[WARN] [backup] SystemDataPath is empty in config — SSD-only apps will not have correct backup paths")
|
||||
}
|
||||
dataDir := cfg.Paths.DataDir
|
||||
if dataDir == "" {
|
||||
@@ -178,7 +178,7 @@ func (m *Manager) GetAppDrivePath(stackName string) string {
|
||||
}
|
||||
}
|
||||
if m.systemDataPath == "" {
|
||||
m.logger.Printf("[ERROR] systemDataPath is empty — cannot determine drive for %s", stackName)
|
||||
m.logger.Printf("[ERROR] [backup] systemDataPath is empty — cannot determine drive for %s", stackName)
|
||||
}
|
||||
return m.systemDataPath
|
||||
}
|
||||
@@ -238,16 +238,16 @@ func (m *Manager) RunDBDumps(ctx context.Context) error {
|
||||
// runDBDumpsInternal is the implementation of RunDBDumps. Caller must hold the running flag.
|
||||
func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
|
||||
start := time.Now()
|
||||
m.logger.Printf("[INFO] Starting database dump run")
|
||||
m.logger.Printf("[INFO] [backup] Starting database dump run")
|
||||
|
||||
dbs, err := DiscoverDatabases(ctx, m.logger, m.isDebug())
|
||||
if err != nil {
|
||||
m.logger.Printf("[ERROR] Database discovery failed: %v", err)
|
||||
m.logger.Printf("[ERROR] [backup] Database discovery failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dbs) == 0 {
|
||||
m.logger.Printf("[INFO] No database containers found")
|
||||
m.logger.Printf("[INFO] [backup] No database containers found")
|
||||
m.mu.Lock()
|
||||
m.lastDBDump = &DBDumpStatus{
|
||||
LastRun: time.Now(),
|
||||
@@ -258,7 +258,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.logger.Printf("[INFO] Discovered %d database(s): %s", len(dbs), dbNames(dbs))
|
||||
m.logger.Printf("[INFO] [backup] Discovered %d database(s): %s", len(dbs), dbNames(dbs))
|
||||
|
||||
// Dump each DB to its app's drive path
|
||||
var results []DumpResult
|
||||
@@ -271,12 +271,12 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
|
||||
|
||||
// Skip if drive is disconnected or decommissioned
|
||||
if m.settings != nil && m.settings.IsDisconnected(drivePath) {
|
||||
m.logger.Printf("[WARN] Skipping DB dump for %s — drive disconnected: %s", db.StackName, drivePath)
|
||||
m.logger.Printf("[WARN] [backup] Skipping DB dump for %s — drive disconnected: %s", db.StackName, drivePath)
|
||||
summary = append(summary, fmt.Sprintf("SKIP %s (drive disconnected)", db.ContainerName))
|
||||
continue
|
||||
}
|
||||
if m.settings != nil && m.settings.IsDecommissioned(drivePath) {
|
||||
m.logger.Printf("[WARN] Skipping DB dump for %s — drive decommissioned: %s", db.StackName, drivePath)
|
||||
m.logger.Printf("[WARN] [backup] Skipping DB dump for %s — drive decommissioned: %s", db.StackName, drivePath)
|
||||
summary = append(summary, fmt.Sprintf("SKIP %s (drive decommissioned)", db.ContainerName))
|
||||
continue
|
||||
}
|
||||
@@ -289,7 +289,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
|
||||
if result.Error != nil {
|
||||
allOK = false
|
||||
summary = append(summary, fmt.Sprintf("FAIL %s: %v", result.DB.ContainerName, result.Error))
|
||||
m.logger.Printf("[ERROR] DB dump failed for %s: %v", result.DB.ContainerName, result.Error)
|
||||
m.logger.Printf("[ERROR] [backup] DB dump failed for %s: %v", result.DB.ContainerName, result.Error)
|
||||
} else {
|
||||
totalSize += result.Size
|
||||
summary = append(summary, fmt.Sprintf("OK %s (%s)", result.DB.ContainerName, humanizeBytes(result.Size)))
|
||||
@@ -306,7 +306,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
|
||||
cache.Error = result.Validation.Error
|
||||
}
|
||||
if err := m.settings.SetDBValidation(filename, cache); err != nil {
|
||||
m.logger.Printf("[WARN] Failed to cache validation for %s: %v", filename, err)
|
||||
m.logger.Printf("[WARN] [backup] Failed to cache validation for %s: %v", filename, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,7 +329,7 @@ func (m *Manager) runDBDumpsInternal(ctx context.Context) error {
|
||||
|
||||
if allOK {
|
||||
m.pinger.Ping(uuid, body)
|
||||
m.logger.Printf("[INFO] DB dump completed: %d databases, %s total (%s)",
|
||||
m.logger.Printf("[INFO] [backup] DB dump completed: %d databases, %s total (%s)",
|
||||
len(results), humanizeBytes(totalSize), duration.Round(time.Millisecond))
|
||||
} else {
|
||||
m.pinger.Fail(uuid, body)
|
||||
@@ -352,16 +352,16 @@ func (m *Manager) RunBackup(ctx context.Context) error {
|
||||
func (m *Manager) runBackupInternal(ctx context.Context) error {
|
||||
// Skip if a full drive migration is in progress
|
||||
if m.MigrationActiveCheck != nil && m.MigrationActiveCheck() {
|
||||
m.logger.Printf("[WARN] Skipping nightly backup — drive migration in progress")
|
||||
m.logger.Printf("[WARN] [backup] Skipping nightly backup — drive migration in progress")
|
||||
return nil
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
m.logger.Printf("[INFO] Starting restic backup (per-drive)")
|
||||
m.logger.Printf("[INFO] [backup] Starting restic backup (per-drive)")
|
||||
|
||||
driveStacks := m.groupStacksByDrive()
|
||||
if len(driveStacks) == 0 {
|
||||
m.logger.Printf("[INFO] No deployed stacks — skipping backup")
|
||||
m.logger.Printf("[INFO] [backup] No deployed stacks — skipping backup")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ func (m *Manager) runBackupInternal(ctx context.Context) error {
|
||||
duration.Round(time.Second))
|
||||
m.pinger.Ping(m.cfg.Monitoring.PingUUIDs.Backup, body)
|
||||
|
||||
m.logger.Printf("[INFO] Restic backup completed: %d drives, snapshot %s, %d new, %d changed, %s added (%s)",
|
||||
m.logger.Printf("[INFO] [backup] Restic backup completed: %d drives, snapshot %s, %d new, %d changed, %s added (%s)",
|
||||
driveCount, lastResult.SnapshotID, lastResult.FilesNew, lastResult.FilesChanged, lastResult.DataAdded,
|
||||
duration.Round(time.Millisecond))
|
||||
}
|
||||
@@ -454,11 +454,11 @@ func (m *Manager) runBackupInternal(ctx context.Context) error {
|
||||
func (m *Manager) backupDrive(ctx context.Context, drivePath string, stacks []StackSummary, infraPaths []string) (*SnapshotResult, error) {
|
||||
// Skip disconnected or decommissioned drives
|
||||
if m.settings != nil && m.settings.IsDisconnected(drivePath) {
|
||||
m.logger.Printf("[WARN] Skipping backup for drive %s — disconnected", drivePath)
|
||||
m.logger.Printf("[WARN] [backup] Skipping backup for drive %s — disconnected", drivePath)
|
||||
return nil, nil
|
||||
}
|
||||
if m.settings != nil && m.settings.IsDecommissioned(drivePath) {
|
||||
m.logger.Printf("[WARN] Skipping backup for drive %s — decommissioned", drivePath)
|
||||
m.logger.Printf("[WARN] [backup] Skipping backup for drive %s — decommissioned", drivePath)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -466,7 +466,7 @@ func (m *Manager) backupDrive(ctx context.Context, drivePath string, stacks []St
|
||||
|
||||
// Ensure repo is initialized
|
||||
if err := m.restic.EnsureInitialized(repoPath); err != nil {
|
||||
m.logger.Printf("[ERROR] Restic init failed for %s: %v", repoPath, err)
|
||||
m.logger.Printf("[ERROR] [backup] Restic init failed for %s: %v", repoPath, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -506,19 +506,19 @@ func (m *Manager) backupDrive(ctx context.Context, drivePath string, stacks []St
|
||||
}
|
||||
|
||||
tags := []string{"felhom", m.cfg.Customer.ID, filepath.Base(drivePath)}
|
||||
m.logger.Printf("[INFO] Backing up drive %s (%d apps, %d paths)", drivePath, len(stacks), len(paths))
|
||||
m.logger.Printf("[INFO] [backup] Backing up drive %s (%d apps, %d paths)", drivePath, len(stacks), len(paths))
|
||||
|
||||
result, err := m.restic.Snapshot(repoPath, paths, tags)
|
||||
if err != nil {
|
||||
m.logger.Printf("[ERROR] Restic backup failed for drive %s: %v", drivePath, err)
|
||||
m.logger.Printf("[ERROR] [backup] Restic backup failed for drive %s: %v", drivePath, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prune check (weekly — Sunday)
|
||||
if shouldPrune(m.cfg.Backup.PruneSchedule) {
|
||||
m.logger.Printf("[INFO] Running weekly prune for %s", repoPath)
|
||||
m.logger.Printf("[INFO] [backup] Running weekly prune for %s", repoPath)
|
||||
if err := m.restic.Prune(repoPath, m.cfg.Backup.Retention); err != nil {
|
||||
m.logger.Printf("[WARN] Restic prune failed for %s: %v", repoPath, err)
|
||||
m.logger.Printf("[WARN] [backup] Restic prune failed for %s: %v", repoPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +548,7 @@ func (m *Manager) TryRunDriveBackup(ctx context.Context, drivePath string) error
|
||||
driveStacks := m.groupStacksByDrive()
|
||||
stacks, ok := driveStacks[drivePath]
|
||||
if !ok || len(stacks) == 0 {
|
||||
m.logger.Printf("[INFO] No deployed stacks on drive %s — skipping backup", drivePath)
|
||||
m.logger.Printf("[INFO] [backup] No deployed stacks on drive %s — skipping backup", drivePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ func (m *Manager) TryRunDriveBackup(ctx context.Context, drivePath string) error
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
m.logger.Printf("[INFO] Single-drive backup for %s: snapshot %s, %d new, %d changed, %s added",
|
||||
m.logger.Printf("[INFO] [backup] Single-drive backup for %s: snapshot %s, %d new, %d changed, %s added",
|
||||
drivePath, result.SnapshotID, result.FilesNew, result.FilesChanged, result.DataAdded)
|
||||
}
|
||||
|
||||
@@ -572,12 +572,12 @@ func (m *Manager) TryRunDriveBackup(ctx context.Context, drivePath string) error
|
||||
|
||||
// RunIntegrityCheck runs restic check on all primary repos and pings healthchecks.
|
||||
func (m *Manager) RunIntegrityCheck(ctx context.Context) error {
|
||||
m.logger.Printf("[INFO] Starting restic integrity check")
|
||||
m.logger.Printf("[INFO] [backup] Starting restic integrity check")
|
||||
start := time.Now()
|
||||
|
||||
drives := m.activeDrives()
|
||||
if len(drives) == 0 {
|
||||
m.logger.Printf("[INFO] No active drives — skipping integrity check")
|
||||
m.logger.Printf("[INFO] [backup] No active drives — skipping integrity check")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ func (m *Manager) RunIntegrityCheck(ctx context.Context) error {
|
||||
m.logger.Printf("[DEBUG] RunIntegrityCheck: checking repo %s", repoPath)
|
||||
}
|
||||
if err := m.restic.Check(repoPath); err != nil {
|
||||
m.logger.Printf("[ERROR] Restic check failed for %s: %v", repoPath, err)
|
||||
m.logger.Printf("[ERROR] [backup] Restic check failed for %s: %v", repoPath, err)
|
||||
checkErr = err
|
||||
} else if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] RunIntegrityCheck: repo %s OK", repoPath)
|
||||
@@ -614,12 +614,12 @@ func (m *Manager) RunIntegrityCheck(ctx context.Context) error {
|
||||
m.mu.Unlock()
|
||||
|
||||
if checkErr != nil {
|
||||
m.logger.Printf("[ERROR] Restic integrity check failed (%s): %v", duration.Round(time.Second), checkErr)
|
||||
m.logger.Printf("[ERROR] [backup] Restic integrity check failed (%s): %v", duration.Round(time.Second), checkErr)
|
||||
m.pinger.Fail(uuid, fmt.Sprintf("restic check failed: %v", checkErr))
|
||||
return checkErr
|
||||
}
|
||||
|
||||
m.logger.Printf("[INFO] Restic integrity check passed (%d repos, %s)", len(drives), duration.Round(time.Second))
|
||||
m.logger.Printf("[INFO] [backup] Restic integrity check passed (%d repos, %s)", len(drives), duration.Round(time.Second))
|
||||
m.pinger.Ping(uuid, fmt.Sprintf("restic check passed (%d repos, %s)", len(drives), duration.Round(time.Second)))
|
||||
return nil
|
||||
}
|
||||
@@ -646,7 +646,7 @@ func (m *Manager) RunFullBackup(ctx context.Context) error {
|
||||
m.logger.Printf("[DEBUG] RunFullBackup: phase 1 — database dumps")
|
||||
}
|
||||
if err := m.runDBDumpsInternal(ctx); err != nil {
|
||||
m.logger.Printf("[WARN] DB dump had errors, continuing with backup anyway")
|
||||
m.logger.Printf("[WARN] [backup] DB dump had errors, continuing with backup anyway")
|
||||
}
|
||||
|
||||
// Step 2: Restic backup
|
||||
@@ -713,7 +713,7 @@ func (m *Manager) ListSnapshots(limit int) ([]SnapshotInfo, error) {
|
||||
}
|
||||
snapshots, err := m.restic.ListSnapshots(repoPath, 0)
|
||||
if err != nil {
|
||||
m.logger.Printf("[WARN] Could not list snapshots from %s: %v", repoPath, err)
|
||||
m.logger.Printf("[WARN] [backup] Could not list snapshots from %s: %v", repoPath, err)
|
||||
continue
|
||||
}
|
||||
for i := range snapshots {
|
||||
@@ -744,7 +744,7 @@ func (m *Manager) ListAllSnapshots(limit int) ([]SnapshotInfo, error) {
|
||||
}
|
||||
snapshots, err := m.restic.ListSnapshots(repoPath, 0)
|
||||
if err != nil {
|
||||
m.logger.Printf("[WARN] Could not list snapshots from %s: %v", repoPath, err)
|
||||
m.logger.Printf("[WARN] [backup] Could not list snapshots from %s: %v", repoPath, err)
|
||||
continue
|
||||
}
|
||||
for i := range snapshots {
|
||||
@@ -782,7 +782,7 @@ func (m *Manager) UnlockRepo(ctx context.Context, repoPath string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("restic unlock: %v (%s)", err, strings.TrimSpace(string(out)))
|
||||
}
|
||||
m.logger.Printf("[INFO] Restic repo unlocked: %s", repoPath)
|
||||
m.logger.Printf("[INFO] [backup] Restic repo unlocked: %s", repoPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -819,14 +819,14 @@ func (m *Manager) DumpStackDB(ctx context.Context, stackName string) error {
|
||||
}
|
||||
dumpDir := AppDBDumpPath(drivePath, stackName)
|
||||
|
||||
m.logger.Printf("[INFO] Running pre-backup DB dump for %s (%d database(s)) → %s", stackName, len(stackDBs), dumpDir)
|
||||
m.logger.Printf("[INFO] [backup] Running pre-backup DB dump for %s (%d database(s)) → %s", stackName, len(stackDBs), dumpDir)
|
||||
|
||||
for _, db := range stackDBs {
|
||||
result := DumpOne(ctx, db, dumpDir, m.logger, m.isDebug())
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("DB dump failed for %s: %w", result.DB.ContainerName, result.Error)
|
||||
}
|
||||
m.logger.Printf("[INFO] Pre-backup DB dump OK: %s (%s)", result.DB.ContainerName, humanizeBytes(result.Size))
|
||||
m.logger.Printf("[INFO] [backup] Pre-backup DB dump OK: %s (%s)", result.DB.ContainerName, humanizeBytes(result.Size))
|
||||
|
||||
// Persist validation to settings
|
||||
if m.settings != nil && result.FilePath != "" {
|
||||
@@ -973,16 +973,16 @@ func (m *Manager) saveSnapshotHistory() {
|
||||
}
|
||||
data, err := json.Marshal(m.snapshotHistory)
|
||||
if err != nil {
|
||||
m.logger.Printf("[WARN] Could not marshal snapshot history: %v", err)
|
||||
m.logger.Printf("[WARN] [backup] Could not marshal snapshot history: %v", err)
|
||||
return
|
||||
}
|
||||
tmp := m.snapshotHistoryFile + ".tmp"
|
||||
if err := os.WriteFile(tmp, data, 0644); err != nil {
|
||||
m.logger.Printf("[WARN] Could not write snapshot history tmp file: %v", err)
|
||||
m.logger.Printf("[WARN] [backup] Could not write snapshot history tmp file: %v", err)
|
||||
return
|
||||
}
|
||||
if err := os.Rename(tmp, m.snapshotHistoryFile); err != nil {
|
||||
m.logger.Printf("[WARN] Could not rename snapshot history file: %v", err)
|
||||
m.logger.Printf("[WARN] [backup] Could not rename snapshot history file: %v", err)
|
||||
return
|
||||
}
|
||||
m.logger.Printf("[INFO] [backup] Saved snapshot history (%d entries)", len(m.snapshotHistory))
|
||||
@@ -997,13 +997,13 @@ func (m *Manager) loadSnapshotHistoryFromFile() []SnapshotRecord {
|
||||
data, err := os.ReadFile(m.snapshotHistoryFile)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
m.logger.Printf("[WARN] Could not read snapshot history file: %v", err)
|
||||
m.logger.Printf("[WARN] [backup] Could not read snapshot history file: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var records []SnapshotRecord
|
||||
if err := json.Unmarshal(data, &records); err != nil {
|
||||
m.logger.Printf("[WARN] Could not parse snapshot history file: %v", err)
|
||||
m.logger.Printf("[WARN] [backup] Could not parse snapshot history file: %v", err)
|
||||
return nil
|
||||
}
|
||||
return records
|
||||
@@ -1033,7 +1033,7 @@ func (m *Manager) LoadSnapshotHistory() {
|
||||
}
|
||||
snapshots, err := m.restic.ListSnapshots(repoPath, 20)
|
||||
if err != nil {
|
||||
m.logger.Printf("[WARN] Could not load snapshot history from %s: %v", repoPath, err)
|
||||
m.logger.Printf("[WARN] [backup] Could not load snapshot history from %s: %v", repoPath, err)
|
||||
continue
|
||||
}
|
||||
allSnapshots = append(allSnapshots, snapshots...)
|
||||
@@ -1064,7 +1064,7 @@ func (m *Manager) LoadSnapshotHistory() {
|
||||
sort.Slice(m.snapshotHistory, func(i, j int) bool {
|
||||
return m.snapshotHistory[i].Time.Before(m.snapshotHistory[j].Time)
|
||||
})
|
||||
m.logger.Printf("[INFO] Loaded %d snapshots from persisted history (merged with %d restic entries)", len(persisted), len(allSnapshots))
|
||||
m.logger.Printf("[INFO] [backup] Loaded %d snapshots from persisted history (merged with %d restic entries)", len(persisted), len(allSnapshots))
|
||||
} else {
|
||||
// No persisted file — fall back to restic-only loading (first run)
|
||||
for _, s := range allSnapshots {
|
||||
@@ -1075,7 +1075,7 @@ func (m *Manager) LoadSnapshotHistory() {
|
||||
Success: true,
|
||||
})
|
||||
}
|
||||
m.logger.Printf("[INFO] Loaded %d historical snapshots from %d restic repos (no persisted history)", len(m.snapshotHistory), len(drives))
|
||||
m.logger.Printf("[INFO] [backup] Loaded %d historical snapshots from %d restic repos (no persisted history)", len(m.snapshotHistory), len(drives))
|
||||
}
|
||||
|
||||
if len(m.snapshotHistory) > 20 {
|
||||
@@ -1139,7 +1139,7 @@ func (m *Manager) RefreshCache(nextDBDump, nextBackup time.Time) {
|
||||
filename := filepath.Base(r.FilePath)
|
||||
if fv, ok := fileValidation[filename]; ok {
|
||||
m.lastDBDump.Results[i].Validation = fv
|
||||
m.logger.Printf("[INFO] Re-validated %s from disk: valid=%v tables=%d",
|
||||
m.logger.Printf("[INFO] [backup] Re-validated %s from disk: valid=%v tables=%d",
|
||||
filename, fv.Valid, fv.TableCount)
|
||||
}
|
||||
}
|
||||
@@ -1155,7 +1155,7 @@ func (m *Manager) RefreshCache(nextDBDump, nextBackup time.Time) {
|
||||
m.cacheTime = time.Now()
|
||||
m.mu.Unlock()
|
||||
|
||||
m.logger.Printf("[INFO] Backup status cache refreshed")
|
||||
m.logger.Printf("[INFO] [backup] Backup status cache refreshed")
|
||||
}
|
||||
|
||||
// GetFullStatus returns the cached backup status for page rendering.
|
||||
|
||||
@@ -117,7 +117,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
|
||||
})
|
||||
|
||||
start := time.Now()
|
||||
r.logger.Printf("[INFO] Cross-drive backup starting: %s → %s (rsync)",
|
||||
r.logger.Printf("[INFO] [backup] Cross-drive backup starting: %s → %s (rsync)",
|
||||
stackName, cfg.DestinationPath)
|
||||
|
||||
// Trigger fresh DB dump for this app before cross-drive backup
|
||||
@@ -126,7 +126,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
|
||||
r.logger.Printf("[DEBUG] RunAppBackup: triggering pre-backup DB dump for %s", stackName)
|
||||
}
|
||||
if err := r.dbDumper.DumpStackDB(ctx, stackName); err != nil {
|
||||
r.logger.Printf("[WARN] Pre-backup DB dump failed for %s: %v — proceeding with user data backup", stackName, err)
|
||||
r.logger.Printf("[WARN] [backup] Pre-backup DB dump failed for %s: %v — proceeding with user data backup", stackName, err)
|
||||
// Non-fatal: user data backup is still valuable without fresh dump
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
|
||||
duration := time.Since(start)
|
||||
|
||||
if runErr != nil {
|
||||
r.logger.Printf("[ERROR] Cross-drive backup failed: %s: %v", stackName, runErr)
|
||||
r.logger.Printf("[ERROR] [backup] Cross-drive backup failed: %s: %v", stackName, runErr)
|
||||
r.updateStatus(stackName, "error", runErr.Error(), duration, "")
|
||||
return runErr
|
||||
}
|
||||
@@ -171,7 +171,7 @@ func (r *CrossDriveRunner) RunAppBackup(ctx context.Context, stackName string) e
|
||||
}
|
||||
}
|
||||
|
||||
r.logger.Printf("[INFO] Cross-drive backup completed: %s (%s)", stackName, duration.Round(time.Second))
|
||||
r.logger.Printf("[INFO] [backup] Cross-drive backup completed: %s (%s)", stackName, duration.Round(time.Second))
|
||||
r.updateStatus(stackName, "ok", "", duration, sizeHuman)
|
||||
return nil
|
||||
}
|
||||
@@ -333,14 +333,14 @@ func (r *CrossDriveRunner) ValidateDestination(path string) error {
|
||||
r.logger.Printf("[DEBUG] ValidateDestination: path=%s, isMountPoint=%v", path, !onSystemDrive)
|
||||
}
|
||||
if onSystemDrive {
|
||||
r.logger.Printf("[WARN] Destination %s is not a separate mount point (system drive) — backup will proceed but data is not protected against drive failure", path)
|
||||
r.logger.Printf("[WARN] [backup] Destination %s is not a separate mount point (system drive) — backup will proceed but data is not protected against drive failure", path)
|
||||
}
|
||||
if !system.IsWritable(path) {
|
||||
return fmt.Errorf("destination %s is not writable", path)
|
||||
}
|
||||
di := system.GetDiskUsage(path)
|
||||
if di == nil {
|
||||
r.logger.Printf("[WARN] Cannot determine disk usage for %s — proceeding without space verification", path)
|
||||
r.logger.Printf("[WARN] [backup] Cannot determine disk usage for %s — proceeding without space verification", path)
|
||||
return nil
|
||||
}
|
||||
if r.debug {
|
||||
@@ -417,7 +417,9 @@ func (r *CrossDriveRunner) runRsyncBackup(ctx context.Context, stackName, destBa
|
||||
"--exclude", "backups/*.sql",
|
||||
"--exclude", "backups/*.dump",
|
||||
src, dst)
|
||||
r.logger.Printf("[DEBUG] rsync: %s → %s", src, dst)
|
||||
if r.debug {
|
||||
r.logger.Printf("[DEBUG] rsync: %s → %s", src, dst)
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if r.debug {
|
||||
@@ -437,7 +439,7 @@ func (r *CrossDriveRunner) runRsyncBackup(ctx context.Context, stackName, destBa
|
||||
return fmt.Errorf("creating DB dump dest dir: %w", err)
|
||||
}
|
||||
if err := r.copyStackDBDumps(stackName, dbDestDir); err != nil {
|
||||
r.logger.Printf("[WARN] Cross-drive DB dump copy failed for %s: %v", stackName, err)
|
||||
r.logger.Printf("[WARN] [backup] Cross-drive DB dump copy failed for %s: %v", stackName, err)
|
||||
// Non-fatal: user data is the primary concern
|
||||
}
|
||||
|
||||
@@ -451,9 +453,11 @@ func (r *CrossDriveRunner) runRsyncBackup(ctx context.Context, stackName, destBa
|
||||
src := strings.TrimRight(configSrcDir, "/") + "/"
|
||||
dst := strings.TrimRight(configDestDir, "/") + "/"
|
||||
cmd := exec.CommandContext(ctx, "rsync", "-a", "--delete", src, dst)
|
||||
r.logger.Printf("[DEBUG] rsync config: %s → %s", src, dst)
|
||||
if r.debug {
|
||||
r.logger.Printf("[DEBUG] rsync config: %s → %s", src, dst)
|
||||
}
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
r.logger.Printf("[WARN] Cross-drive config rsync failed for %s: %v (%s)", stackName, err, strings.TrimSpace(string(out)))
|
||||
r.logger.Printf("[WARN] [backup] Cross-drive config rsync failed for %s: %v (%s)", stackName, err, strings.TrimSpace(string(out)))
|
||||
// Non-fatal
|
||||
}
|
||||
}
|
||||
@@ -488,9 +492,6 @@ func (r *CrossDriveRunner) copyStackDBDumps(stackName, destDir string) error {
|
||||
}
|
||||
copied++
|
||||
}
|
||||
if copied > 0 {
|
||||
r.logger.Printf("[DEBUG] Copied %d DB dump file(s) to %s", copied, destDir)
|
||||
}
|
||||
r.logger.Printf("[INFO] [backup] Copied %d DB dumps for %s", copied, stackName)
|
||||
return nil
|
||||
}
|
||||
@@ -514,7 +515,7 @@ func (r *CrossDriveRunner) syncInfraConfig(ctx context.Context) {
|
||||
for dest := range destDrives {
|
||||
infraDir := SecondaryInfraPath(dest)
|
||||
if err := os.MkdirAll(infraDir, 0755); err != nil {
|
||||
r.logger.Printf("[WARN] Cannot create infra backup dir %s: %v", infraDir, err)
|
||||
r.logger.Printf("[WARN] [backup] Cannot create infra backup dir %s: %v", infraDir, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -524,7 +525,7 @@ func (r *CrossDriveRunner) syncInfraConfig(ctx context.Context) {
|
||||
stacksSrc := strings.TrimRight(r.stacksDir, "/") + "/"
|
||||
cmd := exec.CommandContext(ctx, "rsync", "-a", "--delete", stacksSrc, stacksDest)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
r.logger.Printf("[WARN] Infra rsync (stacks) failed for %s: %v (%s)", dest, err, strings.TrimSpace(string(out)))
|
||||
r.logger.Printf("[WARN] [backup] Infra rsync (stacks) failed for %s: %v (%s)", dest, err, strings.TrimSpace(string(out)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,11 +533,11 @@ func (r *CrossDriveRunner) syncInfraConfig(ctx context.Context) {
|
||||
if _, err := os.Stat(r.controllerYAMLPath); err == nil {
|
||||
yamlDest := filepath.Join(infraDir, "controller.yaml")
|
||||
if err := copyFile(r.controllerYAMLPath, yamlDest); err != nil {
|
||||
r.logger.Printf("[WARN] Cannot copy controller.yaml to %s: %v", yamlDest, err)
|
||||
r.logger.Printf("[WARN] [backup] Cannot copy controller.yaml to %s: %v", yamlDest, err)
|
||||
}
|
||||
}
|
||||
|
||||
r.logger.Printf("[INFO] Infrastructure config synced to %s", infraDir)
|
||||
r.logger.Printf("[INFO] [backup] Infrastructure config synced to %s", infraDir)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,11 +605,11 @@ func (r *CrossDriveRunner) AutoEnableSmallApps() {
|
||||
Schedule: "daily",
|
||||
}
|
||||
if err := r.sett.SetCrossDriveConfig(stack.Name, cfg); err != nil {
|
||||
r.logger.Printf("[WARN] Auto-enable Tier 2 failed for %s: %v", stack.Name, err)
|
||||
r.logger.Printf("[WARN] [backup] Auto-enable Tier 2 failed for %s: %v", stack.Name, err)
|
||||
continue
|
||||
}
|
||||
autoEnabled++
|
||||
r.logger.Printf("[INFO] Auto-enabled Tier 2 backup for %s → %s (no HDD mounts, daily rsync)", stack.Name, destPath)
|
||||
r.logger.Printf("[INFO] [backup] Auto-enabled Tier 2 backup for %s → %s (no HDD mounts, daily rsync)", stack.Name, destPath)
|
||||
}
|
||||
|
||||
if r.debug && autoEnabled > 0 {
|
||||
|
||||
@@ -117,7 +117,7 @@ func DiscoverDatabases(ctx context.Context, logger *log.Logger, debug bool) ([]D
|
||||
|
||||
// Get env vars from container
|
||||
if err := populateDBEnv(ctx, &db); err != nil {
|
||||
logger.Printf("[WARN] Could not read env vars for %s: %v", name, err)
|
||||
logger.Printf("[WARN] [backup] Could not read env vars for %s: %v", name, err)
|
||||
if debug {
|
||||
logger.Printf("[DEBUG] DiscoverDatabases: skipping %s — env read failed", name)
|
||||
}
|
||||
@@ -310,7 +310,7 @@ func DumpOne(ctx context.Context, db DiscoveredDB, dumpDir string, logger *log.L
|
||||
result.Validation.Valid, result.Validation.TableCount, result.Duration.Round(time.Millisecond))
|
||||
}
|
||||
|
||||
logger.Printf("[INFO] DB dump: %s → %s (%s, %s, %d tables)", db.ContainerName, filename,
|
||||
logger.Printf("[INFO] [backup] DB dump: %s → %s (%s, %s, %d tables)", db.ContainerName, filename,
|
||||
humanizeBytes(stat.Size()), result.Duration.Round(time.Millisecond), result.Validation.TableCount)
|
||||
|
||||
return result
|
||||
@@ -318,7 +318,6 @@ func DumpOne(ctx context.Context, db DiscoveredDB, dumpDir string, logger *log.L
|
||||
|
||||
// ValidateDump checks a SQL dump file for basic structural integrity.
|
||||
func ValidateDump(filePath string, dbType DBType) DumpValidation {
|
||||
log.Printf("[DEBUG] ValidateDump: %s (type=%s)", filePath, dbType)
|
||||
stat, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
return DumpValidation{Error: fmt.Sprintf("stat failed: %v", err)}
|
||||
@@ -331,7 +330,7 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
|
||||
|
||||
if stat.Size() < 100 {
|
||||
v.Error = "dump file too small (< 100 bytes)"
|
||||
log.Printf("[WARN] ValidateDump FAIL: %s — %s", filePath, v.Error)
|
||||
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s", filePath, v.Error)
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -340,7 +339,7 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
v.Error = fmt.Sprintf("read failed: %v", err)
|
||||
log.Printf("[WARN] ValidateDump FAIL: %s — %s", filePath, v.Error)
|
||||
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s", filePath, v.Error)
|
||||
return v
|
||||
}
|
||||
defer f.Close()
|
||||
@@ -359,7 +358,7 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
v.Error = fmt.Sprintf("hiba az olvasás közben: %v", err)
|
||||
log.Printf("[WARN] ValidateDump FAIL: %s — read error: %v", filePath, err)
|
||||
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — read error: %v", filePath, err)
|
||||
return v
|
||||
}
|
||||
break // EOF
|
||||
@@ -408,18 +407,17 @@ func ValidateDump(filePath string, dbType DBType) DumpValidation {
|
||||
case DBTypePostgres:
|
||||
v.Error = "PostgreSQL dump missing comment header"
|
||||
}
|
||||
log.Printf("[WARN] ValidateDump FAIL: %s — %s", filePath, v.Error)
|
||||
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s", filePath, v.Error)
|
||||
return v
|
||||
}
|
||||
|
||||
if tableCount == 0 {
|
||||
v.Error = "no CREATE TABLE statements found"
|
||||
log.Printf("[WARN] ValidateDump FAIL: %s — %s (header was found, scanned %d lines)", filePath, v.Error, lineNum)
|
||||
log.Printf("[WARN] [backup] ValidateDump FAIL: %s — %s (header was found, scanned %d lines)", filePath, v.Error, lineNum)
|
||||
return v
|
||||
}
|
||||
|
||||
v.Valid = true
|
||||
log.Printf("[DEBUG] ValidateDump OK: %s — %d tables, header found", filePath, tableCount)
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -570,7 +568,7 @@ func cleanupTmpFiles(dumpDir string, logger *log.Logger) {
|
||||
if info.ModTime().Before(cutoff) {
|
||||
path := filepath.Join(dumpDir, e.Name())
|
||||
os.Remove(path)
|
||||
logger.Printf("[INFO] Cleaned up stale tmp file: %s", e.Name())
|
||||
logger.Printf("[INFO] [backup] Cleaned up stale tmp file: %s", e.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func (r *ResticManager) EnsureInitialized(repoPath string) error {
|
||||
// Check if repo is already initialized
|
||||
configPath := filepath.Join(repoPath, "config")
|
||||
if _, err := os.Stat(configPath); err == nil {
|
||||
r.logger.Printf("[INFO] Restic repo already initialized at %s", repoPath)
|
||||
r.logger.Printf("[INFO] [backup] Restic repo already initialized at %s", repoPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func (r *ResticManager) EnsureInitialized(repoPath string) error {
|
||||
}
|
||||
|
||||
// Initialize repo
|
||||
r.logger.Printf("[INFO] Initializing restic repository at %s", repoPath)
|
||||
r.logger.Printf("[INFO] [backup] Initializing restic repository at %s", repoPath)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
@@ -108,7 +108,7 @@ func (r *ResticManager) EnsureInitialized(repoPath string) error {
|
||||
return fmt.Errorf("restic init failed: %v — %s", err, truncate(string(out), 200))
|
||||
}
|
||||
|
||||
r.logger.Printf("[INFO] Restic repository initialized successfully")
|
||||
r.logger.Printf("[INFO] [backup] Restic repository initialized successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func (r *ResticManager) Snapshot(repoPath string, paths []string, tags []string)
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
existingPaths = append(existingPaths, p)
|
||||
} else {
|
||||
r.logger.Printf("[WARN] Backup path does not exist, skipping: %s", p)
|
||||
r.logger.Printf("[WARN] [backup] Backup path does not exist, skipping: %s", p)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,10 +155,10 @@ func (r *ResticManager) Snapshot(repoPath string, paths []string, tags []string)
|
||||
errStr += string(exitErr.Stderr)
|
||||
}
|
||||
if strings.Contains(errStr, "lock") || strings.Contains(errStr, "locked") {
|
||||
r.logger.Printf("[WARN] Restic repo locked — attempting unlock")
|
||||
r.logger.Printf("[WARN] [backup] Restic repo locked — attempting unlock")
|
||||
unlockCmd := r.command(ctx, repoPath, "unlock")
|
||||
if unlockErr := unlockCmd.Run(); unlockErr != nil {
|
||||
r.logger.Printf("[WARN] Restic unlock failed: %v", unlockErr)
|
||||
r.logger.Printf("[WARN] [backup] Restic unlock failed: %v", unlockErr)
|
||||
}
|
||||
// Retry once with a fresh context (H9 fix — original may be nearly expired).
|
||||
retryCtx, retryCancel := context.WithTimeout(context.Background(), 30*time.Minute)
|
||||
@@ -239,7 +239,7 @@ func (r *ResticManager) Prune(repoPath string, retention config.RetentionConfig)
|
||||
if r.debug {
|
||||
r.logger.Printf("[DEBUG] [restic] Prune: completed in %s, output=%d bytes", time.Since(start), len(out))
|
||||
}
|
||||
r.logger.Printf("[INFO] Restic prune completed for %s", repoPath)
|
||||
r.logger.Printf("[INFO] [backup] Restic prune completed for %s", repoPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -420,19 +420,19 @@ func (r *ResticManager) RestoreAppData(repoPath string, snapshotID string, paths
|
||||
}
|
||||
start := time.Now()
|
||||
|
||||
r.logger.Printf("[WARN] RESTORE started: repo=%s, snapshot=%s, paths=%v", repoPath, snapshotID, paths)
|
||||
r.logger.Printf("[INFO] [backup] Restore started: repo=%s, snapshot=%s, paths=%v", repoPath, snapshotID, paths)
|
||||
|
||||
cmd := r.command(ctx, repoPath, args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
r.logger.Printf("[ERROR] Restore failed: %v, output: %s", err, truncate(string(output), 500))
|
||||
r.logger.Printf("[ERROR] [backup] Restore failed: %v, output: %s", err, truncate(string(output), 500))
|
||||
return fmt.Errorf("restic restore failed: %w", err)
|
||||
}
|
||||
|
||||
if r.debug {
|
||||
r.logger.Printf("[DEBUG] [restic] RestoreAppData: completed in %s, output=%d bytes", time.Since(start), len(output))
|
||||
}
|
||||
r.logger.Printf("[INFO] RESTORE completed: snapshot=%s, paths=%v", snapshotID, paths)
|
||||
r.logger.Printf("[INFO] [backup] Restore completed: snapshot=%s, paths=%v", snapshotID, paths)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -483,8 +483,8 @@ func (r *ResticManager) generatePassword() error {
|
||||
return fmt.Errorf("writing password file: %w", err)
|
||||
}
|
||||
|
||||
r.logger.Printf("[INFO] Generated new restic repository password at %s", r.passwordFile)
|
||||
r.logger.Printf("[WARN] Save this password externally — losing it means losing access to ALL backups")
|
||||
r.logger.Printf("[INFO] [backup] Generated new restic repository password at %s", r.passwordFile)
|
||||
r.logger.Printf("[WARN] [backup] Save this password externally — losing it means losing access to ALL backups")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user