package cat import ( "log" "os" "path/filepath" ) // LogSink, when set by the host app at startup, receives every CAT debug // line so they land in the unified app log (opslog.log) alongside the rest // of OpsLog's diagnostics. Until it's wired we fall back to a dedicated // cat.log file so early-startup lines aren't lost. var LogSink func(format string, args ...any) // catLogger forwards Printf either to the host LogSink (preferred) or to a // local file/stderr fallback. Keeps the call sites (debugLog.Printf(...)) // unchanged. type catLogger struct{ fallback *log.Logger } func (c *catLogger) Printf(format string, args ...any) { if LogSink != nil { LogSink("cat: "+format, args...) return } if c.fallback != nil { c.fallback.Printf(format, args...) } } // debugLog writes CAT debug events so users can diagnose mode/freq mismatches // without rebuilding with a console. Once LogSink is set, lines flow into the // main opslog.log. var debugLog = &catLogger{fallback: openFallbackLog()} func openFallbackLog() *log.Logger { base, err := os.UserConfigDir() if err != nil { return log.Default() } dir := filepath.Join(base, "OpsLog") if err := os.MkdirAll(dir, 0o755); err != nil { return log.Default() } f, err := os.OpenFile(filepath.Join(dir, "cat.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { return log.Default() } return log.New(f, "", log.LstdFlags|log.Lmicroseconds) } // DebugLogPath returns where the fallback cat.log lives, for surfacing in the // UI / docs. When LogSink is wired, CAT lines are in the main app log instead. func DebugLogPath() string { base, err := os.UserConfigDir() if err != nil { return "" } return filepath.Join(base, "OpsLog", "cat.log") }