secret/internal/secret/debug.go
sneak 080a3dc253 fix: resolve all nlreturn linter errors
Add blank lines before return statements in all files to satisfy
the nlreturn linter. This improves code readability by providing
visual separation before return statements.

Changes made across 24 files:
- internal/cli/*.go
- internal/secret/*.go
- internal/vault/*.go
- pkg/agehd/agehd.go
- pkg/bip85/bip85.go

All 143 nlreturn issues have been resolved.
2025-07-15 06:00:32 +02:00

140 lines
3.4 KiB
Go

package secret
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"strings"
"syscall"
"golang.org/x/term"
)
var (
debugEnabled bool //nolint:gochecknoglobals // Package-wide debug state is necessary
debugLogger *slog.Logger //nolint:gochecknoglobals // Package-wide logger instance is necessary
)
func init() {
InitDebugLogging()
}
// InitDebugLogging initializes the debug logging system based on current GODEBUG environment variable
func InitDebugLogging() {
godebug := os.Getenv("GODEBUG")
debugEnabled = strings.Contains(godebug, "berlin.sneak.pkg.secret")
if !debugEnabled {
// Create a no-op logger that discards all output
debugLogger = slog.New(slog.NewTextHandler(io.Discard, nil))
return
}
// Disable stderr buffering for immediate debug output when debugging is enabled
_, _, _ = syscall.Syscall(syscall.SYS_FCNTL, os.Stderr.Fd(), syscall.F_SETFL, syscall.O_SYNC)
// Check if STDERR is a TTY
isTTY := term.IsTerminal(syscall.Stderr)
var handler slog.Handler
if isTTY {
// TTY output: colorized structured format
handler = newColorizedHandler(os.Stderr)
} else {
// Non-TTY output: JSON Lines format
handler = slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelDebug,
})
}
debugLogger = slog.New(handler)
}
// IsDebugEnabled returns true if debug logging is enabled
func IsDebugEnabled() bool {
return debugEnabled
}
// Debug logs a debug message with optional attributes
func Debug(msg string, args ...any) {
if !debugEnabled {
return
}
debugLogger.Debug(msg, args...)
}
// DebugF logs a formatted debug message with optional attributes
func DebugF(format string, args ...any) {
if !debugEnabled {
return
}
debugLogger.Debug(fmt.Sprintf(format, args...))
}
// DebugWith logs a debug message with structured attributes
func DebugWith(msg string, attrs ...slog.Attr) {
if !debugEnabled {
return
}
debugLogger.LogAttrs(context.Background(), slog.LevelDebug, msg, attrs...)
}
// colorizedHandler implements a TTY-friendly structured log handler
type colorizedHandler struct {
output io.Writer
}
func newColorizedHandler(output io.Writer) slog.Handler {
return &colorizedHandler{output: output}
}
func (h *colorizedHandler) Enabled(_ context.Context, level slog.Level) bool {
// Explicitly check that debug is enabled AND the level is DEBUG or higher
// This ensures we don't default to INFO level when debug is enabled
return debugEnabled && level >= slog.LevelDebug
}
func (h *colorizedHandler) Handle(_ context.Context, record slog.Record) error {
if !debugEnabled {
return nil
}
// Format: [DEBUG] message {key=value, key2=value2}
output := fmt.Sprintf("\033[36m[DEBUG]\033[0m \033[1m%s\033[0m", record.Message)
if record.NumAttrs() > 0 {
output += " \033[33m{"
first := true
record.Attrs(func(attr slog.Attr) bool {
if !first {
output += ", "
}
first = false
output += fmt.Sprintf("%s=%#v", attr.Key, attr.Value.Any())
return true
})
output += "}\033[0m"
}
output += "\n"
_, err := h.output.Write([]byte(output))
return err
}
func (h *colorizedHandler) WithAttrs(_ []slog.Attr) slog.Handler {
// For simplicity, return the same handler
// In a more complex implementation, we'd create a new handler with the attrs
return h
}
func (h *colorizedHandler) WithGroup(_ string) slog.Handler {
// For simplicity, return the same handler
// In a more complex implementation, we'd create a new handler with the group
return h
}