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:
@@ -67,6 +67,12 @@ func New(hubURL, apiKey, assetsDir, fallbackDir string, logger *log.Logger, debu
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Syncer) dbg(format string, args ...interface{}) {
|
||||
if s.debug {
|
||||
s.logger.Printf("[DEBUG] [assets] "+format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Sync fetches the manifest from the Hub, compares checksums, and downloads
|
||||
// changed/new files. It also removes local files not in the Hub manifest.
|
||||
func (s *Syncer) Sync(ctx context.Context) error {
|
||||
@@ -85,6 +91,7 @@ func (s *Syncer) Sync(ctx context.Context) error {
|
||||
}()
|
||||
|
||||
s.logger.Println("[INFO] Asset sync starting...")
|
||||
syncStart := time.Now()
|
||||
|
||||
if err := os.MkdirAll(s.assetsDir, 0755); err != nil {
|
||||
s.setError(fmt.Errorf("create assets dir: %w", err))
|
||||
@@ -92,27 +99,24 @@ func (s *Syncer) Sync(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// 1. Fetch Hub manifest
|
||||
if s.debug {
|
||||
s.logger.Printf("[DEBUG] Asset sync: fetching manifest from %s/api/v1/assets/manifest", s.hubURL)
|
||||
}
|
||||
s.dbg("fetching manifest from %s/api/v1/assets/manifest", s.hubURL)
|
||||
manifestStart := time.Now()
|
||||
manifest, err := s.fetchManifest(ctx)
|
||||
if err != nil {
|
||||
s.setError(fmt.Errorf("fetch manifest: %w", err))
|
||||
return err
|
||||
}
|
||||
if s.debug {
|
||||
s.logger.Printf("[DEBUG] Asset sync: manifest has %d files", len(manifest.Files))
|
||||
}
|
||||
s.dbg("manifest fetched in %s: %d files, generated=%s",
|
||||
time.Since(manifestStart).Round(time.Millisecond), len(manifest.Files), manifest.Generated)
|
||||
|
||||
// 2. Build local hash map
|
||||
hashStart := time.Now()
|
||||
localHashes, err := s.buildLocalHashes()
|
||||
if err != nil {
|
||||
s.setError(fmt.Errorf("scan local assets: %w", err))
|
||||
return err
|
||||
}
|
||||
if s.debug {
|
||||
s.logger.Printf("[DEBUG] Asset sync: %d local files found", len(localHashes))
|
||||
}
|
||||
s.dbg("local hash scan: %d files in %s", len(localHashes), time.Since(hashStart).Round(time.Millisecond))
|
||||
|
||||
// 3. Download changed/new files
|
||||
hubFiles := make(map[string]bool, len(manifest.Files))
|
||||
@@ -124,17 +128,22 @@ func (s *Syncer) Sync(ctx context.Context) error {
|
||||
totalBytes += entry.Size
|
||||
|
||||
if localHash, ok := localHashes[entry.Filename]; ok && localHash == entry.SHA256 {
|
||||
s.dbg("file %s: hash match (%s), skipping", entry.Filename, entry.SHA256[:12]+"...")
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
if s.debug {
|
||||
s.logger.Printf("[DEBUG] Asset sync: downloading %s (remote sha256=%s)", entry.Filename, entry.SHA256[:12]+"...")
|
||||
reason := "new"
|
||||
if localHash, ok := localHashes[entry.Filename]; ok {
|
||||
reason = fmt.Sprintf("hash mismatch (local=%s remote=%s)", localHash[:12]+"...", entry.SHA256[:12]+"...")
|
||||
}
|
||||
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)
|
||||
continue
|
||||
}
|
||||
s.dbg("file %s: downloaded in %s", entry.Filename, time.Since(dlStart).Round(time.Millisecond))
|
||||
downloaded++
|
||||
}
|
||||
|
||||
@@ -143,9 +152,7 @@ func (s *Syncer) Sync(ctx context.Context) error {
|
||||
for name := range localHashes {
|
||||
if !hubFiles[name] {
|
||||
path := filepath.Join(s.assetsDir, name)
|
||||
if s.debug {
|
||||
s.logger.Printf("[DEBUG] Asset sync: removing stale file %s", 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)
|
||||
} else {
|
||||
@@ -169,6 +176,7 @@ func (s *Syncer) Sync(ctx context.Context) error {
|
||||
|
||||
s.logger.Printf("[INFO] 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))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -223,9 +231,11 @@ func (s *Syncer) fetchManifest(ctx context.Context) (*HubManifest, error) {
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
s.dbg("fetchManifest: HTTP request failed: %v", err)
|
||||
return nil, fmt.Errorf("HTTP request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
s.dbg("fetchManifest: HTTP %d, content-length=%d", resp.StatusCode, resp.ContentLength)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
|
||||
@@ -270,13 +280,16 @@ func (s *Syncer) downloadFile(ctx context.Context, filename string) error {
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
s.dbg("downloadFile %s: HTTP request failed: %v", filename, err)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
s.dbg("downloadFile %s: unexpected HTTP %d", filename, resp.StatusCode)
|
||||
return fmt.Errorf("HTTP %d for %s", resp.StatusCode, filename)
|
||||
}
|
||||
s.dbg("downloadFile %s: HTTP %d, content-length=%d", filename, resp.StatusCode, resp.ContentLength)
|
||||
|
||||
// Atomic write: write to .tmp, rename
|
||||
dst := filepath.Join(s.assetsDir, filepath.Base(filename))
|
||||
|
||||
Reference in New Issue
Block a user