From 81624588216aedd0f30c6a6722cda2805ee800fd Mon Sep 17 00:00:00 2001 From: Umputun Date: Thu, 7 Nov 2019 01:03:49 -0600 Subject: [PATCH] adds secrets, fix #17 --- README.md | 1 + logger.go | 11 +++++++++++ logger_test.go | 9 +++++++++ options.go | 8 ++++++++ 4 files changed, 29 insertions(+) diff --git a/README.md b/README.md index db5ae90..9e2d14d 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ _Without `lgr.Caller*` it will drop `{caller}` part_ - `lgr.LevelBraces` - wraps levels with "[" and "]" - `lgr.Msec` - adds milliseconds to timestamp - `lgr.Format` - sets custom template, overwrite all other formatting modifiers. +- `lgr.Secret(secret ...)` - sets list of the secrets to hide from the logging outputs. example: `l := lgr.New(lgr.Debug, lgr.Msec)` diff --git a/logger.go b/logger.go index 30dc4dc..baec972 100644 --- a/logger.go +++ b/logger.go @@ -34,6 +34,8 @@ const ( FullDebug = `{{.DT.Format "2006/01/02 15:04:05.000"}} {{.Level}} ({{.CallerFile}}:{{.CallerLine}} {{.CallerFunc}}) {{.Message}}` ) +var secretReplacement = []byte("******") + // Logger provided simple logger with basic support of levels. Thread safe type Logger struct { // set with Option calls @@ -46,6 +48,7 @@ type Logger struct { levelBraces bool // encloses level with [], i.e. [INFO] callerDepth int // how many stack frames to skip, relative to the real (reported) frame format string // layout template + secrets []string // sub-strings to secrets by matching // internal use now nowFn @@ -163,6 +166,7 @@ func (l *Logger) logf(format string, args ...interface{}) { data = bytes.Replace(data, []byte("[WARN ]"), []byte("[WARN] "), 1) data = bytes.Replace(data, []byte("[INFO ]"), []byte("[INFO] "), 1) } + data = l.hideSecrets(data) l.lock.Lock() _, _ = l.stdout.Write(data) @@ -189,6 +193,13 @@ func (l *Logger) logf(format string, args ...interface{}) { l.lock.Unlock() } +func (l *Logger) hideSecrets(data []byte) []byte { + for _, h := range l.secrets { + data = bytes.Replace(data, []byte(h), secretReplacement, -1) + } + return data +} + type callerInfo struct { File string Line int diff --git a/logger_test.go b/logger_test.go index 3beba72..ff89a48 100644 --- a/logger_test.go +++ b/logger_test.go @@ -356,6 +356,15 @@ func TestLoggerNoSpaceLevel(t *testing.T) { }) } } + +func TestLoggerHidden(t *testing.T) { + rout, rerr := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}) + l := New(Out(rout), Err(rerr), Format(Short), Secret("password", "secret")) + l.now = func() time.Time { return time.Date(2018, 1, 7, 13, 2, 34, 123000000, time.Local) } + l.Logf("INFO something password 123 secret xyz") + assert.Equal(t, "2018/01/07 13:02:34 INFO something ****** 123 ****** xyz\n", rout.String(), "secrets secrets") +} + func BenchmarkNoDbgNoFormat(b *testing.B) { rout, rerr := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}) l := New(Out(rout), Err(rerr)) diff --git a/options.go b/options.go index 3d79039..4de15d8 100644 --- a/options.go +++ b/options.go @@ -68,3 +68,11 @@ func CallerFile(l *Logger) { func Msec(l *Logger) { l.msec = true } + +// Secret sets list of substring to be hidden, i.e. replaced by "******" +// Useful to prevent passwords or other sensitive tokens to be logged. +func Secret(vals ...string) Option { + return func(l *Logger) { + l.secrets = vals + } +}