package main import ( "fmt" "log" "os" "path/filepath" "time" "verstak/internal/core/config" ) // appLogPath returns the path to the application log file. // Uses ~/.local/state/verstak/logs/verstak.log on Linux, // or falls back to ~/.config/verstak/logs/verstak.log. func appLogPath() string { // Prefer XDG_STATE_HOME, then fallback to config dir stateDir := os.Getenv("XDG_STATE_HOME") if stateDir == "" { home, err := os.UserHomeDir() if err == nil { stateDir = filepath.Join(home, ".local", "state") } } if stateDir != "" { dir := filepath.Join(stateDir, "verstak", "logs") if err := os.MkdirAll(dir, 0o755); err == nil { return filepath.Join(dir, "verstak.log") } } // Fallback to config dir cfgDir, err := config.EnsureConfigDir() if err != nil { return "" } dir := filepath.Join(cfgDir, "logs") if err := os.MkdirAll(dir, 0o755); err != nil { return "" } return filepath.Join(dir, "verstak.log") } // appLog writes a timestamped line to the application log file. func appLog(level, msg string) { logPath := appLogPath() if logPath == "" { log.Printf("[%s] %s", level, msg) return } line := fmt.Sprintf("[%s] [%s] %s\n", time.Now().Format("2006-01-02T15:04:05"), level, msg) f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { log.Printf("[%s] %s", level, msg) return } defer f.Close() f.WriteString(line) } // FrontendLog receives log messages from the frontend runtime. // Called from JS via Wails binding. Works even if vault is not open. // level: "info", "warn", "error" // message: human-readable message // stack: JS stack trace (optional, only for errors) func (a *App) FrontendLog(level, message, stack string) { msg := "[frontend] " + message if stack != "" { msg += "\n stack: " + stack } // Always log to Go's standard logger (visible in dev mode console) log.Printf("[frontend][%s] %s", level, message) // Persist to log file appLog("frontend-"+level, msg) } // LogStartupStep logs a startup diagnostic step. // Called from Go side to trace initialization progress. func (a *App) LogStartupStep(step string, success bool, detail string) { status := "ok" if !success { status = "fail" } msg := fmt.Sprintf("[startup] %s: %s", step, status) if detail != "" { msg += " — " + detail } log.Print(msg) appLog("startup", msg) }