mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-17 03:59:56 +00:00
When the daemon is set to debug/trace, the GUI now automatically writes a rotated gui-client.log in the user's config dir and the daemon's debug bundle collects it. The UI learns the level both at startup (daemon already in debug) and live, by piggybacking the existing SubscribeEvents stream: the daemon publishes a marked log-level-changed SystemEvent (and a per-subscription snapshot), which DaemonFeed routes to guilog.DebugLog instead of an OS toast. The UI registers its log path via a new RegisterUILog RPC so the root daemon, which can't resolve the user's config dir, knows where to find the file. Manual --log-file (any value) disables the daemon-driven file logging. Fix: client/ui SetLogLevel looked up proto.LogLevel_value with the lowercase logrus name, which never matched the uppercase enum keys and silently fell back to INFO — so trace/debug requests from the bundle flow had no effect.
93 lines
3.2 KiB
Go
93 lines
3.2 KiB
Go
//go:build !android && !ios && !freebsd && !js
|
|
|
|
// Package guilog manages the desktop UI's own file log (gui-client.log), which
|
|
// follows the daemon's log level: when the daemon is in debug/trace the GUI
|
|
// attaches a rotated file alongside the console so its (and the React frontend's
|
|
// forwarded) output is captured for the debug bundle. It is intentionally not a
|
|
// Wails service — it has no frontend-facing methods and generates no TS
|
|
// bindings — so it lives outside client/ui/services.
|
|
package guilog
|
|
|
|
import (
|
|
"sync"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/netbirdio/netbird/util"
|
|
)
|
|
|
|
// DebugLog is the daemon-debug-driven GUI file log. The daemon publishes a
|
|
// marked "log-level-changed" SystemEvent over SubscribeEvents (both on change
|
|
// and once per new subscription, so a daemon already in debug is picked up at
|
|
// startup); services.DaemonFeed routes it here via Apply.
|
|
//
|
|
// When the daemon is in debug/trace and the GUI owns its log (no manual
|
|
// --log-file), it attaches a rotated gui-client.log alongside the console and
|
|
// raises the logrus level; back to a higher level it detaches the file and
|
|
// restores info. The file is left on disk (rotated by timberjack) for the debug
|
|
// bundle to collect. When the user set --log-file explicitly, it is disabled and
|
|
// never touches logging.
|
|
type DebugLog struct {
|
|
uiPath string
|
|
enabled bool
|
|
|
|
mu sync.Mutex
|
|
fileOn bool
|
|
}
|
|
|
|
// NewDebugLog builds the GUI debug log. uiPath is the absolute gui-client.log
|
|
// path; enabled is false when the user passed --log-file (manual override), in
|
|
// which case it leaves logging untouched.
|
|
func NewDebugLog(uiPath string, enabled bool) *DebugLog {
|
|
return &DebugLog{uiPath: uiPath, enabled: enabled}
|
|
}
|
|
|
|
// Path returns the GUI log path to register with the daemon, or "" when the GUI
|
|
// doesn't own its log (manual --log-file) — in that case the daemon shouldn't
|
|
// try to collect a gui-client.log the GUI never writes.
|
|
func (d *DebugLog) Path() string {
|
|
if !d.enabled {
|
|
return ""
|
|
}
|
|
return d.uiPath
|
|
}
|
|
|
|
// Apply reacts to a daemon log level (the lowercase logrus name, e.g. "debug").
|
|
// Idempotent: repeated identical levels are no-ops, so the startup replay plus a
|
|
// racing change-event do no harm.
|
|
func (d *DebugLog) Apply(level string) {
|
|
if !d.enabled {
|
|
return
|
|
}
|
|
|
|
// "debug or more verbose" (debug/trace) turns the file log on; anything less
|
|
// verbose turns it off. Compare numerically against logrus' own levels so
|
|
// there are no hard-coded level-name literals.
|
|
lvl, err := log.ParseLevel(level)
|
|
if err != nil {
|
|
lvl = log.InfoLevel
|
|
}
|
|
debug := lvl >= log.DebugLevel
|
|
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
switch {
|
|
case debug && !d.fileOn:
|
|
if err := util.SetLogOutputs(log.StandardLogger(), util.LogConsole, d.uiPath); err != nil {
|
|
log.Errorf("attach GUI file log %s: %v", d.uiPath, err)
|
|
return
|
|
}
|
|
log.SetLevel(lvl)
|
|
d.fileOn = true
|
|
log.Infof("GUI file logging enabled (daemon level %s), writing to %s", level, d.uiPath)
|
|
case !debug && d.fileOn:
|
|
if err := util.SetLogOutputs(log.StandardLogger(), util.LogConsole); err != nil {
|
|
log.Errorf("detach GUI file log: %v", err)
|
|
}
|
|
log.SetLevel(log.InfoLevel)
|
|
d.fileOn = false
|
|
log.Infof("GUI file logging disabled (daemon level: %s)", level)
|
|
}
|
|
}
|