feat: add file logging to %APPDATA%\Wraith\wraith.log for headless debugging
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m5s

slog now writes to wraith.log instead of stdout. Debug-level logging enabled.
ConnectSSH and UpdateConnection log credential resolution details. This lets
us diagnose the pubkey auth issue without needing a console window.

Check: %APPDATA%\Wraith\wraith.log after attempting a connection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-17 12:09:56 -04:00
parent 9338fef0c2
commit ddd214d6d8
2 changed files with 45 additions and 2 deletions

View File

@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"log/slog"
"strings"
"time"
)
@ -311,6 +312,12 @@ func scanConnections(rows *sql.Rows) ([]Connection, error) {
}
func (s *ConnectionService) UpdateConnection(id int64, input UpdateConnectionInput) (*Connection, error) {
slog.Info("UpdateConnection called",
"id", id,
"name", input.Name,
"credentialID", input.CredentialID,
"groupID", input.GroupID,
)
setClauses := []string{"updated_at = CURRENT_TIMESTAMP"}
args := []interface{}{}
@ -330,9 +337,13 @@ func (s *ConnectionService) UpdateConnection(id int64, input UpdateConnectionInp
setClauses = append(setClauses, "group_id = ?")
args = append(args, *input.GroupID)
}
if input.CredentialID != nil {
// Always update credential_id — nil clears it, non-nil sets it.
// Unlike other fields, credential assignment is explicit on every save.
setClauses = append(setClauses, "credential_id = ?")
if input.CredentialID != nil {
args = append(args, *input.CredentialID)
} else {
args = append(args, nil)
}
if input.Tags != nil {
tags, _ := json.Marshal(input.Tags)

34
main.go
View File

@ -4,6 +4,8 @@ import (
"embed"
"log"
"log/slog"
"os"
"path/filepath"
wraithapp "github.com/vstockwell/wraith/internal/app"
"github.com/wailsapp/wails/v3/pkg/application"
@ -16,7 +18,11 @@ var version = "dev"
var assets embed.FS
func main() {
slog.Info("starting Wraith")
// Set up file logging so we can debug without a console window
if logFile := setupFileLogging(); logFile != nil {
defer logFile.Close()
}
slog.Info("starting Wraith", "version", version)
wraith, err := wraithapp.New(version)
if err != nil {
@ -57,3 +63,29 @@ func main() {
log.Fatal(err)
}
}
// setupFileLogging configures slog to write to %APPDATA%\Wraith\wraith.log (Windows)
// or ~/.local/share/wraith/wraith.log (macOS/Linux). Returns the file handle for deferred close.
func setupFileLogging() *os.File {
var logDir string
if appData := os.Getenv("APPDATA"); appData != "" {
logDir = filepath.Join(appData, "Wraith")
} else if home, err := os.UserHomeDir(); err == nil {
logDir = filepath.Join(home, ".local", "share", "wraith")
} else {
return nil
}
os.MkdirAll(logDir, 0755)
logPath := filepath.Join(logDir, "wraith.log")
f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return nil
}
handler := slog.NewTextHandler(f, &slog.HandlerOptions{Level: slog.LevelDebug})
slog.SetDefault(slog.New(handler))
log.SetOutput(f)
return f
}