feat: comprehensive debug logging across all controller modules
Add detailed [DEBUG] logging to every controller module when logging.level is set to "debug". Each module with stateful debug uses SetDebug(bool) wired from main.go. Covers stacks, backup, cloudflare, integrations, system, monitor, settings, scheduler, web handlers, storage, metrics, API, selfupdate, and assets. Also includes the app export/import (.fab bundles) feature from v0.32.0 and its debug page integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -117,15 +117,33 @@ func (m *Manager) SetEncryptionKey(key []byte) {
|
||||
m.encKey = key
|
||||
}
|
||||
|
||||
// GetStacksBaseDir returns the base directory where stacks live.
|
||||
func (m *Manager) GetStacksBaseDir() string {
|
||||
return m.cfg.Paths.StacksDir
|
||||
}
|
||||
|
||||
// MigrateEncryption re-saves app.yaml for deployed stacks that still have
|
||||
// plaintext values in sensitive fields. Called once on startup.
|
||||
func (m *Manager) MigrateEncryption() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.encKey == nil {
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] MigrateEncryption: no encryption key set, skipping")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if m.isDebug() {
|
||||
deployedCount := 0
|
||||
for _, s := range m.stacks {
|
||||
if s.Deployed {
|
||||
deployedCount++
|
||||
}
|
||||
}
|
||||
m.logger.Printf("[DEBUG] [stacks] MigrateEncryption: checking %d deployed stacks for plaintext sensitive values", deployedCount)
|
||||
}
|
||||
|
||||
migrated := 0
|
||||
for _, s := range m.stacks {
|
||||
if !s.Deployed {
|
||||
@@ -141,6 +159,11 @@ func (m *Manager) MigrateEncryption() {
|
||||
if len(sensitive) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] MigrateEncryption: checking stack %q (%d sensitive fields)", s.Name, len(sensitive))
|
||||
}
|
||||
|
||||
needsMigration := false
|
||||
for _, envVar := range sensitive {
|
||||
if v, ok := appCfg.Env[envVar]; ok && v != "" && !crypto.IsEncrypted(v) {
|
||||
@@ -149,6 +172,9 @@ func (m *Manager) MigrateEncryption() {
|
||||
}
|
||||
}
|
||||
if needsMigration {
|
||||
if m.isDebug() {
|
||||
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)
|
||||
} else {
|
||||
@@ -229,6 +255,10 @@ func (m *Manager) ScanStacks() error {
|
||||
appCfg := LoadAppConfig(stackDir)
|
||||
deployed := appCfg != nil && appCfg.Deployed
|
||||
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] ScanStacks: found stack %q deployed=%v composePath=%s", name, deployed, composePath)
|
||||
}
|
||||
|
||||
if existing, ok := m.stacks[name]; ok {
|
||||
existing.ComposePath = composePath
|
||||
existing.Meta = meta
|
||||
@@ -261,6 +291,13 @@ func (m *Manager) ScanStacks() error {
|
||||
|
||||
// Detect orphaned stacks (deployed but no longer in catalog)
|
||||
catalogTemplates := m.getCatalogTemplateSlugs()
|
||||
if m.isDebug() {
|
||||
if catalogTemplates != nil {
|
||||
m.logger.Printf("[DEBUG] [stacks] ScanStacks: catalog has %d template slugs for orphan detection", len(catalogTemplates))
|
||||
} else {
|
||||
m.logger.Printf("[DEBUG] [stacks] ScanStacks: catalog templates unavailable, skipping orphan detection")
|
||||
}
|
||||
}
|
||||
if catalogTemplates != nil {
|
||||
orphanCount := 0
|
||||
for _, stack := range m.stacks {
|
||||
@@ -271,6 +308,9 @@ func (m *Manager) ScanStacks() error {
|
||||
stack.Orphaned = !catalogTemplates[stack.Name]
|
||||
if stack.Orphaned {
|
||||
orphanCount++
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] ScanStacks: stack %q is orphaned (deployed but not in catalog)", stack.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if orphanCount > 0 {
|
||||
@@ -306,6 +346,7 @@ func (m *Manager) refreshStatusLocked() error {
|
||||
|
||||
projectContainers := make(map[string][]ContainerInfo)
|
||||
|
||||
totalContainers := 0
|
||||
for _, line := range strings.Split(strings.TrimSpace(output), "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
@@ -322,6 +363,11 @@ func (m *Manager) refreshStatusLocked() error {
|
||||
Status: parts[3],
|
||||
}
|
||||
projectContainers[parts[4]] = append(projectContainers[parts[4]], ci)
|
||||
totalContainers++
|
||||
}
|
||||
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] refreshStatusLocked: docker ps returned %d containers across %d projects", totalContainers, len(projectContainers))
|
||||
}
|
||||
|
||||
for name, stack := range m.stacks {
|
||||
@@ -346,6 +392,10 @@ func (m *Manager) refreshStatusLocked() error {
|
||||
stack.State = StateUnhealthy
|
||||
}
|
||||
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] refreshStatusLocked: stack %q → state=%s containers=%d", name, stack.State, len(stack.Containers))
|
||||
}
|
||||
|
||||
stack.LastUpdated = time.Now()
|
||||
}
|
||||
|
||||
@@ -569,12 +619,20 @@ func (m *Manager) StartStack(name string) error {
|
||||
return fmt.Errorf("stack %q not found", name)
|
||||
}
|
||||
|
||||
if m.isDebug() {
|
||||
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)
|
||||
start := time.Now()
|
||||
|
||||
dir := filepath.Dir(stack.ComposePath)
|
||||
env := m.stackEnv(dir)
|
||||
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] StartStack %s: prepared %d env vars for compose", name, len(env))
|
||||
}
|
||||
|
||||
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)
|
||||
return fmt.Errorf("starting stack %s: %w", name, err)
|
||||
@@ -604,6 +662,10 @@ func (m *Manager) StopStack(name string) error {
|
||||
return fmt.Errorf("stack %q not found", name)
|
||||
}
|
||||
|
||||
if m.isDebug() {
|
||||
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)
|
||||
start := time.Now()
|
||||
dir := filepath.Dir(stack.ComposePath)
|
||||
@@ -623,6 +685,10 @@ func (m *Manager) RestartStack(name string) error {
|
||||
return fmt.Errorf("stack %q not found", name)
|
||||
}
|
||||
|
||||
if m.isDebug() {
|
||||
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)
|
||||
start := time.Now()
|
||||
dir := filepath.Dir(stack.ComposePath)
|
||||
@@ -997,5 +1063,8 @@ func (m *Manager) getCatalogTemplateSlugs() map[string]bool {
|
||||
}
|
||||
}
|
||||
}
|
||||
if m.isDebug() {
|
||||
m.logger.Printf("[DEBUG] [stacks] getCatalogTemplateSlugs: found %d template slugs in %s", len(slugs), cacheDir)
|
||||
}
|
||||
return slugs
|
||||
}
|
||||
Reference in New Issue
Block a user