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:
@@ -12,6 +12,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/appexport"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/assets"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
@@ -78,6 +79,9 @@ type Server struct {
|
||||
// App-to-app integration manager (optional)
|
||||
integrationMgr *integrations.Manager
|
||||
|
||||
// App export/import engine (optional)
|
||||
appExporter *appexport.Exporter
|
||||
|
||||
// Debug mode support
|
||||
logBuffer *LogBuffer
|
||||
debugCallbacks *DebugCallbacks
|
||||
@@ -102,6 +106,13 @@ func NewServer(cfg *config.Config, stackMgr *stacks.Manager, cpuCollector *syste
|
||||
loginAttempts: make(map[string]*loginAttempt),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
if cfg.Logging.Level == "debug" {
|
||||
logger.Printf("[DEBUG] [web] NewServer: initializing web server v%s", version)
|
||||
logger.Printf("[DEBUG] [web] NewServer: backup=%v crossDrive=%v scheduler=%v alertMgr=%v notifier=%v updater=%v",
|
||||
backupMgr != nil, crossDrive != nil, sched != nil, alertMgr != nil, notif != nil, updater != nil)
|
||||
}
|
||||
|
||||
s.loadTemplates()
|
||||
go s.cleanupSessions()
|
||||
|
||||
@@ -138,6 +149,10 @@ func (s *Server) loadTemplates() {
|
||||
s.tmpl = template.Must(
|
||||
template.New("").Funcs(s.templateFuncMap()).ParseFS(templateFS, "templates/*.html"),
|
||||
)
|
||||
if s.isDebug() {
|
||||
names := s.tmpl.Templates()
|
||||
s.logger.Printf("[DEBUG] [web] loadTemplates: loaded %d templates", len(names))
|
||||
}
|
||||
}
|
||||
|
||||
// SetRestoreState puts the server into DR restore mode with the given plan.
|
||||
@@ -190,6 +205,11 @@ func (s *Server) SetDebugCallbacks(dc *DebugCallbacks) {
|
||||
s.debugCallbacks = dc
|
||||
}
|
||||
|
||||
// SetAppExporter sets the app export/import engine.
|
||||
func (s *Server) SetAppExporter(e *appexport.Exporter) {
|
||||
s.appExporter = e
|
||||
}
|
||||
|
||||
// SetStartTime records the controller start time for uptime calculation.
|
||||
func (s *Server) SetStartTime(t time.Time) {
|
||||
s.startTime = t
|
||||
@@ -221,6 +241,10 @@ func (s *Server) InRestoreMode() bool {
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
path := r.URL.Path
|
||||
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] [web] ServeHTTP: %s %s from %s", r.Method, path, r.RemoteAddr)
|
||||
}
|
||||
|
||||
// DR restore mode: intercept all routes except restore page, static, and restore API
|
||||
if s.InRestoreMode() {
|
||||
switch {
|
||||
@@ -283,6 +307,10 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.storageAttachHandler(w, r)
|
||||
case path == "/settings/storage/migrate-drive":
|
||||
s.migrateDrivePageHandler(w, r)
|
||||
case strings.HasPrefix(path, "/stacks/") && strings.HasSuffix(path, "/export"):
|
||||
name := strings.TrimPrefix(path, "/stacks/")
|
||||
name = strings.TrimSuffix(name, "/export")
|
||||
s.exportPageHandler(w, r, name)
|
||||
case strings.HasPrefix(path, "/stacks/") && strings.HasSuffix(path, "/migrate"):
|
||||
name := strings.TrimPrefix(path, "/stacks/")
|
||||
name = strings.TrimSuffix(name, "/migrate")
|
||||
@@ -295,6 +323,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
name := strings.TrimPrefix(path, "/stacks/")
|
||||
name = strings.TrimSuffix(name, "/deploy")
|
||||
s.deployHandler(w, r, name)
|
||||
case path == "/import":
|
||||
s.importPageHandler(w, r)
|
||||
case path == "/static/style.css":
|
||||
s.serveCSSHandler(w, r)
|
||||
case path == "/static/chart.min.js":
|
||||
@@ -324,6 +354,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// the controller host (felhom.DOMAIN) pass through normally.
|
||||
func (s *Server) CatchAllMiddleware(next http.Handler) http.Handler {
|
||||
controllerHost := "felhom." + s.cfg.Customer.Domain
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] [web] CatchAllMiddleware: controller host=%s", controllerHost)
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
host := r.Host
|
||||
if idx := strings.LastIndex(host, ":"); idx != -1 {
|
||||
@@ -335,6 +368,9 @@ func (s *Server) CatchAllMiddleware(next http.Handler) http.Handler {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] [web] CatchAllMiddleware: non-controller host=%s, serving catch-all page", host)
|
||||
}
|
||||
s.serveCatchAll(w, r, host)
|
||||
})
|
||||
}
|
||||
@@ -405,6 +441,9 @@ func (s *Server) findStackBySubdomain(subdomain string) (*stacks.Stack, bool) {
|
||||
|
||||
// ServeStorageAPI handles /api/storage/* routes (JSON API for disk operations).
|
||||
func (s *Server) ServeStorageAPI(w http.ResponseWriter, r *http.Request) {
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] [web] ServeStorageAPI: %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr)
|
||||
}
|
||||
s.storageAPIHandler(w, r)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user