From e545eb4e138c219d6b1bb0c3606ddc469dd8b4fb Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Mon, 5 Jul 2021 22:32:47 +0300 Subject: [PATCH] logger: add wrapper support Signed-off-by: Vasiliy Tolstov --- logger/{micro.go => default.go} | 23 ++++++++--- logger/logger_test.go | 19 +++++++++ logger/options.go | 9 +++++ logger/wrapper.go | 68 +++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 6 deletions(-) rename logger/{micro.go => default.go} (90%) create mode 100644 logger/wrapper.go diff --git a/logger/micro.go b/logger/default.go similarity index 90% rename from logger/micro.go rename to logger/default.go index 9ec61039..6623d4a1 100644 --- a/logger/micro.go +++ b/logger/default.go @@ -24,6 +24,8 @@ type defaultLogger struct { enc *json.Encoder opts Options sync.RWMutex + logFunc LogFunc + logfFunc LogfFunc } // Init(opts...) should only overwrite provided options @@ -33,6 +35,15 @@ func (l *defaultLogger) Init(opts ...Option) error { o(&l.opts) } l.enc = json.NewEncoder(l.opts.Out) + + l.logFunc = l.Log + l.logfFunc = l.Logf + // wrap the Log func + for i := len(l.opts.Wrappers); i > 0; i-- { + l.logFunc = l.opts.Wrappers[i-1].Log(l.logFunc) + l.logfFunc = l.opts.Wrappers[i-1].Logf(l.logfFunc) + } + l.Unlock() return nil } @@ -121,27 +132,27 @@ func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) { } func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, InfoLevel, msg, args...) + l.logfFunc(ctx, InfoLevel, msg, args...) } func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, ErrorLevel, msg, args...) + l.logfFunc(ctx, ErrorLevel, msg, args...) } func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, DebugLevel, msg, args...) + l.logfFunc(ctx, DebugLevel, msg, args...) } func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, WarnLevel, msg, args...) + l.logfFunc(ctx, WarnLevel, msg, args...) } func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, TraceLevel, msg, args...) + l.logfFunc(ctx, TraceLevel, msg, args...) } func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) { - l.Logf(ctx, FatalLevel, msg, args...) + l.logfFunc(ctx, FatalLevel, msg, args...) os.Exit(1) } diff --git a/logger/logger_test.go b/logger/logger_test.go index a6a1f376..db2be511 100644 --- a/logger/logger_test.go +++ b/logger/logger_test.go @@ -1,6 +1,7 @@ package logger import ( + "bytes" "context" "testing" ) @@ -16,3 +17,21 @@ func TestLogger(t *testing.T) { l.Fields(map[string]interface{}{"error": "test"}).Info(ctx, "error message") l.Warn(ctx, "first", " ", "second") } + +func TestLoggerWrapper(t *testing.T) { + ctx := context.TODO() + buf := bytes.NewBuffer(nil) + l := NewLogger(WithLevel(TraceLevel), WithOutput(buf)) + if err := l.Init(WrapLogger(NewOmitWrapper())); err != nil { + t.Fatal(err) + } + type secret struct { + Name string + Passw string `logger:"omit"` + } + s := &secret{Name: "name", Passw: "secret"} + l.Errorf(ctx, "test %#+v", s) + if !bytes.Contains(buf.Bytes(), []byte(`logger.secret{Name:\"name\", Passw:\"\"}"`)) { + t.Fatalf("omit not works, struct: %v, output: %s", s, buf.Bytes()) + } +} diff --git a/logger/options.go b/logger/options.go index 72a5653e..deb9f441 100644 --- a/logger/options.go +++ b/logger/options.go @@ -23,6 +23,8 @@ type Options struct { CallerSkipCount int // The logging level the logger should log Level Level + // Wrappers logger wrapper that called before actual Log/Logf function + Wrappers []Wrapper } // NewOptions creates new options struct @@ -81,3 +83,10 @@ func WithName(n string) Option { o.Name = n } } + +// WrapLogger adds a logger Wrapper to a list of options passed into the logger +func WrapLogger(w Wrapper) Option { + return func(o *Options) { + o.Wrappers = append(o.Wrappers, w) + } +} diff --git a/logger/wrapper.go b/logger/wrapper.go new file mode 100644 index 00000000..1021ad4b --- /dev/null +++ b/logger/wrapper.go @@ -0,0 +1,68 @@ +package logger + +import ( + "context" + "reflect" + + rutil "github.com/unistack-org/micro/v3/util/reflect" +) + +// LogFunc function used for Log method +type LogFunc func(ctx context.Context, level Level, args ...interface{}) + +// LogfFunc function used for Logf method +type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{}) + +type Wrapper interface { + // Log logs message with needed level + Log(LogFunc) LogFunc + // Log(ctx context.Context, level Level, args ...interface{}) + // Logf logs message with needed level + Logf(LogfFunc) LogfFunc + //Logf(ctx context.Context, level Level, msg string, args ...interface{}) +} + +type OmitWrapper struct{} + +func NewOmitWrapper() Wrapper { + return &OmitWrapper{} +} + +func getArgs(args []interface{}) []interface{} { + nargs := make([]interface{}, 0, len(args)) + var err error + for _, arg := range args { + val := reflect.ValueOf(arg) + switch val.Kind() { + case reflect.Ptr: + val = val.Elem() + } + narg := arg + if val.Kind() == reflect.Struct { + if narg, err = rutil.Zero(arg); err == nil { + rutil.CopyDefaults(narg, arg) + if flds, ferr := rutil.StructFields(narg); ferr == nil { + for _, fld := range flds { + if tv, ok := fld.Field.Tag.Lookup("logger"); ok && tv == "omit" { + fld.Value.Set(reflect.Zero(fld.Value.Type())) + } + } + } + } + } + nargs = append(nargs, narg) + } + return nargs +} + +func (w *OmitWrapper) Log(fn LogFunc) LogFunc { + return func(ctx context.Context, level Level, args ...interface{}) { + fn(ctx, level, getArgs(args)...) + } +} + +func (w *OmitWrapper) Logf(fn LogfFunc) LogfFunc { + return func(ctx context.Context, level Level, msg string, args ...interface{}) { + fn(ctx, level, msg, getArgs(args)...) + } +}