chore: release v0.21.3

This commit is contained in:
2026-07-26 16:57:19 +02:00
parent 4fd70f6a9d
commit 91b5af1c7b
46 changed files with 2491 additions and 355 deletions
+19 -4
View File
@@ -4,6 +4,7 @@ import (
"log"
"os"
"path/filepath"
"sync"
)
// LogSink, when set by the host app at startup, receives every CAT debug
@@ -15,13 +16,21 @@ 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 }
type catLogger struct {
once sync.Once
fallback *log.Logger
}
func (c *catLogger) Printf(format string, args ...any) {
if LogSink != nil {
LogSink("cat: "+format, args...)
return
}
// Only now, on a line that genuinely has nowhere else to go, is the fallback
// file opened. It used to be created at package init, so every installation
// grew an %APPDATA%\OpsLog\cat.log that nothing ever wrote to once the app
// wired LogSink — a decoy for anyone told to "check the CAT log".
c.once.Do(func() { c.fallback = openFallbackLog() })
if c.fallback != nil {
c.fallback.Printf(format, args...)
}
@@ -30,7 +39,7 @@ func (c *catLogger) Printf(format string, args ...any) {
// 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()}
var debugLog = &catLogger{}
func openFallbackLog() *log.Logger {
base, err := os.UserConfigDir()
@@ -49,9 +58,15 @@ func openFallbackLog() *log.Logger {
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.
// DebugLogPath returns where the fallback cat.log lives, or "" when CAT lines are
// going to the app log instead — which is the normal case, and the answer callers
// actually need. It previously returned the path unconditionally, so the one place
// that displayed it sent operators to an empty file in %APPDATA% while their CAT
// diagnostics sat in data\opslog.log.
func DebugLogPath() string {
if LogSink != nil {
return "" // lines go to the unified app log; there is no separate cat.log
}
base, err := os.UserConfigDir()
if err != nil {
return ""