diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fa2f94..72e9c41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ ## Changelog +### v0.32.3 — Logging cleanup: consistent tags, dedup, standardized prefixes (2026-02-26) + +#### Fixed +- **All modules**: Standardized `[LEVEL] [module]` format across every log line — added missing module tags (`[stacks]`, `[backup]`, `[cloudflare]`, `[sync]`, `[scheduler]`, `[storage]`, `[monitor]`, `[metrics]`, `[report]`, `[settings]`, `[setup]`, `[api]`, `[integrations]`, `[selfupdate]`, `[assets]`, `[web]`) +- **Removed duplicate logs**: ScanStacks double completion, GetLogs INFO+DEBUG, LoadAppConfig WARN+DEBUG, copyStackDBDumps DEBUG+INFO, invalidateAllSessions INFO+DEBUG +- **Standardized stale prefixes**: `[CF]`/`[CF-DEBUG]` → `[INFO/DEBUG] [cloudflare]`, `[SYNC]` → `[LEVEL] [sync]`, `[SCHED]` → `[LEVEL] [scheduler]`, `[API]` → `[LEVEL] [api]`, `[STORAGE]` → `[storage]`, `[HEALTH]` → `[monitor]`, `[ROLLBACK]`/`[ROLLBACK-ERROR]` → `[LEVEL] [storage]`, `[DEBUG-SIM]` → `(simulation)` +- **Fixed wrong log levels**: Restic restore start WARN→INFO, ungated DEBUG lines in crossdrive/dbdump/onlyoffice/alerts removed or gated +- **Improved vague messages**: Settings SetDisconnected/SetDecommissioned now include storage path and migration target +- **Added missing logs**: `execCommand()` error, `DiscoverAppData()` completion, `BuildInfraBackup()` completion + ### v0.32.2 — Comprehensive INFO/WARN/ERROR logging across all modules (2026-02-26) #### Added diff --git a/controller/internal/api/geo.go b/controller/internal/api/geo.go index 50f13f0..47ac3bf 100644 --- a/controller/internal/api/geo.go +++ b/controller/internal/api/geo.go @@ -66,18 +66,18 @@ func (r *Router) geoUpdateSettings(w http.ResponseWriter, req *http.Request) { } if err := r.sett.SetGeoRestriction(geo); err != nil { - r.logger.Printf("[API] Failed to save geo settings: %v", err) + r.logger.Printf("[ERROR] [api] Failed to save geo settings: %v", err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } - r.logger.Printf("[API] Geo settings updated: enabled=%v, countries=%v", body.Enabled, body.AllowedCountries) + r.logger.Printf("[INFO] [api] Geo settings updated: enabled=%v, countries=%v", body.Enabled, body.AllowedCountries) // Trigger async CF sync if r.geoSync != nil { go func() { if err := r.geoSync.Sync(context.Background()); err != nil { - r.logger.Printf("[API] Geo sync after settings update failed: %v", err) + r.logger.Printf("[ERROR] [api] Geo sync after settings update failed: %v", err) } }() } @@ -93,7 +93,7 @@ func (r *Router) geoTriggerSync(w http.ResponseWriter, _ *http.Request) { go func() { if err := r.geoSync.Sync(context.Background()); err != nil { - r.logger.Printf("[API] Manual geo sync failed: %v", err) + r.logger.Printf("[ERROR] [api] Manual geo sync failed: %v", err) } }() @@ -135,18 +135,18 @@ func (r *Router) geoSetAppOverride(w http.ResponseWriter, req *http.Request, app override := &settings.AppGeoOverride{AllowedCountries: body.AllowedCountries} if err := r.sett.SetGeoAppOverride(appName, override); err != nil { - r.logger.Printf("[API] Failed to save geo override for %s: %v", appName, err) + r.logger.Printf("[ERROR] [api] Failed to save geo override for %s: %v", appName, err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } - r.logger.Printf("[API] Geo override set for %s: countries=%v", appName, body.AllowedCountries) + r.logger.Printf("[INFO] [api] Geo override set for %s: countries=%v", appName, body.AllowedCountries) // Trigger async CF sync if r.geoSync != nil { go func() { if err := r.geoSync.Sync(context.Background()); err != nil { - r.logger.Printf("[API] Geo sync after app override failed: %v", err) + r.logger.Printf("[ERROR] [api] Geo sync after app override failed: %v", err) } }() } @@ -161,18 +161,18 @@ func (r *Router) geoRemoveAppOverride(w http.ResponseWriter, _ *http.Request, ap } if err := r.sett.RemoveGeoAppOverride(appName); err != nil { - r.logger.Printf("[API] Failed to remove geo override for %s: %v", appName, err) + r.logger.Printf("[ERROR] [api] Failed to remove geo override for %s: %v", appName, err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } - r.logger.Printf("[API] Geo override removed for %s", appName) + r.logger.Printf("[INFO] [api] Geo override removed for %s", appName) // Trigger async CF sync if r.geoSync != nil { go func() { if err := r.geoSync.Sync(context.Background()); err != nil { - r.logger.Printf("[API] Geo sync after override removal failed: %v", err) + r.logger.Printf("[ERROR] [api] Geo sync after override removal failed: %v", err) } }() } diff --git a/controller/internal/api/router.go b/controller/internal/api/router.go index 906e187..ba631f1 100644 --- a/controller/internal/api/router.go +++ b/controller/internal/api/router.go @@ -314,14 +314,14 @@ func (r *Router) listStacks(w http.ResponseWriter, _ *http.Request) { } func (r *Router) rescanStacks(w http.ResponseWriter, _ *http.Request) { - r.logger.Printf("[API] Manual stack rescan requested") + r.logger.Printf("[INFO] [api] Manual stack rescan requested") if err := r.stackMgr.ScanStacks(); err != nil { - r.logger.Printf("[API] Stack rescan failed: %v", err) + r.logger.Printf("[ERROR] [api] Stack rescan failed: %v", err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } stackCount := len(r.stackMgr.GetStacks()) - r.logger.Printf("[API] Stack rescan completed: %d stacks found", stackCount) + r.logger.Printf("[INFO] [api] Stack rescan completed: %d stacks found", stackCount) writeJSON(w, http.StatusOK, apiResponse{ OK: true, Message: fmt.Sprintf("Rescan completed: %d stacks found", stackCount), @@ -355,7 +355,7 @@ func (r *Router) getDeployFields(w http.ResponseWriter, _ *http.Request, name st func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name string) { limitBody(w, req) - r.logger.Printf("[API] Deploy requested for stack: %s", name) + r.logger.Printf("[INFO] [api] Deploy requested for stack: %s", name) r.dbg("deployStack: name=%s contentLength=%d", name, req.ContentLength) var body struct { @@ -373,7 +373,7 @@ func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name stri warning, err := r.stackMgr.DeployStack(deployReq) if err != nil { - r.logger.Printf("[API] Deploy failed for %s: %v", name, err) + r.logger.Printf("[ERROR] [api] Deploy failed for %s: %v", name, err) status := http.StatusInternalServerError if strings.Contains(err.Error(), "already deployed") { status = http.StatusConflict @@ -412,7 +412,7 @@ func (r *Router) deployStack(w http.ResponseWriter, req *http.Request, name stri } func (r *Router) actionStack(w http.ResponseWriter, action, name string) { - r.logger.Printf("[API] %s requested for stack: %s", action, name) + r.logger.Printf("[INFO] [api] %s requested for stack: %s", action, name) r.dbg("actionStack: action=%s name=%s", action, name) // Protected stacks only allow restart — block all other actions @@ -483,7 +483,7 @@ func (r *Router) actionStack(w http.ResponseWriter, action, name string) { func (r *Router) updateOptionalConfig(w http.ResponseWriter, req *http.Request, name string) { limitBody(w, req) - r.logger.Printf("[API] Optional config update requested for stack: %s", name) + r.logger.Printf("[INFO] [api] Optional config update requested for stack: %s", name) var body struct { Values map[string]string `json:"values"` @@ -494,7 +494,7 @@ func (r *Router) updateOptionalConfig(w http.ResponseWriter, req *http.Request, } if err := r.stackMgr.UpdateOptionalConfig(name, body.Values); err != nil { - r.logger.Printf("[API] Optional config update failed for %s: %v", name, err) + r.logger.Printf("[ERROR] [api] Optional config update failed for %s: %v", name, err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } @@ -535,11 +535,11 @@ func (r *Router) toggleIntegration(w http.ResponseWriter, req *http.Request, pro if !body.Enabled { action = "disable" } - r.logger.Printf("[API] Integration %s requested: %s:%s", action, provider, target) + r.logger.Printf("[INFO] [api] Integration %s requested: %s:%s", action, provider, target) state, err := r.integrationMgr.Toggle(req.Context(), provider, target, body.Enabled) if err != nil { - r.logger.Printf("[API] Integration toggle failed for %s:%s: %v", provider, target, err) + r.logger.Printf("[ERROR] [api] Integration toggle failed for %s:%s: %v", provider, target, err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } @@ -605,7 +605,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri return } limitBody(w, req) - r.logger.Printf("[API] Remove requested for stack: %s", name) + r.logger.Printf("[INFO] [api] Remove requested for stack: %s", name) r.dbg("removeStack: name=%s", name) var body struct { @@ -632,7 +632,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri resp, err := r.stackMgr.RemoveStack(name, body.RemoveHDDData, backupPaths) if err != nil { - r.logger.Printf("[API] Remove failed for %s: %v", name, err) + r.logger.Printf("[ERROR] [api] Remove failed for %s: %v", name, err) status := http.StatusInternalServerError if strings.Contains(err.Error(), "protected") { status = http.StatusForbidden @@ -650,7 +650,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri // Clean up cross-drive backup config for this stack if r.sett != nil { if err := r.sett.SetCrossDriveConfig(name, nil); err != nil { - r.logger.Printf("[WARN] Failed to clean cross-drive config for %s: %v", name, err) + r.logger.Printf("[WARN] [api] Failed to clean cross-drive config for %s: %v", name, err) } } @@ -674,7 +674,7 @@ func (r *Router) removeStack(w http.ResponseWriter, req *http.Request, name stri func (r *Router) deleteStack(w http.ResponseWriter, req *http.Request, name string) { limitBody(w, req) - r.logger.Printf("[API] Delete requested for stack: %s", name) + r.logger.Printf("[INFO] [api] Delete requested for stack: %s", name) var body struct { RemoveHDDData bool `json:"remove_hdd_data"` @@ -685,7 +685,7 @@ func (r *Router) deleteStack(w http.ResponseWriter, req *http.Request, name stri resp, err := r.stackMgr.DeleteStack(name, body.RemoveHDDData) if err != nil { - r.logger.Printf("[API] Delete failed for %s: %v", name, err) + r.logger.Printf("[ERROR] [api] Delete failed for %s: %v", name, err) status := http.StatusInternalServerError if strings.Contains(err.Error(), "protected") { status = http.StatusForbidden @@ -704,13 +704,13 @@ func (r *Router) deleteStack(w http.ResponseWriter, req *http.Request, name stri } func (r *Router) triggerSync(w http.ResponseWriter, _ *http.Request) { - r.logger.Println("[API] Manual catalog sync requested") + r.logger.Println("[INFO] [api] Manual catalog sync requested") result := r.syncer.TriggerSync() if !result.OK { writeJSON(w, http.StatusTooManyRequests, apiResponse{OK: false, Error: result.Message}) return } - r.logger.Printf("[API] Catalog sync completed: %s", result.Message) + r.logger.Printf("[INFO] [api] Catalog sync completed: %s", result.Message) writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: result.Message, Data: result}) } @@ -783,7 +783,7 @@ func (r *Router) triggerBackup(w http.ResponseWriter, _ *http.Request) { return } - r.logger.Println("[API] Manual backup triggered") + r.logger.Println("[INFO] [api] Manual backup triggered") go r.backupMgr.RunFullBackup(context.Background()) writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Mentés elindítva"}) @@ -979,12 +979,12 @@ func (r *Router) saveCrossBackupConfig(w http.ResponseWriter, req *http.Request, } if err := r.sett.SetCrossDriveConfig(name, cfg); err != nil { - r.logger.Printf("[API] Failed to save cross-drive config for %s: %v", name, err) + r.logger.Printf("[ERROR] [api] Failed to save cross-drive config for %s: %v", name, err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()}) return } - r.logger.Printf("[API] Cross-drive backup config saved for %s: dest=%s schedule=%s", + r.logger.Printf("[INFO] [api] Cross-drive backup config saved for %s: dest=%s schedule=%s", name, body.DestinationPath, body.Schedule) writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Cross-drive backup configuration saved"}) } @@ -999,10 +999,10 @@ func (r *Router) triggerCrossBackup(w http.ResponseWriter, req *http.Request, na return } - r.logger.Printf("[API] Cross-drive backup triggered for: %s", name) + r.logger.Printf("[INFO] [api] Cross-drive backup triggered for: %s", name) go func() { if err := r.crossDriveRunner.RunAppBackup(context.Background(), name); err != nil { - r.logger.Printf("[API] Cross-drive backup failed for %s: %v", name, err) + r.logger.Printf("[ERROR] [api] Cross-drive backup failed for %s: %v", name, err) } if r.OnCrossDriveComplete != nil { r.OnCrossDriveComplete() @@ -1037,14 +1037,14 @@ func (r *Router) triggerAllCrossBackups(w http.ResponseWriter, _ *http.Request) writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: "cross-drive runner not available"}) return } - r.logger.Println("[API] All cross-drive backups triggered") + r.logger.Println("[INFO] [api] All cross-drive backups triggered") go func() { ctx := context.Background() if err := r.crossDriveRunner.RunAllScheduled(ctx, "daily"); err != nil { - r.logger.Printf("[API] Cross-drive run-all error: %v", err) + r.logger.Printf("[ERROR] [api] Cross-drive run-all error: %v", err) } if err := r.crossDriveRunner.RunAllScheduled(ctx, "weekly"); err != nil { - r.logger.Printf("[API] Cross-drive run-all weekly error: %v", err) + r.logger.Printf("[ERROR] [api] Cross-drive run-all weekly error: %v", err) } if r.OnCrossDriveComplete != nil { r.OnCrossDriveComplete() @@ -1150,7 +1150,7 @@ func (r *Router) selfupdateTrigger(w http.ResponseWriter, _ *http.Request) { writeJSON(w, http.StatusConflict, apiResponse{OK: false, Error: err.Error()}) return } - r.logger.Println("[API] Manual self-update triggered") + r.logger.Println("[INFO] [api] Manual self-update triggered") writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Frissítés elindítva"}) } @@ -1171,7 +1171,7 @@ func (r *Router) configApply(w http.ResponseWriter, req *http.Request) { // Validate it's parseable YAML by attempting to load it if _, err := config.LoadFromBytes(body); err != nil { - r.logger.Printf("[API] Config apply rejected: invalid YAML: %v", err) + r.logger.Printf("[WARN] [api] Config apply rejected: invalid YAML: %v", err) writeJSON(w, http.StatusBadRequest, apiResponse{OK: false, Error: fmt.Sprintf("invalid config YAML: %v", err)}) return } @@ -1180,7 +1180,7 @@ func (r *Router) configApply(w http.ResponseWriter, req *http.Request) { // (os.Rename fails on Docker bind mounts with "device or resource busy") tmpPath := r.configPath + ".tmp" if err := os.WriteFile(tmpPath, body, 0644); err != nil { - r.logger.Printf("[ERROR] Config apply: failed to write temp file: %v", err) + r.logger.Printf("[ERROR] [api] Config apply: failed to write temp file: %v", err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: "failed to write config"}) return } @@ -1189,14 +1189,14 @@ func (r *Router) configApply(w http.ResponseWriter, req *http.Request) { os.Remove(tmpPath) // Rename failed (likely Docker bind mount) — write directly if err := os.WriteFile(r.configPath, body, 0644); err != nil { - r.logger.Printf("[ERROR] Config apply: failed to write config: %v", err) + r.logger.Printf("[ERROR] [api] Config apply: failed to write config: %v", err) writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: "failed to apply config"}) return } - r.logger.Printf("[API] Config apply: rename failed, wrote directly (bind mount)") + r.logger.Printf("[INFO] [api] Config apply: rename failed, wrote directly (bind mount)") } - r.logger.Printf("[API] Config applied from Hub (%d bytes), restart needed to take effect", len(body)) + r.logger.Printf("[INFO] [api] Config applied from Hub (%d bytes), restart needed to take effect", len(body)) writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Config applied. Restart controller to apply changes."}) // Push updated infra backup so Hub has fresh config data immediately @@ -1231,10 +1231,10 @@ func (r *Router) triggerAssetSync(w http.ResponseWriter, req *http.Request) { writeJSON(w, http.StatusOK, apiResponse{OK: false, Error: "asset sync not configured"}) return } - r.logger.Println("[API] Manual asset sync requested") + r.logger.Println("[INFO] [api] Manual asset sync requested") go func() { if err := r.assetsSyncer.Sync(context.Background()); err != nil { - r.logger.Printf("[WARN] Manual asset sync failed: %v", err) + r.logger.Printf("[WARN] [api] Manual asset sync failed: %v", err) } }() writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Asset sync started"}) @@ -1252,7 +1252,7 @@ func writeJSON(w http.ResponseWriter, status int, v interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if err := json.NewEncoder(w).Encode(v); err != nil { - log.Printf("[ERROR] Failed to write JSON response: %v", err) + log.Printf("[ERROR] [api] Failed to write JSON response: %v", err) } } diff --git a/controller/internal/assets/syncer.go b/controller/internal/assets/syncer.go index 03f92ed..fc9ae34 100644 --- a/controller/internal/assets/syncer.go +++ b/controller/internal/assets/syncer.go @@ -90,7 +90,7 @@ func (s *Syncer) Sync(ctx context.Context) error { s.mu.Unlock() }() - s.logger.Println("[INFO] Asset sync starting...") + s.logger.Println("[INFO] [assets] Asset sync starting...") syncStart := time.Now() if err := os.MkdirAll(s.assetsDir, 0755); err != nil { @@ -140,7 +140,7 @@ func (s *Syncer) Sync(ctx context.Context) error { s.dbg("file %s: downloading (%s, %d bytes)", entry.Filename, reason, entry.Size) dlStart := time.Now() if err := s.downloadFile(ctx, entry.Filename); err != nil { - s.logger.Printf("[WARN] Failed to download asset %s: %v", entry.Filename, err) + s.logger.Printf("[WARN] [assets] Failed to download asset %s: %v", entry.Filename, err) continue } s.dbg("file %s: downloaded in %s", entry.Filename, time.Since(dlStart).Round(time.Millisecond)) @@ -154,7 +154,7 @@ func (s *Syncer) Sync(ctx context.Context) error { path := filepath.Join(s.assetsDir, name) s.dbg("removing stale file %s", name) if err := os.Remove(path); err != nil { - s.logger.Printf("[WARN] Failed to remove stale asset %s: %v", name, err) + s.logger.Printf("[WARN] [assets] Failed to remove stale asset %s: %v", name, err) } else { removed++ } @@ -174,7 +174,7 @@ func (s *Syncer) Sync(ctx context.Context) error { } s.mu.Unlock() - s.logger.Printf("[INFO] Asset sync complete: %d downloaded, %d unchanged, %d removed (%d total files)", + s.logger.Printf("[INFO] [assets] Asset sync complete: %d downloaded, %d unchanged, %d removed (%d total files)", downloaded, skipped, removed, len(manifest.Files)) s.dbg("sync completed in %s", time.Since(syncStart).Round(time.Millisecond)) diff --git a/controller/internal/backup/appdata.go b/controller/internal/backup/appdata.go index e7f07b9..ee527ec 100644 --- a/controller/internal/backup/appdata.go +++ b/controller/internal/backup/appdata.go @@ -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 } diff --git a/controller/internal/backup/backup.go b/controller/internal/backup/backup.go index c226b11..df9cc84 100644 --- a/controller/internal/backup/backup.go +++ b/controller/internal/backup/backup.go @@ -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. diff --git a/controller/internal/backup/crossdrive.go b/controller/internal/backup/crossdrive.go index 34b5715..e2aabd9 100644 --- a/controller/internal/backup/crossdrive.go +++ b/controller/internal/backup/crossdrive.go @@ -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 { diff --git a/controller/internal/backup/dbdump.go b/controller/internal/backup/dbdump.go index 7bfa0ae..1252036 100644 --- a/controller/internal/backup/dbdump.go +++ b/controller/internal/backup/dbdump.go @@ -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()) } } } diff --git a/controller/internal/backup/restic.go b/controller/internal/backup/restic.go index b71d75f..d1d9d3b 100644 --- a/controller/internal/backup/restic.go +++ b/controller/internal/backup/restic.go @@ -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 } diff --git a/controller/internal/cloudflare/client.go b/controller/internal/cloudflare/client.go index 5d36e8a..958c394 100644 --- a/controller/internal/cloudflare/client.go +++ b/controller/internal/cloudflare/client.go @@ -64,10 +64,10 @@ func (c *Client) do(ctx context.Context, method, path string, body interface{}) } bodyReader = bytes.NewReader(data) if c.debug { - c.logger.Printf("[CF-DEBUG] %s %s body=%s", method, path, string(data)) + c.logger.Printf("[DEBUG] [cloudflare] %s %s body=%s", method, path, string(data)) } } else if c.debug { - c.logger.Printf("[CF-DEBUG] %s %s", method, path) + c.logger.Printf("[DEBUG] [cloudflare] %s %s", method, path) } url := apiBase + path @@ -91,7 +91,7 @@ func (c *Client) do(ctx context.Context, method, path string, body interface{}) } if c.debug { - c.logger.Printf("[CF-DEBUG] Response %d: %s", resp.StatusCode, string(respBody)) + c.logger.Printf("[DEBUG] [cloudflare] Response %d: %s", resp.StatusCode, string(respBody)) } var apiResp apiResponse diff --git a/controller/internal/cloudflare/waf.go b/controller/internal/cloudflare/waf.go index 392c1dd..242b26e 100644 --- a/controller/internal/cloudflare/waf.go +++ b/controller/internal/cloudflare/waf.go @@ -73,20 +73,20 @@ func (c *Client) GetCustomRulesetID(ctx context.Context, zoneID string) (string, } if c.debug { - c.logger.Printf("[CF-DEBUG] GetCustomRulesetID: found %d rulesets for zone %s", len(rulesets), zoneID) + c.logger.Printf("[DEBUG] [cloudflare] GetCustomRulesetID: found %d rulesets for zone %s", len(rulesets), zoneID) } for _, rs := range rulesets { if rs.Phase == wafPhase { if c.debug { - c.logger.Printf("[CF-DEBUG] GetCustomRulesetID: matched ruleset %s (phase=%s)", rs.ID, wafPhase) + c.logger.Printf("[DEBUG] [cloudflare] GetCustomRulesetID: matched ruleset %s (phase=%s)", rs.ID, wafPhase) } return rs.ID, nil } } if c.debug { - c.logger.Printf("[CF-DEBUG] GetCustomRulesetID: no ruleset with phase %s found", wafPhase) + c.logger.Printf("[DEBUG] [cloudflare] GetCustomRulesetID: no ruleset with phase %s found", wafPhase) } return "", nil @@ -112,7 +112,7 @@ func (c *Client) CreateCustomRuleset(ctx context.Context, zoneID string) (string return "", fmt.Errorf("decode created ruleset: %w", err) } - c.logger.Printf("[CF] Created custom ruleset %s for zone %s", rs.ID, zoneID) + c.logger.Printf("[INFO] [cloudflare] Created custom ruleset %s for zone %s", rs.ID, zoneID) return rs.ID, nil } @@ -133,7 +133,7 @@ func (c *Client) GetRules(ctx context.Context, zoneID, rulesetID string) ([]rule } if c.debug { - c.logger.Printf("[CF-DEBUG] GetRules: %d total rules in ruleset %s", len(rs.Rules), rulesetID) + c.logger.Printf("[DEBUG] [cloudflare] GetRules: %d total rules in ruleset %s", len(rs.Rules), rulesetID) } return rs.Rules, nil @@ -159,7 +159,7 @@ func (c *Client) GetFelhomRules(ctx context.Context, zoneID, rulesetID string) ( } if c.debug { - c.logger.Printf("[CF-DEBUG] GetFelhomRules: %d felhom rules out of %d total", len(result), len(rules)) + c.logger.Printf("[DEBUG] [cloudflare] GetFelhomRules: %d felhom rules out of %d total", len(result), len(rules)) } return result, nil @@ -185,13 +185,13 @@ func (c *Client) CreateRule(ctx context.Context, zoneID, rulesetID string, r rul for _, created := range rs.Rules { if created.Description == r.Description { - c.logger.Printf("[CF] Created rule %q → %s", r.Description, created.ID) + c.logger.Printf("[INFO] [cloudflare] Created rule %q → %s", r.Description, created.ID) if c.debug { expr := r.Expression if len(expr) > 120 { expr = expr[:120] + "..." } - c.logger.Printf("[CF-DEBUG] CreateRule: expression: %s", expr) + c.logger.Printf("[DEBUG] [cloudflare] CreateRule: expression: %s", expr) } return created.ID, nil } @@ -209,13 +209,13 @@ func (c *Client) UpdateRule(ctx context.Context, zoneID, rulesetID, ruleID strin c.logger.Printf("[ERROR] [cloudflare] Failed to update WAF rule %s: %v", ruleID, err) return fmt.Errorf("update rule %s: %w", ruleID, err) } - c.logger.Printf("[CF] Updated rule %q (%s)", r.Description, ruleID) + c.logger.Printf("[INFO] [cloudflare] Updated rule %q (%s)", r.Description, ruleID) if c.debug { expr := r.Expression if len(expr) > 120 { expr = expr[:120] + "..." } - c.logger.Printf("[CF-DEBUG] UpdateRule: expression: %s", expr) + c.logger.Printf("[DEBUG] [cloudflare] UpdateRule: expression: %s", expr) } return nil } @@ -229,7 +229,7 @@ func (c *Client) DeleteRule(ctx context.Context, zoneID, rulesetID, ruleID strin c.logger.Printf("[ERROR] [cloudflare] Failed to delete WAF rule %s: %v", ruleID, err) return fmt.Errorf("delete rule %s: %w", ruleID, err) } - c.logger.Printf("[CF] Deleted rule %s", ruleID) + c.logger.Printf("[INFO] [cloudflare] Deleted rule %s", ruleID) return nil } diff --git a/controller/internal/cloudflare/zone.go b/controller/internal/cloudflare/zone.go index 0bb9c84..b9aa1f7 100644 --- a/controller/internal/cloudflare/zone.go +++ b/controller/internal/cloudflare/zone.go @@ -17,12 +17,12 @@ type zone struct { // It tries the exact domain first, then strips subdomains progressively. func (c *Client) GetZoneID(ctx context.Context, domain string) (string, error) { if c.debug { - c.logger.Printf("[CF-DEBUG] GetZoneID: looking up zone for domain %q", domain) + c.logger.Printf("[DEBUG] [cloudflare] GetZoneID: looking up zone for domain %q", domain) } // Try exact domain first (e.g., "demo-felhom.eu") if c.debug { - c.logger.Printf("[CF-DEBUG] GetZoneID: trying exact domain %q", domain) + c.logger.Printf("[DEBUG] [cloudflare] GetZoneID: trying exact domain %q", domain) } id, err := c.lookupZone(ctx, domain) if err != nil { @@ -40,7 +40,7 @@ func (c *Client) GetZoneID(ctx context.Context, domain string) (string, error) { break } if c.debug { - c.logger.Printf("[CF-DEBUG] GetZoneID: trying parent domain %q", parent) + c.logger.Printf("[DEBUG] [cloudflare] GetZoneID: trying parent domain %q", parent) } id, err = c.lookupZone(ctx, parent) if err != nil { @@ -73,6 +73,6 @@ func (c *Client) lookupZone(ctx context.Context, name string) (string, error) { return "", nil } - c.logger.Printf("[CF] Resolved zone %q → %s", name, zones[0].ID) + c.logger.Printf("[INFO] [cloudflare] Resolved zone %q → %s", name, zones[0].ID) return zones[0].ID, nil } diff --git a/controller/internal/integrations/manager.go b/controller/internal/integrations/manager.go index 20d58fe..a492434 100644 --- a/controller/internal/integrations/manager.go +++ b/controller/internal/integrations/manager.go @@ -142,11 +142,11 @@ func (m *Manager) Toggle(ctx context.Context, provider, target string, enable bo state.Enabled = true state.Status = "active" state.EnabledAt = time.Now().UTC().Format(time.RFC3339) - m.logger.Printf("[INFO] Integration %s enabled", key) + m.logger.Printf("[INFO] [integrations] Integration %s enabled", key) } else { start := time.Now() if err := handler.Revoke(ac); err != nil { - m.logger.Printf("[WARN] Integration revoke failed for %s: %v", key, err) + m.logger.Printf("[WARN] [integrations] Integration revoke failed for %s: %v", key, err) state.LastError = err.Error() } if m.isDebug() { @@ -154,7 +154,7 @@ func (m *Manager) Toggle(ctx context.Context, provider, target string, enable bo } state.Enabled = false state.Status = "disabled" - m.logger.Printf("[INFO] Integration %s disabled", key) + m.logger.Printf("[INFO] [integrations] Integration %s disabled", key) } if err := m.sett.SetIntegrationState(key, state); err != nil { @@ -268,7 +268,7 @@ func (m *Manager) ReapplyConfigForTarget(targetName string) { ac, err := m.buildApplyContext(provider, target) if err != nil { - m.logger.Printf("[WARN] Cannot build context for integration %s reapply: %v", key, err) + m.logger.Printf("[WARN] [integrations] Cannot build context for integration %s reapply: %v", key, err) continue } @@ -276,7 +276,7 @@ func (m *Manager) ReapplyConfigForTarget(targetName string) { ac.RestartStack = func(string) error { return nil } if err := handler.Apply(ac); err != nil { - m.logger.Printf("[WARN] Integration config reapply failed for %s: %v", key, err) + m.logger.Printf("[WARN] [integrations] Integration config reapply failed for %s: %v", key, err) if m.isDebug() { m.logger.Printf("[DEBUG] [integrations] ReapplyConfigForTarget: %s failed: %v", key, err) } @@ -292,7 +292,7 @@ func (m *Manager) ReapplyConfigForTarget(targetName string) { state.Status = "active" state.LastError = "" _ = m.sett.SetIntegrationState(key, state) - m.logger.Printf("[INFO] Integration %s config reapplied for %s", key, targetName) + m.logger.Printf("[INFO] [integrations] Integration %s config reapplied for %s", key, targetName) } } diff --git a/controller/internal/integrations/onlyoffice_filebrowser.go b/controller/internal/integrations/onlyoffice_filebrowser.go index 3e52dc2..5567297 100644 --- a/controller/internal/integrations/onlyoffice_filebrowser.go +++ b/controller/internal/integrations/onlyoffice_filebrowser.go @@ -29,8 +29,6 @@ func (h *OnlyOfficeFileBrowserHandler) Apply(ac *ApplyContext) error { configPath := filepath.Join(ac.StacksDir, "filebrowser", "config.yaml") officeURL := fmt.Sprintf("https://%s.%s", subdomain, ac.Domain) - ac.Logger.Printf("[DEBUG] [integrations] OnlyOfficeFileBrowser.Apply: jwtSecretPresent=%v subdomain=%s configPath=%s officeURL=%s", jwtSecret != "", subdomain, configPath, officeURL) - configData, err := os.ReadFile(configPath) if err != nil { ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-FileBrowser apply: %v", err) @@ -59,7 +57,7 @@ func (h *OnlyOfficeFileBrowserHandler) Apply(ac *ApplyContext) error { return fmt.Errorf("config átnevezési hiba: %w", err) } - ac.Logger.Printf("[INFO] FileBrowser config updated with OnlyOffice integration") + ac.Logger.Printf("[INFO] [integrations] FileBrowser config updated with OnlyOffice integration") return ac.RestartStack("filebrowser") } @@ -94,7 +92,7 @@ func (h *OnlyOfficeFileBrowserHandler) Revoke(ac *ApplyContext) error { return fmt.Errorf("config átnevezési hiba: %w", err) } - ac.Logger.Printf("[INFO] FileBrowser config cleaned — OnlyOffice integration removed") + ac.Logger.Printf("[INFO] [integrations] FileBrowser config cleaned — OnlyOffice integration removed") return ac.RestartStack("filebrowser") } diff --git a/controller/internal/integrations/onlyoffice_nextcloud.go b/controller/internal/integrations/onlyoffice_nextcloud.go index 5c90922..df65681 100644 --- a/controller/internal/integrations/onlyoffice_nextcloud.go +++ b/controller/internal/integrations/onlyoffice_nextcloud.go @@ -71,16 +71,16 @@ func (h *OnlyOfficeNextcloudHandler) Apply(ac *ApplyContext) error { cancel() if err != nil { if cmd.tolerate != "" && strings.Contains(string(out), cmd.tolerate) { - ac.Logger.Printf("[DEBUG] Nextcloud occ: tolerated — %s", strings.TrimSpace(string(out))) + ac.Logger.Printf("[DEBUG] [integrations] Nextcloud occ: tolerated — %s", strings.TrimSpace(string(out))) continue } ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-Nextcloud apply: occ %s failed: %v", cmd.args[len(cmd.args)-1], err) return fmt.Errorf("occ parancs sikertelen (%s): %v (kimenet: %s)", cmd.args[len(cmd.args)-1], err, strings.TrimSpace(string(out))) } - ac.Logger.Printf("[DEBUG] Nextcloud occ %s: ok", strings.Join(cmd.args[7:], " ")) + ac.Logger.Printf("[DEBUG] [integrations] Nextcloud occ %s: ok", strings.Join(cmd.args[7:], " ")) } - ac.Logger.Printf("[INFO] OnlyOffice integration applied to Nextcloud") + ac.Logger.Printf("[INFO] [integrations] OnlyOffice integration applied to Nextcloud") return nil } @@ -96,13 +96,13 @@ func (h *OnlyOfficeNextcloudHandler) Revoke(ac *ApplyContext) error { if strings.Contains(err.Error(), "No such container") || strings.Contains(outStr, "not enabled") || strings.Contains(outStr, "not installed") { - ac.Logger.Printf("[DEBUG] Nextcloud occ app:disable skipped — %s", strings.TrimSpace(outStr)) + ac.Logger.Printf("[DEBUG] [integrations] Nextcloud occ app:disable skipped — %s", strings.TrimSpace(outStr)) return nil } ac.Logger.Printf("[ERROR] [integrations] OnlyOffice-Nextcloud revoke: occ app:disable failed: %v", err) return fmt.Errorf("occ app:disable sikertelen: %v (kimenet: %s)", err, strings.TrimSpace(outStr)) } - ac.Logger.Printf("[INFO] OnlyOffice integration revoked from Nextcloud") + ac.Logger.Printf("[INFO] [integrations] OnlyOffice integration revoked from Nextcloud") return nil } diff --git a/controller/internal/metrics/collector.go b/controller/internal/metrics/collector.go index e19f4b9..c388327 100644 --- a/controller/internal/metrics/collector.go +++ b/controller/internal/metrics/collector.go @@ -69,12 +69,12 @@ func (c *MetricsCollector) loop(ctx context.Context) { func (c *MetricsCollector) sampleWith(ctx context.Context) { sys := c.sampleSystem() if err := c.store.InsertSystemMetrics(sys); err != nil { - c.logger.Printf("[WARN] Failed to store system metrics: %v", err) + c.logger.Printf("[WARN] [metrics] Failed to store system metrics: %v", err) } containers := c.sampleContainers(ctx) if err := c.store.InsertContainerMetrics(containers); err != nil { - c.logger.Printf("[WARN] Failed to store container metrics: %v", err) + c.logger.Printf("[WARN] [metrics] Failed to store container metrics: %v", err) } } @@ -104,7 +104,7 @@ func (c *MetricsCollector) sampleContainers(parentCtx context.Context) []Contain "--format", "{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}") out, err := cmd.Output() if err != nil { - c.logger.Printf("[WARN] docker stats failed: %v", err) + c.logger.Printf("[WARN] [metrics] docker stats failed: %v", err) return nil } diff --git a/controller/internal/metrics/logscanner.go b/controller/internal/metrics/logscanner.go index f09bc88..0201850 100644 --- a/controller/internal/metrics/logscanner.go +++ b/controller/internal/metrics/logscanner.go @@ -75,7 +75,7 @@ func ScanContainerLogs(containerNames []string, since time.Duration, logger *log elapsed := time.Since(start) dbg("log scan completed: %d containers in %s", len(containerNames), elapsed.Round(time.Millisecond)) if elapsed > 5*time.Minute && logger != nil { - logger.Printf("[WARN] Log scan took %s (>5min) for %d containers", elapsed.Round(time.Second), len(containerNames)) + logger.Printf("[WARN] [metrics] Log scan took %s (>5min) for %d containers", elapsed.Round(time.Second), len(containerNames)) } return results @@ -92,7 +92,7 @@ func scanOneContainer(name string, since time.Duration, logger *log.Logger) Cont output, err := cmd.CombinedOutput() if err != nil { if logger != nil { - logger.Printf("[DEBUG] logscanner: docker logs %s: %v", name, err) + logger.Printf("[DEBUG] [metrics] logscanner: docker logs %s: %v", name, err) } return summary } diff --git a/controller/internal/metrics/telemetry.go b/controller/internal/metrics/telemetry.go index 1117efe..6beb1fa 100644 --- a/controller/internal/metrics/telemetry.go +++ b/controller/internal/metrics/telemetry.go @@ -39,7 +39,7 @@ func (s *MetricsStore) GetContainerTelemetry(since time.Time) ([]ContainerTeleme var ct ContainerTelemetry if err := rows.Scan(&ct.ContainerName, &ct.MemoryAvgMB, &ct.MemoryPeakMB, &ct.CPUAvgPercent, &ct.SampleCount); err != nil { - log.Printf("[WARN] telemetry row scan failed: %v", err) + log.Printf("[WARN] [metrics] telemetry row scan failed: %v", err) continue } results = append(results, ct) diff --git a/controller/internal/monitor/healthcheck.go b/controller/internal/monitor/healthcheck.go index ec2d325..ea3e136 100644 --- a/controller/internal/monitor/healthcheck.go +++ b/controller/internal/monitor/healthcheck.go @@ -38,7 +38,7 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora sysInfo := system.GetInfo(hddPath, cpuCollector) if debug { - logger.Printf("[DEBUG] [HEALTH] Raw values: disk=%.1f%%, hdd=%.1f%% (configured=%v), mem=%.1f%% (%dMB/%dMB), cpu=%.1f%%, temp=%.1f°C (%s)", + logger.Printf("[DEBUG] [monitor] Raw values: disk=%.1f%%, hdd=%.1f%% (configured=%v), mem=%.1f%% (%dMB/%dMB), cpu=%.1f%%, temp=%.1f°C (%s)", sysInfo.DiskPercent, sysInfo.HDDPercent, sysInfo.HDDConfigured, sysInfo.MemPercent, sysInfo.UsedMemMB, sysInfo.TotalMemMB, sysInfo.CPUPercent, sysInfo.TemperatureCelsius, sysInfo.TemperatureSource) @@ -52,7 +52,7 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora logger.Printf("[WARN] [monitor] Disk (SSD) threshold breached: %.0f%% (limit: %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskCritPercent) } if debug { - logger.Printf("[DEBUG] [HEALTH] SSD disk: CRITICAL (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskCritPercent) + logger.Printf("[DEBUG] [monitor] SSD disk: CRITICAL (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskCritPercent) } } else if sysInfo.DiskPercent >= float64(cfg.Monitoring.Thresholds.DiskWarnPercent) { report.Warnings = append(report.Warnings, fmt.Sprintf("SSD disk usage high: %.0f%%", sysInfo.DiskPercent)) @@ -60,12 +60,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora logger.Printf("[WARN] [monitor] Disk (SSD) threshold breached: %.0f%% (limit: %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskWarnPercent) } if debug { - logger.Printf("[DEBUG] [HEALTH] SSD disk: WARN (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskWarnPercent) + logger.Printf("[DEBUG] [monitor] SSD disk: WARN (%.0f%% >= %d%%)", sysInfo.DiskPercent, cfg.Monitoring.Thresholds.DiskWarnPercent) } } else { report.Info = append(report.Info, fmt.Sprintf("SSD: %.0f%% used", sysInfo.DiskPercent)) if debug { - logger.Printf("[DEBUG] [HEALTH] SSD disk: OK (%.0f%%)", sysInfo.DiskPercent) + logger.Printf("[DEBUG] [monitor] SSD disk: OK (%.0f%%)", sysInfo.DiskPercent) } } } @@ -93,12 +93,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora logger.Printf("[WARN] [monitor] Memory threshold breached: %.0f%% (limit: %d%%)", sysInfo.MemPercent, cfg.Monitoring.Thresholds.MemoryWarnPercent) } if debug { - logger.Printf("[DEBUG] [HEALTH] Memory: WARN (%.0f%% >= %d%%)", sysInfo.MemPercent, cfg.Monitoring.Thresholds.MemoryWarnPercent) + logger.Printf("[DEBUG] [monitor] Memory: WARN (%.0f%% >= %d%%)", sysInfo.MemPercent, cfg.Monitoring.Thresholds.MemoryWarnPercent) } } else { report.Info = append(report.Info, fmt.Sprintf("Memory: %.0f%% used", sysInfo.MemPercent)) if debug { - logger.Printf("[DEBUG] [HEALTH] Memory: OK (%.0f%%)", sysInfo.MemPercent) + logger.Printf("[DEBUG] [monitor] Memory: OK (%.0f%%)", sysInfo.MemPercent) } } } @@ -111,12 +111,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora logger.Printf("[WARN] [monitor] CPU threshold breached: %.0f%% (limit: %d%%)", sysInfo.CPUPercent, cfg.Monitoring.Thresholds.CPUWarnPercent) } if debug { - logger.Printf("[DEBUG] [HEALTH] CPU: WARN (%.0f%% >= %d%%)", sysInfo.CPUPercent, cfg.Monitoring.Thresholds.CPUWarnPercent) + logger.Printf("[DEBUG] [monitor] CPU: WARN (%.0f%% >= %d%%)", sysInfo.CPUPercent, cfg.Monitoring.Thresholds.CPUWarnPercent) } } else { report.Info = append(report.Info, fmt.Sprintf("CPU: %.0f%%", sysInfo.CPUPercent)) if debug { - logger.Printf("[DEBUG] [HEALTH] CPU: OK (%.0f%%)", sysInfo.CPUPercent) + logger.Printf("[DEBUG] [monitor] CPU: OK (%.0f%%)", sysInfo.CPUPercent) } } } @@ -129,12 +129,12 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora logger.Printf("[WARN] [monitor] Temperature threshold breached: %.0f°C (limit: %d°C)", sysInfo.TemperatureCelsius, cfg.Monitoring.Thresholds.TemperatureWarnCelsius) } if debug { - logger.Printf("[DEBUG] [HEALTH] Temperature: WARN (%.0f°C >= %d°C)", sysInfo.TemperatureCelsius, cfg.Monitoring.Thresholds.TemperatureWarnCelsius) + logger.Printf("[DEBUG] [monitor] Temperature: WARN (%.0f°C >= %d°C)", sysInfo.TemperatureCelsius, cfg.Monitoring.Thresholds.TemperatureWarnCelsius) } } else { report.Info = append(report.Info, fmt.Sprintf("Temperature: %.0f°C", sysInfo.TemperatureCelsius)) if debug { - logger.Printf("[DEBUG] [HEALTH] Temperature: OK (%.0f°C)", sysInfo.TemperatureCelsius) + logger.Printf("[DEBUG] [monitor] Temperature: OK (%.0f°C)", sysInfo.TemperatureCelsius) } } } @@ -143,18 +143,18 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora if err := checkDocker(); err != nil { report.Issues = append(report.Issues, fmt.Sprintf("Docker: %v", err)) if debug { - logger.Printf("[DEBUG] [HEALTH] Docker daemon: FAIL (%v)", err) + logger.Printf("[DEBUG] [monitor] Docker daemon: FAIL (%v)", err) } } else { report.Info = append(report.Info, "Docker: reachable") if debug { - logger.Printf("[DEBUG] [HEALTH] Docker daemon: OK") + logger.Printf("[DEBUG] [monitor] Docker daemon: OK") } } // 6. Protected containers if debug { - logger.Printf("[DEBUG] [HEALTH] Checking %d protected containers: %v", len(cfg.Stacks.Protected), cfg.Stacks.Protected) + logger.Printf("[DEBUG] [monitor] Checking %d protected containers: %v", len(cfg.Stacks.Protected), cfg.Stacks.Protected) } missingProtected := checkProtectedContainers(cfg.Stacks.Protected) for _, name := range missingProtected { @@ -162,9 +162,9 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora } if debug { if len(missingProtected) > 0 { - logger.Printf("[DEBUG] [HEALTH] Protected containers missing: %v", missingProtected) + logger.Printf("[DEBUG] [monitor] Protected containers missing: %v", missingProtected) } else { - logger.Printf("[DEBUG] [HEALTH] All protected containers running") + logger.Printf("[DEBUG] [monitor] All protected containers running") } } @@ -185,7 +185,7 @@ func RunHealthCheck(cfg *config.Config, cpuCollector *system.CPUCollector, stora } if debug { - logger.Printf("[DEBUG] [HEALTH] Final status: %s (issues=%d, warnings=%d, info=%d)", + logger.Printf("[DEBUG] [monitor] Final status: %s (issues=%d, warnings=%d, info=%d)", report.Status, len(report.Issues), len(report.Warnings), len(report.Info)) } diff --git a/controller/internal/monitor/pinger.go b/controller/internal/monitor/pinger.go index d43a490..ed23853 100644 --- a/controller/internal/monitor/pinger.go +++ b/controller/internal/monitor/pinger.go @@ -115,6 +115,6 @@ func (p *Pinger) send(uuid, suffix, body string) error { lastErr = fmt.Errorf("HTTP %d", resp.StatusCode) } - p.logger.Printf("[WARN] Health ping failed after 3 attempts (%s): %v", uuid, lastErr) + p.logger.Printf("[WARN] [monitor] Health ping failed after 3 attempts (%s): %v", uuid, lastErr) return nil // Never let ping failures affect the caller } diff --git a/controller/internal/monitor/watchdog.go b/controller/internal/monitor/watchdog.go index b6a10fe..c862480 100644 --- a/controller/internal/monitor/watchdog.go +++ b/controller/internal/monitor/watchdog.go @@ -200,7 +200,7 @@ func (w *StorageWatchdog) handleConnectedProbe(sp settings.StoragePath, state *p if result.Status == system.ProbeConnected { if state.consecutiveFailures > 0 { - w.logger.Printf("[DEBUG] [STORAGE] Probe recovered for %s after %d failures", sp.Path, state.consecutiveFailures) + w.logger.Printf("[DEBUG] [storage] Probe recovered for %s after %d failures", sp.Path, state.consecutiveFailures) } state.consecutiveFailures = 0 state.lastStatus = "connected" @@ -209,7 +209,7 @@ func (w *StorageWatchdog) handleConnectedProbe(sp settings.StoragePath, state *p // Every 60 probes (~5 minutes at 5s interval): emit summary if state.probeCount >= 60 { avgLatency := state.totalLatency / time.Duration(state.probeCount) - w.logger.Printf("[DEBUG] [STORAGE] Storage watchdog: %s — %d/%d probes OK (last 5m, avg %dms)", + w.logger.Printf("[DEBUG] [storage] Storage watchdog: %s — %d/%d probes OK (last 5m, avg %dms)", sp.Path, state.probeOKCount, state.probeCount, avgLatency.Milliseconds()) state.probeCount = 0 state.probeOKCount = 0 @@ -224,11 +224,11 @@ func (w *StorageWatchdog) handleConnectedProbe(sp settings.StoragePath, state *p // Debug: log immediately on unexpected failure (was connected, now failing) if w.isDebug() && state.lastStatus == "connected" { - w.logger.Printf("[DEBUG] [STORAGE] Storage probe failed for %s (%d/%d before disconnect): %v", + w.logger.Printf("[DEBUG] [storage] Storage probe failed for %s (%d/%d before disconnect): %v", sp.Path, state.consecutiveFailures, probeThreshold, result.Err) } - w.logger.Printf("[WARN] [STORAGE] Probe failed for %s (%d/%d): %v", + w.logger.Printf("[WARN] [storage] Probe failed for %s (%d/%d): %v", sp.Path, state.consecutiveFailures, probeThreshold, result.Err) if state.consecutiveFailures >= probeThreshold { @@ -249,14 +249,14 @@ func (w *StorageWatchdog) handleDisconnect(sp settings.StoragePath, state *pathP if label == "" { label = sp.Path } - w.logger.Printf("[ERROR] [STORAGE] Drive disconnected: %s (%s)", sp.Path, label) + w.logger.Printf("[ERROR] [storage] Drive disconnected: %s (%s)", sp.Path, label) // 1. Find and stop affected stacks stoppedStacks := w.stopAffectedStacks(sp.Path) // 2. Mark disconnected in settings (persists to settings.json) if err := w.settings.SetDisconnected(sp.Path, true, stoppedStacks); err != nil { - w.logger.Printf("[ERROR] [STORAGE] Failed to mark disconnected: %v", err) + w.logger.Printf("[ERROR] [storage] Failed to mark disconnected: %v", err) } // 3. Lazy unmount stale mount (if probe timed out — mount is likely hanging) @@ -302,7 +302,7 @@ func (w *StorageWatchdog) handleReconnectCheck(ctx context.Context, sp settings. } if w.isDebug() { - w.logger.Printf("[DEBUG] [STORAGE] Reconnect check for %s: UUID=%s, mountPath=%s, isAttachWizard=%v", + w.logger.Printf("[DEBUG] [storage] Reconnect check for %s: UUID=%s, mountPath=%s, isAttachWizard=%v", sp.Path, uuid, mountPath, isAttachWizard) } @@ -316,35 +316,35 @@ func (w *StorageWatchdog) handleReconnectCheck(ctx context.Context, sp settings. if label == "" { label = sp.Path } - w.logger.Printf("[INFO] [STORAGE] Drive reconnected (UUID found), attempting remount: %s (%s)", sp.Path, label) + w.logger.Printf("[INFO] [storage] Drive reconnected (UUID found), attempting remount: %s (%s)", sp.Path, label) if w.isDebug() { - w.logger.Printf("[DEBUG] [STORAGE] UUID %s found at %s, mounting %s (raw=%s, attachWizard=%v)", + w.logger.Printf("[DEBUG] [storage] UUID %s found at %s, mounting %s (raw=%s, attachWizard=%v)", uuid, uuidPath, sp.Path, rawPath, isAttachWizard) } // Attempt remount if err := w.remount(sp.Path, rawPath, isAttachWizard); err != nil { - w.logger.Printf("[ERROR] [STORAGE] Remount failed for %s: %v", sp.Path, err) + w.logger.Printf("[ERROR] [storage] Remount failed for %s: %v", sp.Path, err) return // Try again next cycle } // Verify with a probe verifyResult := system.ProbeStoragePath(sp.Path) if verifyResult.Status != system.ProbeConnected { - w.logger.Printf("[ERROR] [STORAGE] Post-remount probe failed for %s: %v", sp.Path, verifyResult.Err) + w.logger.Printf("[ERROR] [storage] Post-remount probe failed for %s: %v", sp.Path, verifyResult.Err) if w.isDebug() { - w.logger.Printf("[DEBUG] [STORAGE] Post-mount verification failed for %s: status=%v, err=%v", + w.logger.Printf("[DEBUG] [storage] Post-mount verification failed for %s: status=%v, err=%v", sp.Path, verifyResult.Status, verifyResult.Err) } return } if w.isDebug() { - w.logger.Printf("[DEBUG] [STORAGE] Post-mount verification succeeded for %s", sp.Path) + w.logger.Printf("[DEBUG] [storage] Post-mount verification succeeded for %s", sp.Path) } - w.logger.Printf("[INFO] [STORAGE] Drive successfully remounted: %s (%s)", sp.Path, label) + w.logger.Printf("[INFO] [storage] Drive successfully remounted: %s (%s)", sp.Path, label) // Clean stale restic locks w.cleanResticLocks(ctx, sp.Path) @@ -354,7 +354,7 @@ func (w *StorageWatchdog) handleReconnectCheck(ctx context.Context, sp settings. // Clear disconnected but preserve StoppedStacks for the restart UI if err := w.settings.SetDisconnected(sp.Path, false, filteredStacks); err != nil { - w.logger.Printf("[ERROR] [STORAGE] Failed to clear disconnected: %v", err) + w.logger.Printf("[ERROR] [storage] Failed to clear disconnected: %v", err) } // Update in-memory state @@ -400,20 +400,20 @@ func (w *StorageWatchdog) stopAffectedStacks(drivePath string) []string { // Don't stop protected stacks if w.cfg.IsProtectedStack(stack.Name) { - w.logger.Printf("[WARN] [STORAGE] Skipping protected stack: %s", stack.Name) + w.logger.Printf("[WARN] [storage] Skipping protected stack: %s", stack.Name) continue } - w.logger.Printf("[INFO] [STORAGE] Stopping stack %s (drive disconnected: %s)", stack.Name, drivePath) + w.logger.Printf("[INFO] [storage] Stopping stack %s (drive disconnected: %s)", stack.Name, drivePath) if err := w.stackProvider.StopStack(stack.Name); err != nil { - w.logger.Printf("[ERROR] [STORAGE] Failed to stop stack %s: %v", stack.Name, err) + w.logger.Printf("[ERROR] [storage] Failed to stop stack %s: %v", stack.Name, err) continue // Don't add to stopped list if stop failed } stopped = append(stopped, stack.Name) } if len(stopped) > 0 { - w.logger.Printf("[INFO] [STORAGE] Stopped %d stack(s) due to drive disconnect: %v", len(stopped), stopped) + w.logger.Printf("[INFO] [storage] Stopped %d stack(s) due to drive disconnect: %v", len(stopped), stopped) } return stopped } @@ -426,18 +426,18 @@ func (w *StorageWatchdog) lazyUnmount(path string) { // Unmount the bind/main path cmd := exec.Command("umount", "-l", path) if out, err := cmd.CombinedOutput(); err != nil { - w.logger.Printf("[WARN] [STORAGE] umount -l %s: %v (%s)", path, err, strings.TrimSpace(string(out))) + w.logger.Printf("[WARN] [storage] umount -l %s: %v (%s)", path, err, strings.TrimSpace(string(out))) } else { - w.logger.Printf("[INFO] [STORAGE] Lazy unmounted: %s", path) + w.logger.Printf("[INFO] [storage] Lazy unmounted: %s", path) } // Then unmount the raw path if it's an attach-wizard drive if isAttachWizard && rawPath != "" { cmd = exec.Command("umount", "-l", rawPath) if out, err := cmd.CombinedOutput(); err != nil { - w.logger.Printf("[WARN] [STORAGE] umount -l %s: %v (%s)", rawPath, err, strings.TrimSpace(string(out))) + w.logger.Printf("[WARN] [storage] umount -l %s: %v (%s)", rawPath, err, strings.TrimSpace(string(out))) } else { - w.logger.Printf("[INFO] [STORAGE] Lazy unmounted raw: %s", rawPath) + w.logger.Printf("[INFO] [storage] Lazy unmounted raw: %s", rawPath) } } } @@ -456,19 +456,19 @@ func (w *StorageWatchdog) remount(path, rawPath string, isAttachWizard bool) err if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("mount raw %s: %v (%s)", rawPath, err, strings.TrimSpace(string(out))) } - w.logger.Printf("[INFO] [STORAGE] Mounted raw: %s", rawPath) + w.logger.Printf("[INFO] [storage] Mounted raw: %s", rawPath) cmd = exec.Command("mount", "-T", hostFstabPath, path) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("mount bind %s: %v (%s)", path, err, strings.TrimSpace(string(out))) } - w.logger.Printf("[INFO] [STORAGE] Mounted bind: %s", path) + w.logger.Printf("[INFO] [storage] Mounted bind: %s", path) } else { cmd := exec.Command("mount", "-T", hostFstabPath, path) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("mount %s: %v (%s)", path, err, strings.TrimSpace(string(out))) } - w.logger.Printf("[INFO] [STORAGE] Mounted: %s", path) + w.logger.Printf("[INFO] [storage] Mounted: %s", path) } return nil } @@ -482,11 +482,11 @@ func (w *StorageWatchdog) cleanResticLocks(ctx context.Context, drivePath string return // No locks dir or no lock files } - w.logger.Printf("[INFO] [STORAGE] Found %d restic lock file(s) in %s, running unlock", len(entries), repoPath) + w.logger.Printf("[INFO] [storage] Found %d restic lock file(s) in %s, running unlock", len(entries), repoPath) if w.unlockRepo != nil { if err := w.unlockRepo(ctx, repoPath); err != nil { - w.logger.Printf("[WARN] [STORAGE] Restic unlock failed for %s: %v", repoPath, err) + w.logger.Printf("[WARN] [storage] Restic unlock failed for %s: %v", repoPath, err) } } } @@ -529,7 +529,7 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop if label == "" { label = sp.Path } - w.logger.Printf("[INFO] [STORAGE] Safe disconnect requested: %s (%s)", path, label) + w.logger.Printf("[INFO] [storage] Safe disconnect requested: %s (%s)", path, label) // 1. Stop affected stacks stoppedStacks = w.stopAffectedStacks(path) @@ -544,7 +544,7 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop cmd := exec.Command("umount", path) if out, umountErr := cmd.CombinedOutput(); umountErr != nil { // Try lazy unmount as fallback - w.logger.Printf("[WARN] [STORAGE] umount %s failed, trying lazy: %v", path, umountErr) + w.logger.Printf("[WARN] [storage] umount %s failed, trying lazy: %v", path, umountErr) cmd = exec.Command("umount", "-l", path) if out, umountErr = cmd.CombinedOutput(); umountErr != nil { return stoppedStacks, fmt.Errorf("umount %s failed: %v (%s)", path, umountErr, strings.TrimSpace(string(out))) @@ -557,14 +557,14 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop if out, umountErr := cmd.CombinedOutput(); umountErr != nil { cmd = exec.Command("umount", "-l", rawPath) if out, umountErr = cmd.CombinedOutput(); umountErr != nil { - w.logger.Printf("[WARN] [STORAGE] umount raw %s failed: %v (%s)", rawPath, umountErr, strings.TrimSpace(string(out))) + w.logger.Printf("[WARN] [storage] umount raw %s failed: %v (%s)", rawPath, umountErr, strings.TrimSpace(string(out))) } } } // 4. Mark disconnected if setErr := w.settings.SetDisconnected(path, true, stoppedStacks); setErr != nil { - w.logger.Printf("[ERROR] [STORAGE] Failed to mark disconnected: %v", setErr) + w.logger.Printf("[ERROR] [storage] Failed to mark disconnected: %v", setErr) } // 5. Update in-memory state @@ -587,7 +587,7 @@ func (w *StorageWatchdog) SafeDisconnect(ctx context.Context, path string) (stop go w.pushHubReport() } - w.logger.Printf("[INFO] [STORAGE] Safe disconnect completed: %s — drive can be removed", path) + w.logger.Printf("[INFO] [storage] Safe disconnect completed: %s — drive can be removed", path) return stoppedStacks, nil } @@ -639,7 +639,7 @@ func (w *StorageWatchdog) Reconnect(ctx context.Context, path string) (stoppedSt // Clear disconnected, preserve stopped stacks for restart UI if setErr := w.settings.SetDisconnected(path, false, filteredStacks); setErr != nil { - w.logger.Printf("[ERROR] [STORAGE] Failed to clear disconnected: %v", setErr) + w.logger.Printf("[ERROR] [storage] Failed to clear disconnected: %v", setErr) } // Update in-memory state @@ -661,7 +661,7 @@ func (w *StorageWatchdog) Reconnect(ctx context.Context, path string) (stoppedSt go w.pushHubReport() } - w.logger.Printf("[INFO] [STORAGE] Reconnect completed: %s", path) + w.logger.Printf("[INFO] [storage] Reconnect completed: %s", path) return filteredStacks, nil } @@ -678,9 +678,9 @@ func (w *StorageWatchdog) RestartStoppedApps(path string) (started, failed []str } for _, name := range stacks { - w.logger.Printf("[INFO] [STORAGE] Starting stack %s (drive reconnected: %s)", name, path) + w.logger.Printf("[INFO] [storage] Starting stack %s (drive reconnected: %s)", name, path) if err := w.stackProvider.StartStack(name); err != nil { - w.logger.Printf("[ERROR] [STORAGE] Failed to start stack %s: %v", name, err) + w.logger.Printf("[ERROR] [storage] Failed to start stack %s: %v", name, err) failed = append(failed, name) } else { started = append(started, name) @@ -689,7 +689,7 @@ func (w *StorageWatchdog) RestartStoppedApps(path string) (started, failed []str // Clear stopped stacks list if err := w.settings.ClearStoppedStacks(path); err != nil { - w.logger.Printf("[ERROR] [STORAGE] Failed to clear stopped stacks: %v", err) + w.logger.Printf("[ERROR] [storage] Failed to clear stopped stacks: %v", err) } return started, failed @@ -723,7 +723,7 @@ func (w *StorageWatchdog) SimulateDisconnect(ctx context.Context, path string) ( if label == "" { label = sp.Path } - w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Simulating disconnect: %s (%s)", path, label) + w.logger.Printf("[INFO] [storage] (simulation) Simulating disconnect: %s (%s)", path, label) // Mark as simulated so the watchdog skips probing this path w.simulatedMu.Lock() @@ -735,7 +735,7 @@ func (w *StorageWatchdog) SimulateDisconnect(ctx context.Context, path string) ( // Step 2: Mark disconnected in settings if err := w.settings.SetDisconnected(path, true, stoppedStacks); err != nil { - w.logger.Printf("[ERROR] [STORAGE] [DEBUG-SIM] Failed to mark disconnected: %v", err) + w.logger.Printf("[ERROR] [storage] (simulation) Failed to mark disconnected: %v", err) } // Step 3: SKIPPED (no lazyUnmount — drive stays physically mounted) @@ -761,7 +761,7 @@ func (w *StorageWatchdog) SimulateDisconnect(ctx context.Context, path string) ( go w.pushHubReport() } - w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Disconnect simulated: %s — %d stack(s) stopped", path, len(stoppedStacks)) + w.logger.Printf("[INFO] [storage] (simulation) Disconnect simulated: %s — %d stack(s) stopped", path, len(stoppedStacks)) return stoppedStacks, nil } @@ -780,7 +780,7 @@ func (w *StorageWatchdog) SimulateReconnect(ctx context.Context, path string) er if label == "" { label = sp.Path } - w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Simulating reconnect: %s (%s)", path, label) + w.logger.Printf("[INFO] [storage] (simulation) Simulating reconnect: %s (%s)", path, label) // Remove from simulated set w.simulatedMu.Lock() @@ -801,7 +801,7 @@ func (w *StorageWatchdog) SimulateReconnect(ctx context.Context, path string) er // Clear disconnected, preserve stopped stacks for restart UI if err := w.settings.SetDisconnected(path, false, filteredStacks); err != nil { - w.logger.Printf("[ERROR] [STORAGE] [DEBUG-SIM] Failed to clear disconnected: %v", err) + w.logger.Printf("[ERROR] [storage] (simulation) Failed to clear disconnected: %v", err) } // Update in-memory state @@ -823,7 +823,7 @@ func (w *StorageWatchdog) SimulateReconnect(ctx context.Context, path string) er go w.pushHubReport() } - w.logger.Printf("[INFO] [STORAGE] [DEBUG-SIM] Reconnect simulated: %s", path) + w.logger.Printf("[INFO] [storage] (simulation) Reconnect simulated: %s", path) return nil } diff --git a/controller/internal/report/builder.go b/controller/internal/report/builder.go index c04cb8b..a438eab 100644 --- a/controller/internal/report/builder.go +++ b/controller/internal/report/builder.go @@ -38,7 +38,7 @@ func BuildReport( logger.Printf("[INFO] [report] Building system report") } if debug && logger != nil { - logger.Printf("[DEBUG] BuildReport: starting — version=%s, storagePaths=%d", version, len(storagePaths)) + logger.Printf("[DEBUG] [report] BuildReport: starting — version=%s, storagePaths=%d", version, len(storagePaths)) } r := &Report{ @@ -60,7 +60,7 @@ func BuildReport( h := sha256.Sum256(data) r.ConfigHash = hex.EncodeToString(h[:]) if debug && logger != nil { - logger.Printf("[DEBUG] BuildReport: configHash=%s (%d bytes)", r.ConfigHash[:12]+"...", len(data)) + logger.Printf("[DEBUG] [report] BuildReport: configHash=%s (%d bytes)", r.ConfigHash[:12]+"...", len(data)) } } } @@ -126,9 +126,9 @@ func BuildReport( } if debug && logger != nil { - logger.Printf("[DEBUG] BuildReport: system info collected — cpu=%.1f%%, mem=%d/%dMB, temp=%.1fC", + logger.Printf("[DEBUG] [report] BuildReport: system info collected — cpu=%.1f%%, mem=%d/%dMB, temp=%.1fC", sysInfo.CPUPercent, sysInfo.UsedMemMB, sysInfo.TotalMemMB, sysInfo.TemperatureCelsius) - logger.Printf("[DEBUG] BuildReport: storage entries=%d", len(r.Storage)) + logger.Printf("[DEBUG] [report] BuildReport: storage entries=%d", len(r.Storage)) } // Containers @@ -175,7 +175,7 @@ func BuildReport( } if debug && logger != nil { - logger.Printf("[DEBUG] BuildReport: complete — containers=%d, health=%s, deployed=%d, available=%d, app_telemetry=%d", + logger.Printf("[DEBUG] [report] BuildReport: complete — containers=%d, health=%s, deployed=%d, available=%d, app_telemetry=%d", r.Containers.Total, r.Health.Status, len(r.Stacks.Deployed), len(r.Stacks.Available), len(r.AppTelemetry)) } diff --git a/controller/internal/report/infra_backup.go b/controller/internal/report/infra_backup.go index d50522c..4066d52 100644 --- a/controller/internal/report/infra_backup.go +++ b/controller/internal/report/infra_backup.go @@ -67,14 +67,14 @@ func BuildInfraBackup( if data, err := os.ReadFile(settingsPath); err == nil { ib.SettingsJSONB64 = base64.StdEncoding.EncodeToString(data) } else if !os.IsNotExist(err) { - logger.Printf("[WARN] Infra backup: could not read settings.json: %v", err) + logger.Printf("[WARN] [report] Infra backup: could not read settings.json: %v", err) } // Read primary restic password (important but non-fatal) if data, err := os.ReadFile(resticPasswordFile); err == nil { ib.ResticPassword = base64.StdEncoding.EncodeToString(data) } else if !os.IsNotExist(err) { - logger.Printf("[WARN] Infra backup: could not read restic password file: %v", err) + logger.Printf("[WARN] [report] Infra backup: could not read restic password file: %v", err) } // Read encryption key for app.yaml secrets (important but non-fatal) @@ -82,7 +82,7 @@ func BuildInfraBackup( if data, err := os.ReadFile(encryptionKeyFile); err == nil { ib.EncryptionKeyB64 = base64.StdEncoding.EncodeToString(data) } else if !os.IsNotExist(err) { - logger.Printf("[WARN] Infra backup: could not read encryption key file: %v", err) + logger.Printf("[WARN] [report] Infra backup: could not read encryption key file: %v", err) } } @@ -103,5 +103,6 @@ func BuildInfraBackup( ib.DeployedStacks = []InfraStack{} } + logger.Printf("[INFO] [report] InfraBackup built successfully (stacks=%d)", len(ib.DeployedStacks)) return ib, nil } diff --git a/controller/internal/report/pusher.go b/controller/internal/report/pusher.go index ebf0b36..74a4552 100644 --- a/controller/internal/report/pusher.go +++ b/controller/internal/report/pusher.go @@ -72,7 +72,7 @@ func (p *Pusher) Push(report *Report) error { url := p.hubURL + "/api/v1/report" if p.debug { - p.logger.Printf("[DEBUG] Push: url=%s payload=%d bytes", url, len(data)) + p.logger.Printf("[DEBUG] [report] Push: url=%s payload=%d bytes", url, len(data)) } p.statusMu.Lock() @@ -106,7 +106,7 @@ func (p *Pusher) Push(report *Report) error { resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode < 300 { - p.logger.Printf("[INFO] Hub report pushed successfully (%d bytes)", len(data)) + p.logger.Printf("[INFO] [report] Hub report pushed successfully (%d bytes)", len(data)) p.statusMu.Lock() p.status.LastSuccess = time.Now() p.status.LastError = "" @@ -151,7 +151,7 @@ func (p *Pusher) PushInfraBackup(data []byte) error { url := p.hubURL + "/api/v1/infra-backup" if p.debug { - p.logger.Printf("[DEBUG] PushInfraBackup: url=%s payload=%d bytes", url, len(data)) + p.logger.Printf("[DEBUG] [report] PushInfraBackup: url=%s payload=%d bytes", url, len(data)) } var lastErr error @@ -179,12 +179,12 @@ func (p *Pusher) PushInfraBackup(data []byte) error { resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode < 300 { - p.logger.Printf("[INFO] Infra backup pushed to Hub (%d bytes)", len(data)) + p.logger.Printf("[INFO] [report] Infra backup pushed to Hub (%d bytes)", len(data)) return nil } lastErr = fmt.Errorf("HTTP %d", resp.StatusCode) if p.debug { - p.logger.Printf("[DEBUG] PushInfraBackup: attempt %d failed — HTTP %d", attempt+1, resp.StatusCode) + p.logger.Printf("[DEBUG] [report] PushInfraBackup: attempt %d failed — HTTP %d", attempt+1, resp.StatusCode) } } @@ -221,7 +221,7 @@ func (p *Pusher) PushOnce(report *Report) error { resp.Body.Close() if resp.StatusCode >= 200 && resp.StatusCode < 300 { - p.logger.Printf("[INFO] Hub push-once sent (%d bytes)", len(data)) + p.logger.Printf("[INFO] [report] Hub push-once sent (%d bytes)", len(data)) return nil } return fmt.Errorf("hub push-once: HTTP %d", resp.StatusCode) diff --git a/controller/internal/scheduler/scheduler.go b/controller/internal/scheduler/scheduler.go index 7c9a4c2..2d86be4 100644 --- a/controller/internal/scheduler/scheduler.go +++ b/controller/internal/scheduler/scheduler.go @@ -17,7 +17,7 @@ func getBudapestLocation() *time.Location { budapestLocOnce.Do(func() { loc, err := time.LoadLocation("Europe/Budapest") if err != nil { - log.Printf("[ERROR] Cannot load Europe/Budapest timezone: %v — using UTC", err) + log.Printf("[ERROR] [scheduler] Cannot load Europe/Budapest timezone: %v — using UTC", err) loc = time.UTC } budapestLoc = loc @@ -60,7 +60,7 @@ func (s *Scheduler) SetDebug(on bool) { func (s *Scheduler) dbg(format string, args ...interface{}) { if s.debug { - s.logger.Printf("[DEBUG] [sched] "+format, args...) + s.logger.Printf("[DEBUG] [scheduler] "+format, args...) } } diff --git a/controller/internal/selfupdate/state.go b/controller/internal/selfupdate/state.go index 84bfa9f..a2c41ce 100644 --- a/controller/internal/selfupdate/state.go +++ b/controller/internal/selfupdate/state.go @@ -66,7 +66,7 @@ func SaveState(dataDir string, state *UpdateState) error { func ClearState(dataDir string, logger *log.Logger) { path := filepath.Join(dataDir, stateFileName) if err := os.Remove(path); err != nil && !os.IsNotExist(err) { - logger.Printf("[WARN] Failed to clear update state file: %v", err) + logger.Printf("[WARN] [selfupdate] Failed to clear update state file: %v", err) return } logger.Printf("[INFO] [selfupdate] Update state cleared") diff --git a/controller/internal/selfupdate/updater.go b/controller/internal/selfupdate/updater.go index 6f6b0fe..a426c4a 100644 --- a/controller/internal/selfupdate/updater.go +++ b/controller/internal/selfupdate/updater.go @@ -91,7 +91,7 @@ func (u *Updater) GetStatus() UpdateStatus { state, err := LoadState(u.dataDir) if err != nil { - u.logger.Printf("[WARN] Failed to load update state: %v", err) + u.logger.Printf("[WARN] [selfupdate] Failed to load update state: %v", err) } return UpdateStatus{ @@ -123,7 +123,7 @@ func (u *Updater) CheckForUpdate() CheckResult { latestStr, err := u.queryRegistry() if err != nil { result.Error = fmt.Sprintf("Registry lekérdezés sikertelen: %v", err) - u.logger.Printf("[WARN] Registry check failed: %v", err) + u.logger.Printf("[WARN] [selfupdate] Registry check failed: %v", err) u.mu.Lock() u.lastCheck = &result u.mu.Unlock() @@ -342,7 +342,7 @@ func (u *Updater) TriggerUpdate(initiatedBy string) error { targetImage := fmt.Sprintf("%s:%s", u.cfg.Image, targetVersion) previousImage := fmt.Sprintf("%s:%s", u.cfg.Image, u.currentVer) - u.logger.Printf("[INFO] Starting self-update: %s → %s (initiated by: %s)", u.currentVer, targetVersion, initiatedBy) + u.logger.Printf("[INFO] [selfupdate] Starting self-update: %s → %s (initiated by: %s)", u.currentVer, targetVersion, initiatedBy) u.dbg("TriggerUpdate: target=%s image=%s previousImage=%s", targetVersion, targetImage, previousImage) go u.performUpdate(targetVersion, targetImage, previousImage, initiatedBy) @@ -370,13 +370,13 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi InitiatedBy: initiatedBy, } if err := SaveState(u.dataDir, state); err != nil { - u.logger.Printf("[ERROR] Failed to save update state: %v", err) + u.logger.Printf("[ERROR] [selfupdate] Failed to save update state: %v", err) return } // 2. Docker pull u.dbg("performUpdate: step 2 — docker pull %s", targetImage) - u.logger.Printf("[INFO] Pulling image: %s", targetImage) + u.logger.Printf("[INFO] [selfupdate] Pulling image: %s", targetImage) pullStart := time.Now() pullOut, pullErr := runCommand("docker", "pull", targetImage) if pullErr != nil { @@ -384,10 +384,10 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi state.Error = fmt.Sprintf("docker pull failed: %v — %s", pullErr, pullOut) state.CompletedAt = time.Now().UTC().Format(time.RFC3339) SaveState(u.dataDir, state) - u.logger.Printf("[ERROR] Docker pull failed: %v — %s", pullErr, pullOut) + u.logger.Printf("[ERROR] [selfupdate] Docker pull failed: %v — %s", pullErr, pullOut) return } - u.logger.Printf("[INFO] Image pulled successfully: %s", targetImage) + u.logger.Printf("[INFO] [selfupdate] Image pulled successfully: %s", targetImage) u.dbg("performUpdate: docker pull completed in %s", time.Since(pullStart).Round(time.Millisecond)) // 3. Update compose file (replace image tag) @@ -397,14 +397,14 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi state.Error = fmt.Sprintf("compose update failed: %v", err) state.CompletedAt = time.Now().UTC().Format(time.RFC3339) SaveState(u.dataDir, state) - u.logger.Printf("[ERROR] Compose file update failed: %v", err) + u.logger.Printf("[ERROR] [selfupdate] Compose file update failed: %v", err) return } - u.logger.Printf("[INFO] Compose file updated with new image: %s", targetImage) + u.logger.Printf("[INFO] [selfupdate] Compose file updated with new image: %s", targetImage) // 4. Docker compose up -d (this kills the current container) u.dbg("performUpdate: step 4 — docker compose up -d") - u.logger.Printf("[INFO] Running docker compose up -d — container will restart") + u.logger.Printf("[INFO] [selfupdate] Running docker compose up -d — container will restart") composeDir := strings.TrimSuffix(u.composePath, "/docker-compose.yml") upOut, upErr := runCommand("docker", "compose", "-f", u.composePath, "-p", "felhom-controller", "up", "-d") if upErr != nil { @@ -412,15 +412,15 @@ func (u *Updater) performUpdate(targetVersion, targetImage, previousImage, initi state.Error = fmt.Sprintf("docker compose up -d failed: %v — %s", upErr, upOut) state.CompletedAt = time.Now().UTC().Format(time.RFC3339) SaveState(u.dataDir, state) - u.logger.Printf("[ERROR] docker compose up -d failed: %v — %s (dir: %s)", upErr, upOut, composeDir) + u.logger.Printf("[ERROR] [selfupdate] docker compose up -d failed: %v — %s (dir: %s)", upErr, upOut, composeDir) return } // If we're still alive after compose up -d, log it. // Normally this process should be killed when Docker replaces the container. - u.logger.Printf("[WARN] Still running after docker compose up -d — expected to be replaced") + u.logger.Printf("[WARN] [selfupdate] Still running after docker compose up -d — expected to be replaced") time.Sleep(30 * time.Second) - u.logger.Printf("[WARN] Still alive 30s after docker compose up -d") + u.logger.Printf("[WARN] [selfupdate] Still alive 30s after docker compose up -d") } // updateComposeFile reads the compose file, replaces the image tag, and writes it back atomically. @@ -471,7 +471,7 @@ func (u *Updater) VerifyStartup() *UpdateState { u.dbg("VerifyStartup: checking update state in %s", u.dataDir) state, err := LoadState(u.dataDir) if err != nil { - u.logger.Printf("[WARN] Failed to load update state on startup: %v — clearing", err) + u.logger.Printf("[WARN] [selfupdate] Failed to load update state on startup: %v — clearing", err) ClearState(u.dataDir, u.logger) return nil } @@ -490,7 +490,7 @@ func (u *Updater) VerifyStartup() *UpdateState { state.Error = "Version parse error on startup verification" state.CompletedAt = time.Now().UTC().Format(time.RFC3339) SaveState(u.dataDir, state) - u.logger.Printf("[WARN] Post-update startup: version parse error (current=%s, target=%s)", u.currentVer, state.TargetVersion) + u.logger.Printf("[WARN] [selfupdate] Post-update startup: version parse error (current=%s, target=%s)", u.currentVer, state.TargetVersion) return state } @@ -499,14 +499,14 @@ func (u *Updater) VerifyStartup() *UpdateState { state.Status = "success" state.CompletedAt = time.Now().UTC().Format(time.RFC3339) SaveState(u.dataDir, state) - u.logger.Printf("[INFO] Post-update startup: update successful (%s → %s)", state.PreviousVersion, state.TargetVersion) + u.logger.Printf("[INFO] [selfupdate] Post-update startup: update successful (%s → %s)", state.PreviousVersion, state.TargetVersion) } else { // Version mismatch — update may have failed state.Status = "failed" state.Error = fmt.Sprintf("Version mismatch: expected %s, running %s", state.TargetVersion, u.currentVer) state.CompletedAt = time.Now().UTC().Format(time.RFC3339) SaveState(u.dataDir, state) - u.logger.Printf("[WARN] Post-update startup: version mismatch (expected %s, running %s)", state.TargetVersion, u.currentVer) + u.logger.Printf("[WARN] [selfupdate] Post-update startup: version mismatch (expected %s, running %s)", state.TargetVersion, u.currentVer) } return state diff --git a/controller/internal/settings/settings.go b/controller/internal/settings/settings.go index 1672ceb..fd6900e 100644 --- a/controller/internal/settings/settings.go +++ b/controller/internal/settings/settings.go @@ -173,7 +173,7 @@ func Load(path string, logger *log.Logger) (*Settings, error) { data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { - logger.Printf("[INFO] No settings.json found, using defaults") + logger.Printf("[INFO] [settings] No settings.json found, using defaults") return s, nil } return nil, fmt.Errorf("reading settings file: %w", err) @@ -201,14 +201,14 @@ func (s *Settings) migrateResticToRsync() { prefs.CrossDrive.Method = "rsync" s.AppBackup[name] = prefs if s.log != nil { - s.log.Printf("[INFO] Migrated cross-drive backup for %s from restic to rsync", name) + s.log.Printf("[INFO] [settings] Migrated cross-drive backup for %s from restic to rsync", name) } changed = true } } if changed { if err := s.save(); err != nil && s.log != nil { - s.log.Printf("[ERROR] Failed to save restic→rsync migration: %v", err) + s.log.Printf("[ERROR] [settings] Failed to save restic→rsync migration: %v", err) } } } @@ -592,12 +592,12 @@ func (s *Settings) AutoDiscoverStoragePaths(discoveredPaths []string, fallbackHD if len(s.StoragePaths) > 0 { if err := s.save(); err != nil { - logger.Printf("[ERROR] Failed to save auto-discovered storage paths: %v", err) + logger.Printf("[ERROR] [settings] Failed to save auto-discovered storage paths: %v", err) return } - logger.Printf("[INFO] Auto-discovered %d storage path(s)", len(s.StoragePaths)) + logger.Printf("[INFO] [settings] Auto-discovered %d storage path(s)", len(s.StoragePaths)) for _, sp := range s.StoragePaths { - logger.Printf("[INFO] %s (%s) default=%v", sp.Path, sp.Label, sp.IsDefault) + logger.Printf("[INFO] [settings] %s (%s) default=%v", sp.Path, sp.Label, sp.IsDefault) } } } @@ -619,7 +619,7 @@ func (s *Settings) SetDisconnected(path string, disconnected bool, stoppedStacks s.log.Printf("[DEBUG] [settings] SetDisconnected path=%q disconnected=%v stopped_stacks=%d", path, disconnected, len(stoppedStacks)) } if s.log != nil { - s.log.Printf("[INFO] [settings] Node disconnected: %v", disconnected) + s.log.Printf("[INFO] [settings] Storage path %s disconnected=%v", path, disconnected) } for i := range s.StoragePaths { if s.StoragePaths[i].Path == path { @@ -732,7 +732,7 @@ func (s *Settings) SetDecommissioned(path, migratedTo string) error { s.log.Printf("[DEBUG] [settings] SetDecommissioned path=%q migrated_to=%q", path, migratedTo) } if s.log != nil { - s.log.Printf("[INFO] [settings] Node decommissioned") + s.log.Printf("[INFO] [settings] Storage path %s decommissioned (migrated_to=%s)", path, migratedTo) } for i := range s.StoragePaths { if s.StoragePaths[i].Path == path { @@ -890,7 +890,7 @@ func (s *Settings) DrainPendingEvents() []PendingEvent { copy(events, s.PendingEvents) s.PendingEvents = nil if err := s.save(); err != nil { - s.log.Printf("[ERROR] Failed to save after draining pending events: %v — restoring events", err) + s.log.Printf("[ERROR] [settings] Failed to save after draining pending events: %v — restoring events", err) s.PendingEvents = events return nil } diff --git a/controller/internal/setup/setup.go b/controller/internal/setup/setup.go index 5c41b18..f13b704 100644 --- a/controller/internal/setup/setup.go +++ b/controller/internal/setup/setup.go @@ -101,7 +101,7 @@ func (s *SetupState) SetStep(step string) { s.Step = step s.mu.Unlock() if err := s.Save(); err != nil { - log.Printf("[WARN] Failed to save setup step %q: %v", step, err) + log.Printf("[WARN] [setup] Failed to save setup step %q: %v", step, err) } } diff --git a/controller/internal/stacks/deploy.go b/controller/internal/stacks/deploy.go index 9602fd7..37b5107 100644 --- a/controller/internal/stacks/deploy.go +++ b/controller/internal/stacks/deploy.go @@ -161,12 +161,12 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) { reservedMB := m.cfg.System.ReservedMemoryMB totalMB, usedMB, memErr := system.GetMemoryMB() if memErr != nil { - m.logger.Printf("[WARN] Cannot read system memory: %v — skipping memory check", memErr) + m.logger.Printf("[WARN] [stacks] Cannot read system memory: %v — skipping memory check", memErr) } else { usableMB := totalMB - reservedMB newReqMB := ParseMemoryMB(meta.Resources.MemRequest) - m.logger.Printf("[INFO] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, real_used=%dMB, new_req=%dMB, remaining=%dMB", + m.logger.Printf("[INFO] [stacks] Memory check: total=%dMB, reserved=%dMB, usable=%dMB, real_used=%dMB, new_req=%dMB, remaining=%dMB", totalMB, reservedMB, usableMB, usedMB, newReqMB, usableMB-usedMB-newReqMB) // Hard block: real used + new request exceeds usable memory @@ -309,7 +309,7 @@ func (m *Manager) DeployStack(req DeployRequest) (string, error) { for k := range env { envKeys = append(envKeys, k) } - m.logger.Printf("[INFO] Deploying stack %s with %d env vars: [%s]", req.StackName, len(env), strings.Join(envKeys, ", ")) + m.logger.Printf("[INFO] [stacks] Deploying stack %s with %d env vars: [%s]", req.StackName, len(env), strings.Join(envKeys, ", ")) // Check which images are available locally before pulling if m.isDebug() { @@ -339,7 +339,7 @@ func (m *Manager) runComposeDeploy(name, stackDir string, env map[string]string, _, composeErr := m.composeExecWithEnv(stackDir, env, "up", "-d") if composeErr != nil { - m.logger.Printf("[ERROR] Stack %s deploy failed after %.1fs: %v", name, time.Since(start).Seconds(), composeErr) + m.logger.Printf("[ERROR] [stacks] Stack %s deploy failed after %.1fs: %v", name, time.Since(start).Seconds(), composeErr) // Revert in-memory and disk state m.mu.Lock() if s, ok := m.stacks[name]; ok { @@ -357,7 +357,7 @@ func (m *Manager) runComposeDeploy(name, stackDir string, env map[string]string, return } - m.logger.Printf("[INFO] Stack %s deployed successfully (took %.1fs)", name, time.Since(start).Seconds()) + m.logger.Printf("[INFO] [stacks] Stack %s deployed successfully (took %.1fs)", name, time.Since(start).Seconds()) // Clear deploying flag m.mu.Lock() @@ -427,7 +427,7 @@ func (m *Manager) UpdateStackConfig(name string, values map[string]string) error return fmt.Errorf("restarting with new config: %w", err) } - m.logger.Printf("[INFO] Stack %s config updated and restarted", name) + m.logger.Printf("[INFO] [stacks] Stack %s config updated and restarted", name) return m.RefreshStatus() } @@ -503,13 +503,13 @@ func (m *Manager) UpdateOptionalConfig(stackName string, values map[string]strin changed := false for key, val := range values { if !allowed[key] { - m.logger.Printf("[WARN] Ignoring non-optional env var: %s", key) + m.logger.Printf("[WARN] [stacks] Ignoring non-optional env var: %s", key) continue } if appCfg.Env[key] != val { appCfg.Env[key] = val changed = true - m.logger.Printf("[INFO] Updated optional config %s for %s", key, stackName) + m.logger.Printf("[INFO] [stacks] Updated optional config %s for %s", key, stackName) } } @@ -522,12 +522,12 @@ func (m *Manager) UpdateOptionalConfig(stackName string, values map[string]strin if err := SaveAppConfig(stackDir, appCfg, m.encKey, SensitiveEnvVars(&meta)); err != nil { return fmt.Errorf("saving app config: %w", err) } - m.logger.Printf("[INFO] Saved updated app.yaml for %s", stackName) + m.logger.Printf("[INFO] [stacks] Saved updated app.yaml for %s", stackName) // If deployed, recreate containers to pick up new env vars // (docker compose restart does NOT pick up new env vars — must use up -d) if stack.Deployed { - m.logger.Printf("[INFO] Restarting %s to apply new optional config", stackName) + m.logger.Printf("[INFO] [stacks] Restarting %s to apply new optional config", stackName) env := m.stackEnv(stackDir) if _, err := m.composeExecCustomEnv(stackDir, env, "up", "-d"); err != nil { return fmt.Errorf("restart after config update: %w", err) @@ -591,7 +591,6 @@ func LoadAppConfig(stackDir string) *AppConfig { cfg := &AppConfig{} if err := yaml.Unmarshal(data, cfg); err != nil { log.Printf("[WARN] [stacks] LoadAppConfig: %v", err) - log.Printf("[DEBUG] [stacks] LoadAppConfig: failed to parse %s: %v", path, err) return nil } return cfg @@ -618,7 +617,7 @@ func SaveAppConfig(stackDir string, cfg *AppConfig, encKey []byte, sensitiveVars continue } else { // H10 fix: log encryption failure — value will be saved in plaintext. - log.Printf("[WARN] Failed to encrypt env var %q: %v — saving as plaintext", k, err) + log.Printf("[WARN] [stacks] Failed to encrypt env var %q: %v — saving as plaintext", k, err) } } saveCfg.Env[k] = v @@ -763,12 +762,12 @@ func (m *Manager) InjectMissingFields(stackNames []string) { switch field.Type { case "secret": if field.Generate == "" { - m.logger.Printf("[WARN] Stack %s: new secret field %s has no generator — skipping", name, field.EnvVar) + m.logger.Printf("[WARN] [stacks] Stack %s: new secret field %s has no generator — skipping", name, field.EnvVar) continue } value, err := generateValue(field.Generate) if err != nil { - m.logger.Printf("[ERROR] Stack %s: failed to generate %s: %v", name, field.EnvVar, err) + m.logger.Printf("[ERROR] [stacks] Stack %s: failed to generate %s: %v", name, field.EnvVar, err) continue } appCfg.Env[field.EnvVar] = value @@ -791,7 +790,7 @@ func (m *Manager) InjectMissingFields(stackNames []string) { val = meta.Subdomain } if val == "" { - m.logger.Printf("[WARN] Stack %s: new subdomain field %s has no default — skipping", name, field.EnvVar) + m.logger.Printf("[WARN] [stacks] Stack %s: new subdomain field %s has no default — skipping", name, field.EnvVar) continue } appCfg.Env[field.EnvVar] = val @@ -801,16 +800,16 @@ func (m *Manager) InjectMissingFields(stackNames []string) { injected = append(injected, field.EnvVar) default: - m.logger.Printf("[WARN] Stack %s: new field %s (type=%s) requires manual configuration", name, field.EnvVar, field.Type) + m.logger.Printf("[WARN] [stacks] Stack %s: new field %s (type=%s) requires manual configuration", name, field.EnvVar, field.Type) } } if len(injected) > 0 { if err := SaveAppConfig(stackDir, appCfg, m.encKey, SensitiveEnvVars(&meta)); err != nil { - m.logger.Printf("[ERROR] Stack %s: failed to save app.yaml after injection: %v", name, err) + m.logger.Printf("[ERROR] [stacks] Stack %s: failed to save app.yaml after injection: %v", name, err) continue } - m.logger.Printf("[SYNC] Stack %s: injected missing fields: %s", name, strings.Join(injected, ", ")) + m.logger.Printf("[INFO] [stacks] Stack %s: injected missing fields: %s", name, strings.Join(injected, ", ")) } } m.logger.Printf("[INFO] [stacks] InjectMissingFields: processed %d stacks", count) diff --git a/controller/internal/stacks/manager.go b/controller/internal/stacks/manager.go index 15a8a8d..71fad09 100644 --- a/controller/internal/stacks/manager.go +++ b/controller/internal/stacks/manager.go @@ -96,7 +96,7 @@ func NewManager(cfg *config.Config, logger *log.Logger) (*Manager, error) { return nil, fmt.Errorf("docker compose not found (tried 'docker compose' and 'docker-compose')") } - logger.Printf("[INFO] Using compose command: %s", composeCmd) + logger.Printf("[INFO] [stacks] Using compose command: %s", composeCmd) if err := os.MkdirAll(cfg.Paths.StacksDir, 0755); err != nil { return nil, fmt.Errorf("creating stacks directory %s: %w", cfg.Paths.StacksDir, err) @@ -176,14 +176,14 @@ func (m *Manager) MigrateEncryption() { m.logger.Printf("[DEBUG] [stacks] MigrateEncryption: stack %q needs migration — re-saving with encryption", s.Name) } if err := SaveAppConfig(stackDir, appCfg, m.encKey, sensitive); err != nil { - m.logger.Printf("[WARN] Encryption migration failed for %s: %v", s.Name, err) + m.logger.Printf("[WARN] [stacks] Encryption migration failed for %s: %v", s.Name, err) } else { migrated++ } } } if migrated > 0 { - m.logger.Printf("[INFO] Encrypted sensitive values in %d app.yaml file(s)", migrated) + m.logger.Printf("[INFO] [stacks] Encrypted sensitive values in %d app.yaml file(s)", migrated) } else { m.logger.Printf("[INFO] [stacks] Encryption migration: no stacks needed migration") } @@ -316,7 +316,7 @@ func (m *Manager) ScanStacks() error { } } if orphanCount > 0 { - m.logger.Printf("[INFO] Detected %d orphaned stack(s)", orphanCount) + m.logger.Printf("[INFO] [stacks] Detected %d orphaned stack(s)", orphanCount) } } @@ -326,9 +326,8 @@ func (m *Manager) ScanStacks() error { deployedCount++ } } - m.logger.Printf("[INFO] Scanned stacks: %d found (%d deployed, %d available)", + m.logger.Printf("[INFO] [stacks] ScanStacks complete: %d stacks found (%d deployed, %d available)", len(m.stacks), deployedCount, len(m.stacks)-deployedCount) - m.logger.Printf("[INFO] [stacks] ScanStacks complete: %d stacks found", len(m.stacks)) return m.refreshStatusLocked() } @@ -628,7 +627,7 @@ func (m *Manager) StartStack(name string) error { m.logger.Printf("[DEBUG] [stacks] StartStack %s: current state=%s deployed=%v", name, stack.State, stack.Deployed) } - m.logger.Printf("[INFO] Starting stack: %s", name) + m.logger.Printf("[INFO] [stacks] Starting stack: %s", name) start := time.Now() dir := filepath.Dir(stack.ComposePath) @@ -639,11 +638,11 @@ func (m *Manager) StartStack(name string) error { } if _, err := m.composeExecCustomEnv(dir, env, "up", "-d"); err != nil { - m.logger.Printf("[ERROR] Stack %s start failed after %.1fs: %v", name, time.Since(start).Seconds(), err) + m.logger.Printf("[ERROR] [stacks] Stack %s start failed after %.1fs: %v", name, time.Since(start).Seconds(), err) return fmt.Errorf("starting stack %s: %w", name, err) } - m.logger.Printf("[INFO] Stack %s started successfully (took %.1fs)", name, time.Since(start).Seconds()) + m.logger.Printf("[INFO] [stacks] Stack %s started successfully (took %.1fs)", name, time.Since(start).Seconds()) m.logPostStartStatus(name, dir, env) // Clear stale health probe so refreshStatus won't re-apply an old unhealthy override. @@ -671,16 +670,16 @@ func (m *Manager) StopStack(name string) error { m.logger.Printf("[DEBUG] [stacks] StopStack %s: current state=%s deployed=%v containers=%d", name, stack.State, stack.Deployed, len(stack.Containers)) } - m.logger.Printf("[INFO] Stopping stack: %s", name) + m.logger.Printf("[INFO] [stacks] Stopping stack: %s", name) start := time.Now() dir := filepath.Dir(stack.ComposePath) if _, err := m.composeExec(dir, "down"); err != nil { - m.logger.Printf("[ERROR] Stack %s stop failed after %.1fs: %v", name, time.Since(start).Seconds(), err) + m.logger.Printf("[ERROR] [stacks] Stack %s stop failed after %.1fs: %v", name, time.Since(start).Seconds(), err) return fmt.Errorf("stopping stack %s: %w", name, err) } - m.logger.Printf("[INFO] Stack %s stopped successfully (took %.1fs)", name, time.Since(start).Seconds()) + m.logger.Printf("[INFO] [stacks] Stack %s stopped successfully (took %.1fs)", name, time.Since(start).Seconds()) return m.RefreshStatus() } @@ -694,7 +693,7 @@ func (m *Manager) RestartStack(name string) error { m.logger.Printf("[DEBUG] [stacks] RestartStack %s: current state=%s deployed=%v containers=%d", name, stack.State, stack.Deployed, len(stack.Containers)) } - m.logger.Printf("[INFO] Restarting stack: %s", name) + m.logger.Printf("[INFO] [stacks] Restarting stack: %s", name) start := time.Now() dir := filepath.Dir(stack.ComposePath) env := m.stackEnv(dir) @@ -704,11 +703,11 @@ func (m *Manager) RestartStack(name string) error { // picked up. Plain "docker compose restart" only sends SIGTERM+start // to existing containers without re-reading the compose file or env. if _, err := m.composeExecCustomEnv(dir, env, "up", "-d"); err != nil { - m.logger.Printf("[ERROR] Stack %s restart failed after %.1fs: %v", name, time.Since(start).Seconds(), err) + m.logger.Printf("[ERROR] [stacks] Stack %s restart failed after %.1fs: %v", name, time.Since(start).Seconds(), err) return fmt.Errorf("restarting stack %s: %w", name, err) } - m.logger.Printf("[INFO] Stack %s restarted successfully (took %.1fs)", name, time.Since(start).Seconds()) + m.logger.Printf("[INFO] [stacks] Stack %s restarted successfully (took %.1fs)", name, time.Since(start).Seconds()) m.logPostStartStatus(name, dir, env) // Clear stale health probe so refreshStatus won't re-apply an old unhealthy override. @@ -727,7 +726,7 @@ func (m *Manager) UpdateStack(name string) error { return fmt.Errorf("stack %q not found", name) } - m.logger.Printf("[INFO] Updating stack: %s", name) + m.logger.Printf("[INFO] [stacks] Updating stack: %s", name) start := time.Now() dir := filepath.Dir(stack.ComposePath) env := m.stackEnv(dir) @@ -737,16 +736,16 @@ func (m *Manager) UpdateStack(name string) error { } if _, err := m.composeExecCustomEnv(dir, env, "pull"); err != nil { - m.logger.Printf("[ERROR] Stack %s update (pull) failed after %.1fs: %v", name, time.Since(start).Seconds(), err) + m.logger.Printf("[ERROR] [stacks] Stack %s update (pull) failed after %.1fs: %v", name, time.Since(start).Seconds(), err) return fmt.Errorf("pulling images for %s: %w", name, err) } if _, err := m.composeExecCustomEnv(dir, env, "up", "-d", "--remove-orphans"); err != nil { - m.logger.Printf("[ERROR] Stack %s update (up) failed after %.1fs: %v", name, time.Since(start).Seconds(), err) + m.logger.Printf("[ERROR] [stacks] Stack %s update (up) failed after %.1fs: %v", name, time.Since(start).Seconds(), err) return fmt.Errorf("recreating %s: %w", name, err) } - m.logger.Printf("[INFO] Stack %s updated successfully (took %.1fs)", name, time.Since(start).Seconds()) + m.logger.Printf("[INFO] [stacks] Stack %s updated successfully (took %.1fs)", name, time.Since(start).Seconds()) m.logPostStartStatus(name, dir, env) return m.RefreshStatus() } @@ -765,12 +764,11 @@ func (m *Manager) GetLogs(name string, lines int) (string, error) { } m.logger.Printf("[INFO] [stacks] Fetching logs for stack %s (tail=%d)", name, lines) - m.logger.Printf("[DEBUG] Fetching logs for %s (tail %d)", name, lines) dir := filepath.Dir(stack.ComposePath) output, err := m.composeExec(dir, "logs", "--tail", fmt.Sprintf("%d", lines), "--no-color") if err != nil { - m.logger.Printf("[WARN] Failed to fetch logs for %s: %v", name, err) + m.logger.Printf("[WARN] [stacks] Failed to fetch logs for %s: %v", name, err) return "", fmt.Errorf("getting logs for %s: %w", name, err) } @@ -860,13 +858,13 @@ func (m *Manager) composeExecCustomEnv(dir string, env []string, args ...string) if exitErr, ok := err.(*exec.ExitError); ok { exitCode = exitErr.ExitCode() } - m.logger.Printf("[ERROR] Command failed: %s %s (in %s) — exit code %d (took %.1fs)", + m.logger.Printf("[ERROR] [stacks] Command failed: %s %s (in %s) — exit code %d (took %.1fs)", m.composeCmd, strings.Join(args, " "), dir, exitCode, elapsed.Seconds()) if stdoutStr := truncateStr(stdout.String(), 500); stdoutStr != "" { - m.logger.Printf("[ERROR] stdout: %s", stdoutStr) + m.logger.Printf("[ERROR] [stacks] stdout: %s", stdoutStr) } if stderrStr := truncateStr(stderr.String(), 500); stderrStr != "" { - m.logger.Printf("[ERROR] stderr: %s", stderrStr) + m.logger.Printf("[ERROR] [stacks] stderr: %s", stderrStr) } return stdout.String(), fmt.Errorf("exit code %d\nstderr: %s", exitCode, truncateStr(stderr.String(), 500)) } @@ -883,6 +881,7 @@ func (m *Manager) execCommand(name string, args ...string) (string, error) { cmd.Stderr = &stderr if err := cmd.Run(); err != nil { + m.logger.Printf("[ERROR] [stacks] execCommand failed: %v", err) return "", fmt.Errorf("exec %s %s: %w\nstderr: %s", name, strings.Join(args, " "), err, stderr.String()) } @@ -913,20 +912,20 @@ func (m *Manager) logPostStartStatus(name, stackDir string, env []string) { output, err := m.composeExecCustomEnv(stackDir, envCopy, "ps", "-a", "--format", "table {{.Name}}\t{{.Image}}\t{{.State}}\t{{.Status}}") if err != nil { - m.logger.Printf("[WARN] Post-start status check failed for %s: %v", name, err) + m.logger.Printf("[WARN] [stacks] Post-start status check failed for %s: %v", name, err) return } lines := strings.Split(strings.TrimSpace(output), "\n") if len(lines) <= 1 { - m.logger.Printf("[WARN] Post-start status for %s: no containers found", name) + m.logger.Printf("[WARN] [stacks] Post-start status for %s: no containers found", name) return } - m.logger.Printf("[INFO] Stack %s post-start status:", name) + m.logger.Printf("[INFO] [stacks] Stack %s post-start status:", name) // Skip header line for _, line := range lines[1:] { - m.logger.Printf("[INFO] %s", line) + m.logger.Printf("[INFO] [stacks] %s", line) } }() } @@ -962,7 +961,7 @@ func (m *Manager) checkLocalImages(name, stackDir string) { return } - m.logger.Printf("[INFO] Deploying stack %s — checking %d images...", name, len(images)) + m.logger.Printf("[INFO] [stacks] Deploying stack %s — checking %d images...", name, len(images)) for _, img := range images { cmd := exec.Command("docker", "image", "inspect", img) if err := cmd.Run(); err != nil { @@ -1057,7 +1056,7 @@ func (m *Manager) getCatalogTemplateSlugs() map[string]bool { cacheDir := filepath.Join(m.cfg.Paths.DataDir, "catalog-cache", "templates") entries, err := os.ReadDir(cacheDir) if err != nil { - m.logger.Printf("[WARN] Cannot read catalog cache for orphan detection: %v", err) + m.logger.Printf("[WARN] [stacks] Cannot read catalog cache for orphan detection: %v", err) return nil } slugs := make(map[string]bool, len(entries)) diff --git a/controller/internal/storage/migrate.go b/controller/internal/storage/migrate.go index cfa5f06..d0701bb 100644 --- a/controller/internal/storage/migrate.go +++ b/controller/internal/storage/migrate.go @@ -373,7 +373,7 @@ func (o *MigrateOrchestrator) RunEnhancedMigration( // to the same drive the app now lives on — no redundancy, so we clear it. if strings.HasPrefix(cfg.DestinationPath, req.TargetPath) || cfg.DestinationPath == req.TargetPath { tier2WillClear = true - o.Logger.Printf("[INFO] Migration %s: Tier 2 will be cleared (dest %s is under target %s)", + o.Logger.Printf("[INFO] [storage] Migration %s: Tier 2 will be cleared (dest %s is under target %s)", req.StackName, cfg.DestinationPath, req.TargetPath) } } @@ -408,13 +408,13 @@ func (o *MigrateOrchestrator) RunEnhancedMigration( dstDBDumps := backup.AppDBDumpPath(req.TargetPath, req.StackName) if _, err := os.Stat(srcDBDumps); err == nil { if err := os.MkdirAll(filepath.Dir(dstDBDumps), 0755); err != nil { - o.Logger.Printf("[WARN] Migration %s: failed to create DB dump dir: %v", req.StackName, err) + o.Logger.Printf("[WARN] [storage] Migration %s: failed to create DB dump dir: %v", req.StackName, err) } else { cmd := exec.Command("rsync", "-a", srcDBDumps+"/", dstDBDumps+"/") if out, err := cmd.CombinedOutput(); err != nil { - o.Logger.Printf("[WARN] Migration %s: DB dump copy failed: %v — %s", req.StackName, err, string(out)) + o.Logger.Printf("[WARN] [storage] Migration %s: DB dump copy failed: %v — %s", req.StackName, err, string(out)) } else { - o.Logger.Printf("[INFO] Migration %s: DB dumps copied to %s", req.StackName, dstDBDumps) + o.Logger.Printf("[INFO] [storage] Migration %s: DB dumps copied to %s", req.StackName, dstDBDumps) } } } @@ -422,9 +422,9 @@ func (o *MigrateOrchestrator) RunEnhancedMigration( // 2. Clear Tier 2 if conflict if tier2WillClear { if err := o.Sett.SetCrossDriveConfig(req.StackName, nil); err != nil { - o.Logger.Printf("[WARN] Migration %s: failed to clear Tier 2 config: %v", req.StackName, err) + o.Logger.Printf("[WARN] [storage] Migration %s: failed to clear Tier 2 config: %v", req.StackName, err) } else { - o.Logger.Printf("[INFO] Migration %s: Tier 2 cross-drive config cleared (dest was on same drive)", req.StackName) + o.Logger.Printf("[INFO] [storage] Migration %s: Tier 2 cross-drive config cleared (dest was on same drive)", req.StackName) } } @@ -443,18 +443,18 @@ func (o *MigrateOrchestrator) RunEnhancedMigration( continue } if err := os.RemoveAll(srcPath); err != nil { - o.Logger.Printf("[WARN] Migration %s: failed to delete stale data %s: %v", req.StackName, srcPath, err) + o.Logger.Printf("[WARN] [storage] Migration %s: failed to delete stale data %s: %v", req.StackName, srcPath, err) } else { - o.Logger.Printf("[INFO] Migration %s: deleted stale data %s", req.StackName, srcPath) + o.Logger.Printf("[INFO] [storage] Migration %s: deleted stale data %s", req.StackName, srcPath) } } // Delete DB dumps from source if _, err := os.Stat(srcDBDumps); err == nil { if err := os.RemoveAll(srcDBDumps); err != nil { - o.Logger.Printf("[WARN] Migration %s: failed to delete stale DB dumps %s: %v", req.StackName, srcDBDumps, err) + o.Logger.Printf("[WARN] [storage] Migration %s: failed to delete stale DB dumps %s: %v", req.StackName, srcDBDumps, err) } else { - o.Logger.Printf("[INFO] Migration %s: deleted stale DB dumps %s", req.StackName, srcDBDumps) + o.Logger.Printf("[INFO] [storage] Migration %s: deleted stale DB dumps %s", req.StackName, srcDBDumps) } } } @@ -469,7 +469,7 @@ func (o *MigrateOrchestrator) RunEnhancedMigration( } if err := o.BackupTrigger.TryRunDriveBackup(context.Background(), req.TargetPath); err != nil { - o.Logger.Printf("[WARN] Migration %s: post-migration backup failed: %v", req.StackName, err) + o.Logger.Printf("[WARN] [storage] Migration %s: post-migration backup failed: %v", req.StackName, err) progress <- MigrateProgress{ Step: "backing_up", Message: "Biztonsági mentés nem indítható (másik mentés fut)", @@ -477,7 +477,7 @@ func (o *MigrateOrchestrator) RunEnhancedMigration( ElapsedSeconds: int(time.Since(start).Seconds()), } } else { - o.Logger.Printf("[INFO] Migration %s: post-migration backup completed for %s", req.StackName, req.TargetPath) + o.Logger.Printf("[INFO] [storage] Migration %s: post-migration backup completed for %s", req.StackName, req.TargetPath) } } diff --git a/controller/internal/storage/migrate_drive.go b/controller/internal/storage/migrate_drive.go index 55acb6b..12d680b 100644 --- a/controller/internal/storage/migrate_drive.go +++ b/controller/internal/storage/migrate_drive.go @@ -91,9 +91,9 @@ func (tx *migrationTx) add(desc string, undoFn func() error) { func (tx *migrationTx) rollback() { for i := len(tx.actions) - 1; i >= 0; i-- { a := tx.actions[i] - tx.logger.Printf("[ROLLBACK] %s", a.description) + tx.logger.Printf("[INFO] [storage] Rollback: %s", a.description) if err := a.undo(); err != nil { - tx.logger.Printf("[ROLLBACK-ERROR] %s: %v", a.description, err) + tx.logger.Printf("[ERROR] [storage] Rollback failed: %s: %v", a.description, err) } } } @@ -101,6 +101,8 @@ func (tx *migrationTx) rollback() { // MigrateDrive performs a full drive migration, moving all apps from source to dest. func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateRequest, progress chan<- DriveMigrateProgress) error { start := time.Now() + // TODO: debug should be driven by a dedicated Debug field, not Logger presence. + // Currently always true when Logger is set (which is always in practice). debug := dm.Logger != nil dbg := func(format string, args ...interface{}) { @@ -249,7 +251,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque } dbg("estimated data: %s (%d bytes), free on dest: %s (%d bytes)", bytesHuman(totalBytes), totalBytes, bytesHuman(freeBytes), freeBytes) - dm.Logger.Printf("[INFO] Drive migration: %s (%s) → %s (%s), %d apps, ~%s data", + dm.Logger.Printf("[INFO] [storage] Drive migration: %s (%s) → %s (%s), %d apps, ~%s data", req.SourcePath, srcLabel, req.DestPath, dstLabel, len(appsToMigrate), bytesHuman(totalBytes)) tx := &migrationTx{logger: dm.Logger} @@ -261,7 +263,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque for _, app := range appsToMigrate { sendDetail("stopping", "Leállítás: "+app.DisplayName, app.Name, 5) if err := dm.StackProvider.StopStack(app.Name); err != nil { - dm.Logger.Printf("[ERROR] Drive migration: failed to stop %s: %v", app.Name, err) + dm.Logger.Printf("[ERROR] [storage] Drive migration: failed to stop %s: %v", app.Name, err) // Rollback: restart already stopped apps send("rolling_back", "Hiba a leállításnál, visszagörgetés...", 0) for _, name := range stoppedApps { @@ -274,7 +276,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque tx.add("Restart all stopped apps", func() error { for _, name := range stoppedApps { if err := dm.StackProvider.StartStack(name); err != nil { - dm.Logger.Printf("[ROLLBACK-WARN] Failed to restart %s: %v", name, err) + dm.Logger.Printf("[WARN] [storage] Rollback: failed to restart %s: %v", name, err) } } return nil @@ -365,7 +367,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque if _, err := os.Stat(destAppData); os.IsNotExist(err) { // appdata might not exist for all apps (SSD-only apps that share the drive) // Only warn, don't fail - dm.Logger.Printf("[WARN] Drive migration: %s not found on destination (may be SSD-only)", destAppData) + dm.Logger.Printf("[WARN] [storage] Drive migration: %s not found on destination (may be SSD-only)", destAppData) } } @@ -377,7 +379,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque for i, app := range appsToMigrate { // Guard: verify app still exists if !dm.StackProvider.StackExists(app.Name) { - dm.Logger.Printf("[WARN] Drive migration: app %s no longer exists, skipping config update", app.Name) + dm.Logger.Printf("[WARN] [storage] Drive migration: app %s no longer exists, skipping config update", app.Name) continue } @@ -386,7 +388,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque oldPath := dm.StackProvider.GetStackHDDPath(app.Name) if err := dm.StackProvider.UpdateStackHDDPath(app.Name, req.DestPath); err != nil { - dm.Logger.Printf("[ERROR] Drive migration: failed to update HDD_PATH for %s: %v", app.Name, err) + dm.Logger.Printf("[ERROR] [storage] Drive migration: failed to update HDD_PATH for %s: %v", app.Name, err) send("rolling_back", "Konfiguráció frissítése sikertelen, visszagörgetés...", 0) // Rollback config changes for _, name := range configuredApps { @@ -424,7 +426,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque // Mark source as decommissioned if err := dm.Sett.SetDecommissioned(req.SourcePath, req.DestPath); err != nil { - dm.Logger.Printf("[WARN] Drive migration: failed to mark source as decommissioned: %v", err) + dm.Logger.Printf("[WARN] [storage] Drive migration: failed to mark source as decommissioned: %v", err) } tx.add("Clear decommissioned on source", func() error { return dm.Sett.ClearDecommissioned(req.SourcePath) @@ -441,13 +443,13 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque // Apps that moved (source→dest) with Tier 2 pointing to dest: clear (no redundancy) appHDD := dm.StackProvider.GetStackHDDPath(name) if appHDD == req.DestPath && cfg.DestinationPath == req.DestPath { - dm.Logger.Printf("[INFO] Drive migration: clearing Tier 2 for %s (dest same as app drive)", name) + dm.Logger.Printf("[INFO] [storage] Drive migration: clearing Tier 2 for %s (dest same as app drive)", name) _ = dm.Sett.SetCrossDriveConfig(name, nil) continue } // Apps on OTHER drives with Tier 2 pointing to source: redirect to dest if cfg.DestinationPath == req.SourcePath { - dm.Logger.Printf("[INFO] Drive migration: redirecting Tier 2 for %s from %s to %s", name, req.SourcePath, req.DestPath) + dm.Logger.Printf("[INFO] [storage] Drive migration: redirecting Tier 2 for %s from %s to %s", name, req.SourcePath, req.DestPath) cfg.DestinationPath = req.DestPath _ = dm.Sett.SetCrossDriveConfig(name, cfg) } @@ -464,7 +466,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque sendDetail("starting", "Indítás: "+app.DisplayName, app.Name, pct) if err := dm.StackProvider.StartStack(app.Name); err != nil { - dm.Logger.Printf("[WARN] Drive migration: failed to start %s after migration: %v", app.Name, err) + dm.Logger.Printf("[WARN] [storage] Drive migration: failed to start %s after migration: %v", app.Name, err) // Non-fatal — log but continue } } @@ -476,7 +478,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque if dm.BackupTrigger != nil { if err := dm.BackupTrigger.TryRunDriveBackup(ctx, req.DestPath); err != nil { - dm.Logger.Printf("[WARN] Drive migration: post-migration backup failed: %v", err) + dm.Logger.Printf("[WARN] [storage] Drive migration: post-migration backup failed: %v", err) } } @@ -497,7 +499,7 @@ func (dm *DriveMigrator) MigrateDrive(ctx context.Context, req DriveMigrateReque } elapsed := time.Since(start) - dm.Logger.Printf("[INFO] Drive migration complete: %s → %s, %d apps, %s elapsed", + dm.Logger.Printf("[INFO] [storage] Drive migration complete: %s → %s, %d apps, %s elapsed", req.SourcePath, req.DestPath, len(appsToMigrate), elapsed.Round(time.Second)) // --- Step 10: Done --- diff --git a/controller/internal/sync/sync.go b/controller/internal/sync/sync.go index 97dd404..97ccf4d 100644 --- a/controller/internal/sync/sync.go +++ b/controller/internal/sync/sync.go @@ -77,22 +77,22 @@ func maskRepoURL(url string) string { // Start begins the periodic sync loop. Call Stop() to terminate. func (s *Syncer) Start() { if s.cfg.Git.RepoURL == "" { - s.logger.Println("[SYNC] Git repo URL is empty — sync disabled (manual mode)") + s.logger.Println("[WARN] [sync] Git repo URL is empty — sync disabled (manual mode)") return } interval, err := time.ParseDuration(s.cfg.Git.SyncInterval) if err != nil { - s.logger.Printf("[SYNC] Invalid sync_interval %q, defaulting to 15m", s.cfg.Git.SyncInterval) + s.logger.Printf("[WARN] [sync] Invalid sync_interval %q, defaulting to 15m", s.cfg.Git.SyncInterval) interval = 15 * time.Minute } - s.logger.Printf("[SYNC] Starting catalog sync (repo: %s, interval: %s)", s.cfg.Git.RepoURL, interval) + s.logger.Printf("[INFO] [sync] Starting catalog sync (repo: %s, interval: %s)", s.cfg.Git.RepoURL, interval) // Initial sync on startup go func() { result := s.doSync() - s.logger.Printf("[SYNC] Initial sync: %s", result.Message) + s.logger.Printf("[INFO] [sync] Initial sync: %s", result.Message) }() go func() { @@ -101,11 +101,11 @@ func (s *Syncer) Start() { for { select { case <-s.stopCh: - s.logger.Println("[SYNC] Sync loop stopped") + s.logger.Println("[INFO] [sync] Sync loop stopped") return case <-ticker.C: result := s.doSync() - s.logger.Printf("[SYNC] Periodic sync: %s", result.Message) + s.logger.Printf("[INFO] [sync] Periodic sync: %s", result.Message) } } }() @@ -207,14 +207,14 @@ func (s *Syncer) doSync() SyncResult { // Step 3: Trigger rescan if anything changed if len(newApps) > 0 || len(updated) > 0 { if err := s.rescanFn(); err != nil { - s.logger.Printf("[SYNC] Rescan after sync failed: %v", err) + s.logger.Printf("[WARN] [sync] Rescan after sync failed: %v", err) } } // Step 4: Inject missing deploy fields for updated stacks if len(updated) > 0 && s.postSyncHook != nil { if s.isDebug() { - s.logger.Printf("[DEBUG] [SYNC] Post-sync hook: triggering missing field injection for %d stack(s): %v", len(updated), updated) + s.logger.Printf("[DEBUG] [sync] Post-sync hook: triggering missing field injection for %d stack(s): %v", len(updated), updated) } s.postSyncHook(updated) } @@ -252,12 +252,12 @@ func (s *Syncer) gitCloneOrPull() error { gitDir := filepath.Join(s.cacheDir, ".git") if _, err := os.Stat(gitDir); os.IsNotExist(err) { // Clone - s.logger.Printf("[SYNC] Cloning %s → %s", s.cfg.Git.RepoURL, s.cacheDir) + s.logger.Printf("[INFO] [sync] Cloning %s → %s", s.cfg.Git.RepoURL, s.cacheDir) args := []string{"clone", "--depth", "1", "--branch", s.cfg.Git.Branch} repoURL := s.buildRepoURL() args = append(args, repoURL, s.cacheDir) if s.isDebug() { - s.logger.Printf("[DEBUG] [SYNC] git clone URL: %s, branch: %s, cacheDir: %s", maskRepoURL(repoURL), s.cfg.Git.Branch, s.cacheDir) + s.logger.Printf("[DEBUG] [sync] git clone URL: %s, branch: %s, cacheDir: %s", maskRepoURL(repoURL), s.cfg.Git.Branch, s.cacheDir) } return s.runGit(args...) } @@ -266,9 +266,9 @@ func (s *Syncer) gitCloneOrPull() error { s.removeGitLockFiles() // Pull - s.logger.Printf("[SYNC] Pulling latest from %s (branch: %s)", s.cfg.Git.RepoURL, s.cfg.Git.Branch) + s.logger.Printf("[INFO] [sync] Pulling latest from %s (branch: %s)", s.cfg.Git.RepoURL, s.cfg.Git.Branch) if s.isDebug() { - s.logger.Printf("[DEBUG] [SYNC] git fetch --depth 1 origin %s in %s", s.cfg.Git.Branch, s.cacheDir) + s.logger.Printf("[DEBUG] [sync] git fetch --depth 1 origin %s in %s", s.cfg.Git.Branch, s.cacheDir) } if err := s.runGitInDir(s.cacheDir, "fetch", "--depth", "1", "origin", s.cfg.Git.Branch); err != nil { return fmt.Errorf("git fetch: %w", err) @@ -288,9 +288,9 @@ func (s *Syncer) removeGitLockFiles() { for _, name := range lockFiles { lockPath := filepath.Join(gitDir, name) if _, err := os.Stat(lockPath); err == nil { - s.logger.Printf("[SYNC] Removing stale lock file: %s", lockPath) + s.logger.Printf("[WARN] [sync] Removing stale lock file: %s", lockPath) if err := os.Remove(lockPath); err != nil { - s.logger.Printf("[SYNC] Failed to remove lock file %s: %v", lockPath, err) + s.logger.Printf("[WARN] [sync] Failed to remove lock file %s: %v", lockPath, err) } } } @@ -328,7 +328,7 @@ func (s *Syncer) copyTemplates() (newApps []string, updated []string, err error) if _, err := os.Stat(dstDir); os.IsNotExist(err) { isNew = true if err := os.MkdirAll(dstDir, 0755); err != nil { - s.logger.Printf("[SYNC] Failed to create stack dir %s: %v", dstDir, err) + s.logger.Printf("[WARN] [sync] Failed to create stack dir %s: %v", dstDir, err) continue } } @@ -343,7 +343,7 @@ func (s *Syncer) copyTemplates() (newApps []string, updated []string, err error) if _, err := os.Stat(src); os.IsNotExist(err) { if s.isDebug() { - s.logger.Printf("[DEBUG] [SYNC] %s/%s: source not found, skipping", appName, filename) + s.logger.Printf("[DEBUG] [sync] %s/%s: source not found, skipping", appName, filename) } continue } @@ -355,12 +355,12 @@ func (s *Syncer) copyTemplates() (newApps []string, updated []string, err error) } if changed { anyChanged = true - s.logger.Printf("[SYNC] Updated %s/%s", appName, filename) + s.logger.Printf("[INFO] [sync] Updated %s/%s", appName, filename) if s.isDebug() { s.logFileHashes(appName, filename, src, dst) } } else if s.isDebug() { - s.logger.Printf("[DEBUG] [SYNC] %s/%s: hash match, skipped", appName, filename) + s.logger.Printf("[DEBUG] [sync] %s/%s: hash match, skipped", appName, filename) } } @@ -383,11 +383,11 @@ func (s *Syncer) logFileHashes(appName, filename, src, dst string) { srcHash := sha256.Sum256(srcData) dstData, err := os.ReadFile(dst) if err != nil { - s.logger.Printf("[DEBUG] [SYNC] %s/%s: src=%s, dst=new file", appName, filename, hex.EncodeToString(srcHash[:8])) + s.logger.Printf("[DEBUG] [sync] %s/%s: src=%s, dst=new file", appName, filename, hex.EncodeToString(srcHash[:8])) return } dstHash := sha256.Sum256(dstData) - s.logger.Printf("[DEBUG] [SYNC] %s/%s: src=%s, dst=%s (changed)", appName, filename, hex.EncodeToString(srcHash[:8]), hex.EncodeToString(dstHash[:8])) + s.logger.Printf("[DEBUG] [sync] %s/%s: src=%s, dst=%s (changed)", appName, filename, hex.EncodeToString(srcHash[:8]), hex.EncodeToString(dstHash[:8])) } // copyIfChanged copies src to dst only if the content differs. @@ -430,7 +430,7 @@ func (s *Syncer) runGitInDir(dir string, args ...string) error { cmd.Stdout = io.Discard cmd.Stderr = &stderr - s.logger.Printf("[SYNC] Running: git %s", maskRepoURL(strings.Join(args, " "))) + s.logger.Printf("[DEBUG] [sync] Running: git %s", maskRepoURL(strings.Join(args, " "))) if err := cmd.Run(); err != nil { return fmt.Errorf("git %s: %w\nstderr: %s", strings.Join(args, " "), err, stderr.String()) diff --git a/controller/internal/web/alerts.go b/controller/internal/web/alerts.go index 88fbc73..1ecef05 100644 --- a/controller/internal/web/alerts.go +++ b/controller/internal/web/alerts.go @@ -152,9 +152,6 @@ func (am *AlertManager) Refresh(report *monitor.HealthReport, cfg *config.Config am.mu.Lock() am.alerts = alerts am.mu.Unlock() - - am.logger.Printf("[DEBUG] AlertManager refreshed: %d alerts (%d error, %d warning)", - len(alerts), countLevel(alerts, "error"), countLevel(alerts, "warning")) } // GetAlerts returns a copy of the current alerts, optionally excluding specific IDs. diff --git a/controller/internal/web/auth.go b/controller/internal/web/auth.go index 41db64f..91d3503 100644 --- a/controller/internal/web/auth.go +++ b/controller/internal/web/auth.go @@ -140,7 +140,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) { } if attempt != nil && attempt.count >= loginMaxAttempts { s.loginAttemptMu.Unlock() - s.logger.Printf("[WARN] Login rate limited for %s (%d attempts)", ip, attempt.count) + s.logger.Printf("[WARN] [web] Login rate limited for %s (%d attempts)", ip, attempt.count) s.renderLogin(w, "Túl sok sikertelen próbálkozás, próbálja újra 1 perc múlva", "") return } @@ -148,7 +148,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) { effectiveHash := s.effectivePasswordHash() if err := bcrypt.CompareHashAndPassword([]byte(effectiveHash), []byte(password)); err != nil { - s.logger.Printf("[WARN] Failed login from %s", r.RemoteAddr) + s.logger.Printf("[WARN] [web] Failed login from %s", r.RemoteAddr) s.loginAttemptMu.Lock() if s.loginAttempts[ip] == nil { s.loginAttempts[ip] = &loginAttempt{} @@ -181,7 +181,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) { Secure: isSecure, }) - s.logger.Printf("[INFO] Login from %s", r.RemoteAddr) + s.logger.Printf("[INFO] [web] Login from %s", r.RemoteAddr) // Redirect to ?next= target if provided, otherwise to dashboard redirectTo := "/" @@ -260,9 +260,6 @@ func (s *Server) invalidateAllSessions() { s.sessions = make(map[string]*session) s.sessionsMu.Unlock() s.logger.Printf("[INFO] [web] All sessions invalidated (cleared %d)", count) - if s.isDebug() { - s.logger.Printf("[DEBUG] [web] invalidated all sessions (cleared %d)", count) - } } func (s *Server) cleanupSessions() { @@ -310,7 +307,7 @@ func (s *Server) renderLogin(w http.ResponseWriter, errorMsg, flashMsg string) { } w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := s.tmpl.ExecuteTemplate(w, "login", data); err != nil { - s.logger.Printf("[ERROR] Template error (login): %v", err) + s.logger.Printf("[ERROR] [web] Template error (login): %v", err) http.Error(w, "Internal error", http.StatusInternalServerError) } } diff --git a/controller/internal/web/handler_export.go b/controller/internal/web/handler_export.go index 9f8d9f4..1a24d0a 100644 --- a/controller/internal/web/handler_export.go +++ b/controller/internal/web/handler_export.go @@ -95,14 +95,14 @@ func (s *Server) apiExportEstimate(w http.ResponseWriter, r *http.Request) { stackName := r.URL.Query().Get("stack") drive := r.URL.Query().Get("drive") - s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate: stack=%q drive=%q", stackName, drive) + s.logger.Printf("[DEBUG] [web] apiExportEstimate: stack=%q drive=%q", stackName, drive) if stackName == "" || drive == "" { jsonError(w, "Missing stack or drive parameter", http.StatusBadRequest) return } if !s.isValidDrivePath(drive) { - s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate: invalid drive path %q", drive) + s.logger.Printf("[DEBUG] [web] apiExportEstimate: invalid drive path %q", drive) jsonError(w, "Invalid drive path", http.StatusBadRequest) return } @@ -110,12 +110,12 @@ func (s *Server) apiExportEstimate(w http.ResponseWriter, r *http.Request) { est, err := s.appExporter.EstimateExport(stackName, drive) if err != nil { s.logger.Printf("[ERROR] [web] Export estimate failed for %s: %v", stackName, err) - s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate error: %v", err) + s.logger.Printf("[DEBUG] [web] apiExportEstimate error: %v", err) jsonError(w, err.Error(), http.StatusInternalServerError) return } - s.logger.Printf("[DEBUG] [handler_export] apiExportEstimate: total=%s free=%s fits=%v", + s.logger.Printf("[DEBUG] [web] apiExportEstimate: total=%s free=%s fits=%v", est.TotalSizeHuman, est.DestFreeHuman, est.FitsOnDest) jsonResponse(w, map[string]interface{}{ "ok": true, @@ -137,12 +137,12 @@ func (s *Server) apiExportStart(w http.ResponseWriter, r *http.Request) { StopApp bool `json:"stop_app"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - s.logger.Printf("[DEBUG] [handler_export] apiExportStart: invalid body: %v", err) + s.logger.Printf("[DEBUG] [web] apiExportStart: invalid body: %v", err) jsonError(w, "Invalid request body", http.StatusBadRequest) return } - s.logger.Printf("[DEBUG] [handler_export] apiExportStart: stack=%q drive=%q encrypted=%v stopApp=%v", + s.logger.Printf("[DEBUG] [web] apiExportStart: stack=%q drive=%q encrypted=%v stopApp=%v", req.StackName, req.DestDrive, req.Password != "", req.StopApp) if req.StackName == "" || req.DestDrive == "" { @@ -151,7 +151,7 @@ func (s *Server) apiExportStart(w http.ResponseWriter, r *http.Request) { } if !s.isValidDrivePath(req.DestDrive) { - s.logger.Printf("[DEBUG] [handler_export] apiExportStart: invalid drive path %q", req.DestDrive) + s.logger.Printf("[DEBUG] [web] apiExportStart: invalid drive path %q", req.DestDrive) jsonError(w, "Invalid drive path", http.StatusBadRequest) return } @@ -164,12 +164,12 @@ func (s *Server) apiExportStart(w http.ResponseWriter, r *http.Request) { }) if err != nil { s.logger.Printf("[ERROR] [web] Export start failed for %s: %v", req.StackName, err) - s.logger.Printf("[DEBUG] [handler_export] apiExportStart error: %v", err) + s.logger.Printf("[DEBUG] [web] apiExportStart error: %v", err) jsonError(w, err.Error(), http.StatusConflict) return } - s.logger.Printf("[INFO] Export started for %s to %s", req.StackName, req.DestDrive) + s.logger.Printf("[INFO] [web] Export started for %s to %s", req.StackName, req.DestDrive) jsonResponse(w, map[string]interface{}{"ok": true}) } @@ -230,23 +230,23 @@ func (s *Server) apiExportManifest(w http.ResponseWriter, r *http.Request) { return } - s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: path=%q hasPassword=%v", req.Path, req.Password != "") + s.logger.Printf("[DEBUG] [web] apiExportManifest: path=%q hasPassword=%v", req.Path, req.Password != "") // Security: validate path is within a registered exports directory if !s.isValidExportPath(req.Path) { - s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: invalid path %q", req.Path) + s.logger.Printf("[DEBUG] [web] apiExportManifest: invalid path %q", req.Path) jsonError(w, "Invalid bundle path", http.StatusBadRequest) return } encrypted, _ := appexport.IsEncryptedFAB(req.Path) - s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: encrypted=%v", encrypted) + s.logger.Printf("[DEBUG] [web] apiExportManifest: encrypted=%v", encrypted) var manifest *appexport.Manifest var err error if encrypted { if req.Password == "" { - s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: encrypted, needs password") + s.logger.Printf("[DEBUG] [web] apiExportManifest: encrypted, needs password") jsonResponse(w, map[string]interface{}{ "ok": true, "encrypted": true, @@ -260,12 +260,12 @@ func (s *Server) apiExportManifest(w http.ResponseWriter, r *http.Request) { } if err != nil { - s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: error: %v", err) + s.logger.Printf("[DEBUG] [web] apiExportManifest: error: %v", err) jsonError(w, err.Error(), http.StatusBadRequest) return } - s.logger.Printf("[DEBUG] [handler_export] apiExportManifest: app=%s display=%s size=%d", + s.logger.Printf("[DEBUG] [web] apiExportManifest: app=%s display=%s size=%d", manifest.AppName, manifest.DisplayName, manifest.TotalSizeBytes) jsonResponse(w, map[string]interface{}{ "ok": true, @@ -285,12 +285,12 @@ func (s *Server) apiImportStart(w http.ResponseWriter, r *http.Request) { Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - s.logger.Printf("[DEBUG] [handler_export] apiImportStart: invalid body: %v", err) + s.logger.Printf("[DEBUG] [web] apiImportStart: invalid body: %v", err) jsonError(w, "Invalid request body", http.StatusBadRequest) return } - s.logger.Printf("[DEBUG] [handler_export] apiImportStart: path=%q hasPassword=%v", req.Path, req.Password != "") + s.logger.Printf("[DEBUG] [web] apiImportStart: path=%q hasPassword=%v", req.Path, req.Password != "") if req.Path == "" { jsonError(w, "Missing path", http.StatusBadRequest) @@ -298,7 +298,7 @@ func (s *Server) apiImportStart(w http.ResponseWriter, r *http.Request) { } if !s.isValidExportPath(req.Path) { - s.logger.Printf("[DEBUG] [handler_export] apiImportStart: invalid path %q", req.Path) + s.logger.Printf("[DEBUG] [web] apiImportStart: invalid path %q", req.Path) jsonError(w, "Invalid bundle path", http.StatusBadRequest) return } @@ -309,12 +309,12 @@ func (s *Server) apiImportStart(w http.ResponseWriter, r *http.Request) { }) if err != nil { s.logger.Printf("[ERROR] [web] Import start failed for %s: %v", req.Path, err) - s.logger.Printf("[DEBUG] [handler_export] apiImportStart error: %v", err) + s.logger.Printf("[DEBUG] [web] apiImportStart error: %v", err) jsonError(w, err.Error(), http.StatusConflict) return } - s.logger.Printf("[INFO] Import started from %s", req.Path) + s.logger.Printf("[INFO] [web] Import started from %s", req.Path) jsonResponse(w, map[string]interface{}{"ok": true}) } diff --git a/controller/internal/web/handler_restore.go b/controller/internal/web/handler_restore.go index 53ad0be..91088ca 100644 --- a/controller/internal/web/handler_restore.go +++ b/controller/internal/web/handler_restore.go @@ -111,7 +111,7 @@ func (s *Server) apiRestoreSkip(w http.ResponseWriter, r *http.Request) { return } - s.logger.Println("[INFO] User skipped DR restore — entering normal mode") + s.logger.Println("[INFO] [web] User skipped DR restore — entering normal mode") s.clearRestoreMode() jsonResponse(w, map[string]interface{}{ @@ -122,14 +122,14 @@ func (s *Server) apiRestoreSkip(w http.ResponseWriter, r *http.Request) { // executeAllRestores runs the restore for each pending app sequentially. func (s *Server) executeAllRestores() { - s.logger.Println("[INFO] Starting DR restore for all apps") + s.logger.Println("[INFO] [web] Starting DR restore for all apps") restoreStart := time.Now() s.restoreMu.RLock() plan := s.restorePlan s.restoreMu.RUnlock() if plan == nil { - s.logger.Println("[WARN] Restore plan cleared before execution could start") + s.logger.Println("[WARN] [web] Restore plan cleared before execution could start") return } @@ -155,7 +155,7 @@ func (s *Server) executeAllRestores() { } plan.UpdateApp(app.Name, "restoring", "") - s.logger.Printf("[INFO] Restoring app %s (%s)", app.Name, app.DisplayName) + s.logger.Printf("[INFO] [web] Restoring app %s (%s)", app.Name, app.DisplayName) appStart := time.Now() ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) @@ -164,14 +164,14 @@ func (s *Server) executeAllRestores() { if err != nil { plan.UpdateApp(app.Name, "failed", err.Error()) - s.logger.Printf("[ERROR] Restore failed for %s: %v", app.Name, err) + s.logger.Printf("[ERROR] [web] Restore failed for %s: %v", app.Name, err) if s.isDebug() { s.logger.Printf("[DEBUG] [web] executeAllRestores: app=%s failed after %s", app.Name, time.Since(appStart)) } failCount++ } else { plan.UpdateApp(app.Name, "done", "") - s.logger.Printf("[INFO] Restore completed for %s", app.Name) + s.logger.Printf("[INFO] [web] Restore completed for %s", app.Name) if s.isDebug() { s.logger.Printf("[DEBUG] [web] executeAllRestores: app=%s completed in %s", app.Name, time.Since(appStart)) } @@ -180,7 +180,7 @@ func (s *Server) executeAllRestores() { } plan.SetStatus("done") - s.logger.Println("[INFO] All app restores completed") + s.logger.Println("[INFO] [web] All app restores completed") if s.isDebug() { s.logger.Printf("[DEBUG] [web] executeAllRestores: total=%d success=%d fail=%d elapsed=%s", pendingCount, successCount, failCount, time.Since(restoreStart)) } @@ -193,7 +193,7 @@ func (s *Server) executeAllRestores() { // Re-scan stacks so dashboard picks up restored apps if s.stackMgr != nil { if err := s.stackMgr.ScanStacks(); err != nil { - s.logger.Printf("[WARN] Post-restore stack scan failed: %v", err) + s.logger.Printf("[WARN] [web] Post-restore stack scan failed: %v", err) } } } diff --git a/controller/internal/web/handlers.go b/controller/internal/web/handlers.go index 2d443e9..1aa1c05 100644 --- a/controller/internal/web/handlers.go +++ b/controller/internal/web/handlers.go @@ -992,7 +992,7 @@ func (s *Server) settingsCrossBackupHandler(w http.ResponseWriter, r *http.Reque } } if !validDest { - s.logger.Printf("[WARN] Cross-drive backup: rejected invalid dest path %q for %s", destPath, name) + s.logger.Printf("[WARN] [web] Cross-drive backup: rejected invalid dest path %q for %s", destPath, name) http.Redirect(w, r, "/stacks/"+name+"/deploy?flash_error="+url.QueryEscape("Érvénytelen célútvonal: "+destPath), http.StatusFound) return } @@ -1016,12 +1016,12 @@ func (s *Server) settingsCrossBackupHandler(w http.ResponseWriter, r *http.Reque } if err := s.settings.SetCrossDriveConfig(name, cfg); err != nil { - s.logger.Printf("[ERROR] Failed to save cross-drive config for %s: %v", name, err) + s.logger.Printf("[ERROR] [web] Failed to save cross-drive config for %s: %v", name, err) http.Redirect(w, r, "/stacks/"+name+"/deploy?flash_error=Hiba+a+ment%C3%A9si+be%C3%A1ll%C3%ADt%C3%A1s+ment%C3%A9sakor", http.StatusFound) return } - s.logger.Printf("[INFO] Cross-drive backup config saved for %s: dest=%s schedule=%s enabled=%v", + s.logger.Printf("[INFO] [web] Cross-drive backup config saved for %s: dest=%s schedule=%s enabled=%v", name, destPath, schedule, enabled) http.Redirect(w, r, "/stacks/"+name+"/deploy?flash=Ment%C3%A9si+be%C3%A1ll%C3%ADt%C3%A1s+mentve.", http.StatusFound) @@ -1047,11 +1047,11 @@ func (s *Server) backupRestoreHandler(w http.ResponseWriter, r *http.Request) { return } - s.logger.Printf("[WARN] Restore requested: stack=%s, snapshot=%s from %s", stackName, snapshotID, r.RemoteAddr) + s.logger.Printf("[WARN] [web] Restore requested: stack=%s, snapshot=%s from %s", stackName, snapshotID, r.RemoteAddr) start := time.Now() if err := s.backupMgr.RestoreApp(stackName, snapshotID); err != nil { - s.logger.Printf("[ERROR] Restore failed: %v", err) + s.logger.Printf("[ERROR] [web] Restore failed: %v", err) if s.isDebug() { s.logger.Printf("[DEBUG] [web] backupRestoreHandler: stack=%s failed after %s", stackName, time.Since(start)) } @@ -1223,7 +1223,7 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request) // Generate bcrypt hash hash, err := bcrypt.GenerateFromPassword([]byte(newPassword), 10) if err != nil { - s.logger.Printf("[ERROR] Failed to hash new password: %v", err) + s.logger.Printf("[ERROR] [web] Failed to hash new password: %v", err) data["PasswordError"] = "Belső hiba a jelszó mentésekor" s.executeTemplate(w, r, "settings", data) return @@ -1231,13 +1231,13 @@ func (s *Server) settingsPasswordHandler(w http.ResponseWriter, r *http.Request) // Save to settings.json if err := s.settings.SetPasswordHash(string(hash)); err != nil { - s.logger.Printf("[ERROR] Failed to save password to settings.json: %v", err) + s.logger.Printf("[ERROR] [web] Failed to save password to settings.json: %v", err) data["PasswordError"] = "Belső hiba a jelszó mentésekor" s.executeTemplate(w, r, "settings", data) return } - s.logger.Printf("[INFO] Password changed via settings page from %s", r.RemoteAddr) + s.logger.Printf("[INFO] [web] Password changed via settings page from %s", r.RemoteAddr) // Invalidate all sessions (force re-login) s.invalidateAllSessions() @@ -1297,20 +1297,20 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req } if err := s.settings.SetNotificationPrefs(prefs); err != nil { - s.logger.Printf("[ERROR] Failed to save notification prefs: %v", err) + s.logger.Printf("[ERROR] [web] Failed to save notification prefs: %v", err) data := s.settingsData() data["NotificationError"] = "Hiba a beállítások mentésekor" s.executeTemplate(w, r, "settings", data) return } - s.logger.Printf("[INFO] Notification preferences updated: email=%s, events=%v", email, enabledEvents) + s.logger.Printf("[INFO] [web] Notification preferences updated: email=%s, events=%v", email, enabledEvents) // Sync preferences to hub data := s.settingsData() if s.notifier != nil && s.notifier.IsEnabled() { if err := s.notifier.SyncPreferences(email, enabledEvents, cooldownHours); err != nil { - s.logger.Printf("[WARN] Failed to sync preferences to hub: %v", err) + s.logger.Printf("[WARN] [web] Failed to sync preferences to hub: %v", err) data["NotificationSuccess"] = fmt.Sprintf("Értesítési beállítások mentve (helyi). A központi szinkronizálás sikertelen: %v", err) } else { data["NotificationSuccess"] = "Értesítési beállítások mentve." @@ -1332,7 +1332,7 @@ func (s *Server) settingsNotificationsTestHandler(w http.ResponseWriter, r *http err := s.notifier.SendTest() if err != nil { - s.logger.Printf("[ERROR] Test notification failed: %v", err) + s.logger.Printf("[ERROR] [web] Test notification failed: %v", err) data["NotificationError"] = fmt.Sprintf("Teszt email küldése sikertelen: %v", err) s.executeTemplate(w, r, "settings", data) return @@ -1486,7 +1486,7 @@ func (s *Server) settingsStorageAddHandler(w http.ResponseWriter, r *http.Reques // 5. Soft warning if not under /mnt/ if !strings.HasPrefix(path, "/mnt/") { - s.logger.Printf("[WARN] Storage path %s is not under /mnt/ — unusual but allowed", path) + s.logger.Printf("[WARN] [web] Storage path %s is not under /mnt/ — unusual but allowed", path) } sp := settings.StoragePath{ @@ -1498,13 +1498,13 @@ func (s *Server) settingsStorageAddHandler(w http.ResponseWriter, r *http.Reques } if err := s.settings.AddStoragePath(sp); err != nil { - s.logger.Printf("[ERROR] Failed to add storage path: %v", err) + s.logger.Printf("[ERROR] [web] Failed to add storage path: %v", err) data["StorageError"] = "Hiba a mentés során." s.executeTemplate(w, r, "settings", data) return } - s.logger.Printf("[INFO] Storage path added: %s (%s)", path, label) + s.logger.Printf("[INFO] [web] Storage path added: %s (%s)", path, label) go s.SyncFileBrowserMounts() http.Redirect(w, r, "/settings?storage_msg=success&storage_detail="+url.QueryEscape("Adattároló sikeresen hozzáadva: "+path), http.StatusFound) } @@ -1549,7 +1549,7 @@ func (s *Server) settingsStorageRemoveHandler(w http.ResponseWriter, r *http.Req return } - s.logger.Printf("[INFO] Storage path removed: %s", path) + s.logger.Printf("[INFO] [web] Storage path removed: %s", path) // Sync FileBrowser mounts after storage path removal go s.SyncFileBrowserMounts() http.Redirect(w, r, "/settings?storage_msg=success&storage_detail="+url.QueryEscape("Adattároló eltávolítva: "+path), http.StatusFound) @@ -1564,7 +1564,7 @@ func (s *Server) settingsStorageDefaultHandler(w http.ResponseWriter, r *http.Re } if err := s.settings.SetDefaultStoragePath(path); err != nil { - s.logger.Printf("[ERROR] Failed to set default storage path: %v", err) + s.logger.Printf("[ERROR] [web] Failed to set default storage path: %v", err) http.Redirect(w, r, "/settings", http.StatusFound) return } @@ -1582,7 +1582,7 @@ func (s *Server) settingsStorageSchedulableHandler(w http.ResponseWriter, r *htt } if err := s.settings.SetSchedulable(path, schedulable); err != nil { - s.logger.Printf("[ERROR] Failed to update schedulable: %v", err) + s.logger.Printf("[ERROR] [web] Failed to update schedulable: %v", err) http.Redirect(w, r, "/settings", http.StatusFound) return } @@ -1607,14 +1607,14 @@ func (s *Server) settingsStorageLabelHandler(w http.ResponseWriter, r *http.Requ } if err := s.settings.SetStorageLabel(path, label); err != nil { - s.logger.Printf("[ERROR] Failed to set storage label: %v", err) + s.logger.Printf("[ERROR] [web] Failed to set storage label: %v", err) data := s.settingsData() data["StorageError"] = "Hiba a megnevezés mentésekor." s.executeTemplate(w, r, "settings", data) return } - s.logger.Printf("[INFO] Storage label updated: %s → %q", path, label) + s.logger.Printf("[INFO] [web] Storage label updated: %s → %q", path, label) http.Redirect(w, r, "/settings?storage_msg=success&storage_detail="+url.QueryEscape("Megnevezés módosítva: "+label), http.StatusFound) } @@ -1641,7 +1641,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) { // Check if FileBrowser stack exists if _, err := os.Stat(composePath); os.IsNotExist(err) { - s.logger.Printf("[WARN] FileBrowser stack not found at %s — skipping mount sync", composePath) + s.logger.Printf("[WARN] [web] FileBrowser stack not found at %s — skipping mount sync", composePath) return } @@ -1651,7 +1651,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) { // Use domain from controller config domain := s.cfg.Customer.Domain if domain == "" { - s.logger.Printf("[WARN] Cannot sync FileBrowser mounts — customer domain not configured") + s.logger.Printf("[WARN] [web] Cannot sync FileBrowser mounts — customer domain not configured") return } @@ -1675,7 +1675,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) { } if err := os.WriteFile(configPath, []byte(fbConfig), 0644); err != nil { - s.logger.Printf("[ERROR] Failed to write FileBrowser config: %v", err) + s.logger.Printf("[ERROR] [web] Failed to write FileBrowser config: %v", err) return } @@ -1687,7 +1687,7 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) { // Generate and write compose (includes config.yaml mount) compose := generateFileBrowserCompose(domain, storageMounts) if err := os.WriteFile(composePath, []byte(compose), 0644); err != nil { - s.logger.Printf("[ERROR] Failed to write FileBrowser compose: %v", err) + s.logger.Printf("[ERROR] [web] Failed to write FileBrowser compose: %v", err) return } @@ -1695,13 +1695,13 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) { // nuke the data volume so FileBrowser re-reads config.yaml from scratch. // Normal operations skip this to preserve user accounts, permissions, and share links. if sourcesChanged && resetDBOnChange { - s.logger.Printf("[INFO] FileBrowser sources changed — resetting database (restore mode)") + s.logger.Printf("[INFO] [web] FileBrowser sources changed — resetting database (restore mode)") resetCtx, resetCancel := context.WithTimeout(context.Background(), 30*time.Second) defer resetCancel() stop := exec.CommandContext(resetCtx, "docker", "compose", "down", "-v") stop.Dir = stackDir if out, err := stop.CombinedOutput(); err != nil { - s.logger.Printf("[WARN] FileBrowser down -v: %s — %v", strings.TrimSpace(string(out)), err) + s.logger.Printf("[WARN] [web] FileBrowser down -v: %s — %v", strings.TrimSpace(string(out)), err) } } @@ -1711,9 +1711,9 @@ func (s *Server) syncFileBrowserMounts(resetDBOnChange bool) { cmd := exec.CommandContext(ctx, "docker", "compose", "up", "-d", "--force-recreate", "--remove-orphans") cmd.Dir = stackDir if out, err := cmd.CombinedOutput(); err != nil { - s.logger.Printf("[ERROR] Failed to recreate FileBrowser: %s — %v", string(out), err) + s.logger.Printf("[ERROR] [web] Failed to recreate FileBrowser: %s — %v", string(out), err) } else { - s.logger.Printf("[INFO] FileBrowser mounts synced — %d storage path(s), config updated", len(paths)) + s.logger.Printf("[INFO] [web] FileBrowser mounts synced — %d storage path(s), config updated", len(paths)) } } diff --git a/controller/internal/web/server.go b/controller/internal/web/server.go index cafd139..aeb046e 100644 --- a/controller/internal/web/server.go +++ b/controller/internal/web/server.go @@ -118,11 +118,11 @@ func NewServer(cfg *config.Config, stackMgr *stacks.Manager, cpuCollector *syste // Log auth source on startup if sett != nil && sett.GetPasswordHash() != "" { - logger.Printf("[INFO] Auth: using password from settings.json") + logger.Printf("[INFO] [web] Auth: using password from settings.json") } else if cfg.Web.PasswordHash != "" { - logger.Printf("[INFO] Auth: using password from controller.yaml") + logger.Printf("[INFO] [web] Auth: using password from controller.yaml") } else { - logger.Printf("[INFO] Auth: no password configured — dashboard is open") + logger.Printf("[INFO] [web] Auth: no password configured — dashboard is open") } // Sync FileBrowser config on startup to ensure mounts and sources are current. @@ -416,7 +416,7 @@ func (s *Server) serveCatchAll(w http.ResponseWriter, r *http.Request, host stri w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusNotFound) if err := s.tmpl.ExecuteTemplate(w, "catchall", data); err != nil { - s.logger.Printf("[ERROR] Catch-all template error: %v", err) + s.logger.Printf("[ERROR] [web] Catch-all template error: %v", err) http.Error(w, "Internal error", http.StatusInternalServerError) } } @@ -459,7 +459,7 @@ func (s *Server) primaryHDDPath() string { func (s *Server) render(w http.ResponseWriter, name string, data interface{}) { var buf bytes.Buffer if err := s.tmpl.ExecuteTemplate(&buf, name, data); err != nil { - s.logger.Printf("[ERROR] Template error (%s): %v", name, err) + s.logger.Printf("[ERROR] [web] Template error (%s): %v", name, err) http.Error(w, "Internal error", http.StatusInternalServerError) return } @@ -477,7 +477,7 @@ func (s *Server) executeTemplate(w http.ResponseWriter, r *http.Request, name st data["CSRFToken"] = s.csrfToken(r) var buf bytes.Buffer if err := s.tmpl.ExecuteTemplate(&buf, name, data); err != nil { - s.logger.Printf("[ERROR] Template error (%s): %v", name, err) + s.logger.Printf("[ERROR] [web] Template error (%s): %v", name, err) http.Error(w, "Internal error", http.StatusInternalServerError) return } diff --git a/controller/internal/web/storage_handlers.go b/controller/internal/web/storage_handlers.go index 0ee9624..b8232ef 100644 --- a/controller/internal/web/storage_handlers.go +++ b/controller/internal/web/storage_handlers.go @@ -206,7 +206,7 @@ func (s *Server) storageScanAPIHandler(w http.ResponseWriter, r *http.Request) { } result, err := storage.ScanDisks(s.logger, s.cfg.Logging.Level == "debug") if err != nil { - s.logger.Printf("[ERROR] storageScan: %v", err) + s.logger.Printf("[ERROR] [web] storageScan: %v", err) jsonError(w, "Meghajtók keresése sikertelen: "+err.Error(), http.StatusInternalServerError) return } @@ -256,7 +256,7 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) { return } - s.logger.Printf("[INFO] Storage init started: device=%s mountName=%s by %s", req.DevicePath, req.MountName, r.RemoteAddr) + s.logger.Printf("[INFO] [web] Storage init started: device=%s mountName=%s by %s", req.DevicePath, req.MountName, r.RemoteAddr) fmtReq := storage.FormatRequest{ DevicePath: req.DevicePath, @@ -274,7 +274,7 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) { if scanResult, scanErr := storage.ScanDisks(s.logger, s.cfg.Logging.Level == "debug"); scanErr == nil { for _, disk := range scanResult.AvailableDisks { if disk.Path == req.DevicePath && len(disk.Partitions) == 1 && disk.Partitions[0].FSType == "" { - s.logger.Printf("[INFO] Disk %s has 1 empty partition (%s) — skipping repartition", + s.logger.Printf("[INFO] [web] Disk %s has 1 empty partition (%s) — skipping repartition", req.DevicePath, disk.Partitions[0].Path) fmtReq.DevicePath = disk.Partitions[0].Path fmtReq.CreatePartition = false @@ -297,7 +297,7 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) { close(progressCh) if err != nil { - s.logger.Printf("[ERROR] Storage init failed: %v", err) + s.logger.Printf("[ERROR] [web] Storage init failed: %v", err) return } @@ -314,9 +314,9 @@ func (s *Server) storageInitAPIHandler(w http.ResponseWriter, r *http.Request) { AddedAt: time.Now().UTC().Format(time.RFC3339), } if err := s.settings.AddStoragePath(sp); err != nil { - s.logger.Printf("[WARN] Failed to register storage path after init: %v", err) + s.logger.Printf("[WARN] [web] Failed to register storage path after init: %v", err) } else { - s.logger.Printf("[INFO] Storage path registered: %s (%s)", mountPath, label) + s.logger.Printf("[INFO] [web] Storage path registered: %s (%s)", mountPath, label) // Sync FileBrowser mounts with new storage path s.SyncFileBrowserMounts() } @@ -505,7 +505,7 @@ func (s *Server) storageMigrateAPIHandler(w http.ResponseWriter, r *http.Request return } - s.logger.Printf("[INFO] Migration started: stack=%s from=%s to=%s by %s", + s.logger.Printf("[INFO] [web] Migration started: stack=%s from=%s to=%s by %s", req.StackName, currentHDDPath, req.TargetPath, r.RemoteAddr) migrReq := storage.MigrateRequest{ @@ -551,9 +551,9 @@ func (s *Server) storageMigrateAPIHandler(w http.ResponseWriter, r *http.Request }() if err := orch.RunEnhancedMigration(migrReq, stopFn, startFn, updateFn, opts, progressCh); err != nil { - s.logger.Printf("[ERROR] Migration failed: stack=%s: %v", req.StackName, err) + s.logger.Printf("[ERROR] [web] Migration failed: stack=%s: %v", req.StackName, err) } else { - s.logger.Printf("[INFO] Migration complete: stack=%s → %s", req.StackName, req.TargetPath) + s.logger.Printf("[INFO] [web] Migration complete: stack=%s → %s", req.StackName, req.TargetPath) // Sync FileBrowser mounts (storage paths may now have new app data) go s.SyncFileBrowserMounts() } @@ -879,7 +879,7 @@ func (s *Server) staleDataCleanupHandler(w http.ResponseWriter, r *http.Request) // Safety: never delete protected top-level dirs if protected != nil && protected[cleanPath] { - s.logger.Printf("[WARN] Refusing to delete protected HDD path: %s", cleanPath) + s.logger.Printf("[WARN] [web] Refusing to delete protected HDD path: %s", cleanPath) errors = append(errors, fmt.Sprintf("Védett útvonal, nem törölhető: %s", cleanPath)) continue } @@ -893,10 +893,10 @@ func (s *Server) staleDataCleanupHandler(w http.ResponseWriter, r *http.Request) size := dirSizeInt64(cleanPath) if err := os.RemoveAll(cleanPath); err != nil { - s.logger.Printf("[ERROR] Failed to remove stale data %s: %v", cleanPath, err) + s.logger.Printf("[ERROR] [web] Failed to remove stale data %s: %v", cleanPath, err) errors = append(errors, fmt.Sprintf("Törlés sikertelen: %s — %v", cleanPath, err)) } else { - s.logger.Printf("[INFO] Removed stale data: %s (%s) for stack %s", cleanPath, dirSizeBytesHuman(size), req.StackName) + s.logger.Printf("[INFO] [web] Removed stale data: %s (%s) for stack %s", cleanPath, dirSizeBytesHuman(size), req.StackName) deleted = append(deleted, cleanPath) totalFreed += size } @@ -952,7 +952,7 @@ func (s *Server) storageAttachMountRawHandler(w http.ResponseWriter, r *http.Req rawPath, err := storage.MountRaw(req.DevicePath) if err != nil { s.diskJobMu.Unlock() - s.logger.Printf("[ERROR] storageAttachMountRaw: %v", err) + s.logger.Printf("[ERROR] [web] storageAttachMountRaw: %v", err) jsonError(w, err.Error(), http.StatusInternalServerError) return } @@ -960,7 +960,7 @@ func (s *Server) storageAttachMountRawHandler(w http.ResponseWriter, r *http.Req s.activeRawMount = rawPath s.diskJobMu.Unlock() - s.logger.Printf("[INFO] Raw mount for attach: %s → %s", req.DevicePath, rawPath) + s.logger.Printf("[INFO] [web] Raw mount for attach: %s → %s", req.DevicePath, rawPath) jsonResponse(w, map[string]interface{}{ "ok": true, @@ -1026,7 +1026,7 @@ func (s *Server) storageAttachMkdirHandler(w http.ResponseWriter, r *http.Reques return } - s.logger.Printf("[INFO] Created directory for attach: %s", createdPath) + s.logger.Printf("[INFO] [web] Created directory for attach: %s", createdPath) jsonResponse(w, map[string]interface{}{ "ok": true, @@ -1064,7 +1064,7 @@ func (s *Server) storageAttachAPIHandler(w http.ResponseWriter, r *http.Request) return } - s.logger.Printf("[INFO] Storage attach started: device=%s mountName=%s subPath=%s by %s", + s.logger.Printf("[INFO] [web] Storage attach started: device=%s mountName=%s subPath=%s by %s", req.DevicePath, req.MountName, req.SubPath, r.RemoteAddr) attachReq := storage.AttachRequest{ @@ -1089,7 +1089,7 @@ func (s *Server) storageAttachAPIHandler(w http.ResponseWriter, r *http.Request) close(progressCh) if err != nil { - s.logger.Printf("[ERROR] Storage attach failed: %v", err) + s.logger.Printf("[ERROR] [web] Storage attach failed: %v", err) return } @@ -1111,9 +1111,9 @@ func (s *Server) storageAttachAPIHandler(w http.ResponseWriter, r *http.Request) AddedAt: time.Now().UTC().Format(time.RFC3339), } if err := s.settings.AddStoragePath(sp); err != nil { - s.logger.Printf("[WARN] Failed to register storage path after attach: %v", err) + s.logger.Printf("[WARN] [web] Failed to register storage path after attach: %v", err) } else { - s.logger.Printf("[INFO] Storage path registered: %s (%s)", mountPath, label) + s.logger.Printf("[INFO] [web] Storage path registered: %s (%s)", mountPath, label) s.SyncFileBrowserMounts() } }() @@ -1169,9 +1169,9 @@ func (s *Server) storageAttachCancelHandler(w http.ResponseWriter, r *http.Reque if rawMount != "" { if err := storage.CleanupRawMount(rawMount); err != nil { - s.logger.Printf("[WARN] Failed to cleanup raw mount %s: %v", rawMount, err) + s.logger.Printf("[WARN] [web] Failed to cleanup raw mount %s: %v", rawMount, err) } else { - s.logger.Printf("[INFO] Cleaned up raw mount: %s", rawMount) + s.logger.Printf("[INFO] [web] Cleaned up raw mount: %s", rawMount) } } @@ -1217,7 +1217,7 @@ func (s *Server) storageDisconnectHandler(w http.ResponseWriter, r *http.Request stoppedStacks, err := s.storageWatchdog.SafeDisconnect(r.Context(), req.Path) if err != nil { - s.logger.Printf("[ERROR] Safe disconnect %s: %v", req.Path, err) + s.logger.Printf("[ERROR] [web] Safe disconnect %s: %v", req.Path, err) jsonError(w, fmt.Sprintf("Leválasztás sikertelen: %v", err), http.StatusInternalServerError) return } @@ -1260,7 +1260,7 @@ func (s *Server) storageReconnectHandler(w http.ResponseWriter, r *http.Request) stoppedStacks, err := s.storageWatchdog.Reconnect(r.Context(), req.Path) if err != nil { - s.logger.Printf("[ERROR] Reconnect %s: %v", req.Path, err) + s.logger.Printf("[ERROR] [web] Reconnect %s: %v", req.Path, err) jsonError(w, fmt.Sprintf("Csatlakoztatás sikertelen: %v", err), http.StatusInternalServerError) return } @@ -1482,7 +1482,7 @@ func (s *Server) driveMigrateAPIHandler(w http.ResponseWriter, r *http.Request) return } - s.logger.Printf("[INFO] Drive migration started: %s → %s by %s", req.SourcePath, req.DestPath, r.RemoteAddr) + s.logger.Printf("[INFO] [web] Drive migration started: %s → %s by %s", req.SourcePath, req.DestPath, r.RemoteAddr) go func() { ctx := context.Background() @@ -1498,9 +1498,9 @@ func (s *Server) driveMigrateAPIHandler(w http.ResponseWriter, r *http.Request) DestPath: req.DestPath, } if err := s.driveMigrator.MigrateDrive(ctx, migrReq, progressCh); err != nil { - s.logger.Printf("[ERROR] Drive migration failed: %v", err) + s.logger.Printf("[ERROR] [web] Drive migration failed: %v", err) } else { - s.logger.Printf("[INFO] Drive migration complete: %s → %s", req.SourcePath, req.DestPath) + s.logger.Printf("[INFO] [web] Drive migration complete: %s → %s", req.SourcePath, req.DestPath) go s.SyncFileBrowserMounts() } close(progressCh) @@ -1580,12 +1580,12 @@ func (s *Server) decommissionRemoveHandler(w http.ResponseWriter, r *http.Reques } if err := s.settings.RemoveStoragePath(req.Path); err != nil { - s.logger.Printf("[ERROR] Failed to remove decommissioned path %s: %v", req.Path, err) + s.logger.Printf("[ERROR] [web] Failed to remove decommissioned path %s: %v", req.Path, err) jsonError(w, "Eltávolítás sikertelen: "+err.Error(), http.StatusInternalServerError) return } - s.logger.Printf("[INFO] Decommissioned storage path removed: %s", req.Path) + s.logger.Printf("[INFO] [web] Decommissioned storage path removed: %s", req.Path) // For form submissions, redirect back to settings if r.Header.Get("Content-Type") != "application/json" {