diff --git a/docs/pkg/cli/output.md b/docs/pkg/cli/output.md index d907e7f..a1bc979 100644 --- a/docs/pkg/cli/output.md +++ b/docs/pkg/cli/output.md @@ -280,4 +280,5 @@ cli.LogInfo("server started", "port", 8080) cli.LogWarn("slow query", "duration", "3.2s") cli.LogError("connection failed", "err", err) cli.LogSecurity("login attempt", "user", "admin") +cli.LogSecurityf("login attempt from %s", username) ``` diff --git a/pkg/cli/log.go b/pkg/cli/log.go index 2268c1f..649d459 100644 --- a/pkg/cli/log.go +++ b/pkg/cli/log.go @@ -1,6 +1,8 @@ package cli import ( + "fmt" + "forge.lthn.ai/core/go-log" ) @@ -31,3 +33,10 @@ func LogError(msg string, keyvals ...any) { log.Error(msg, keyvals...) } // // cli.LogSecurity("login attempt", "user", "admin") func LogSecurity(msg string, keyvals ...any) { log.Security(msg, keyvals...) } + +// LogSecurityf logs a formatted security-sensitive message. +// +// cli.LogSecurityf("login attempt from %s", username) +func LogSecurityf(format string, args ...any) { + log.Security(fmt.Sprintf(format, args...)) +} diff --git a/pkg/cli/log_test.go b/pkg/cli/log_test.go index 04b5f92..fb75598 100644 --- a/pkg/cli/log_test.go +++ b/pkg/cli/log_test.go @@ -28,3 +28,21 @@ func TestLogSecurity_Good(t *testing.T) { t.Fatalf("expected structured key/value output, got %q", out) } } + +func TestLogSecurityf_Good(t *testing.T) { + var buf bytes.Buffer + original := log.Default() + t.Cleanup(func() { + log.SetDefault(original) + }) + + logger := log.New(log.Options{Level: log.LevelDebug, Output: &buf}) + log.SetDefault(logger) + + LogSecurityf("login attempt from %s", "admin") + + out := buf.String() + if !strings.Contains(out, "login attempt from admin") { + t.Fatalf("expected formatted security log message, got %q", out) + } +}