From 0ed2e0f3793d389be3407a77c2476d7ae37907ec Mon Sep 17 00:00:00 2001 From: realityone Date: Wed, 12 Jan 2022 15:34:49 +0800 Subject: [PATCH] feat(log): add global logger appliance, as process level default logger (#1761) * add global logger appliance, as process level default logger * replace DefaultLogger as global logger --- app.go | 2 +- config/config.go | 2 +- config/config_test.go | 4 +- contrib/config/apollo/apollo.go | 4 +- contrib/config/apollo/watcher.go | 2 +- log/global.go | 126 ++++++++++++++++++ log/global_test.go | 68 ++++++++++ middleware/recovery/recovery.go | 2 +- middleware/recovery/recovery_test.go | 2 +- transport/grpc/resolver/discovery/builder.go | 2 +- .../grpc/resolver/discovery/resolver_test.go | 6 +- transport/grpc/server.go | 2 +- transport/http/resolver.go | 2 +- transport/http/server.go | 2 +- 14 files changed, 210 insertions(+), 16 deletions(-) create mode 100644 log/global.go create mode 100644 log/global_test.go diff --git a/app.go b/app.go index 736c27a18..8418af429 100644 --- a/app.go +++ b/app.go @@ -39,7 +39,7 @@ type App struct { func New(opts ...Option) *App { o := options{ ctx: context.Background(), - logger: log.NewHelper(log.DefaultLogger), + logger: log.NewHelper(log.GetLogger()), sigs: []os.Signal{syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT}, registrarTimeout: 10 * time.Second, stopTimeout: 10 * time.Second, diff --git a/config/config.go b/config/config.go index 5fed286bc..82c198a15 100644 --- a/config/config.go +++ b/config/config.go @@ -49,7 +49,7 @@ type config struct { // New new a config with options. func New(opts ...Option) Config { o := options{ - logger: log.DefaultLogger, + logger: log.GetLogger(), decoder: defaultDecoder, resolver: defaultResolver, } diff --git a/config/config_test.go b/config/config_test.go index 27f453c12..ecf0b3503 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -123,7 +123,7 @@ func TestConfig(t *testing.T) { WithSource(newTestJSONSource(_testJSON)), WithDecoder(defaultDecoder), WithResolver(defaultResolver), - WithLogger(log.DefaultLogger), + WithLogger(log.GetLogger()), ) err = c.Close() assert.Nil(t, err) @@ -133,7 +133,7 @@ func TestConfig(t *testing.T) { sources: []Source{jSource}, decoder: defaultDecoder, resolver: defaultResolver, - logger: log.DefaultLogger, + logger: log.GetLogger(), } cf := &config{} cf.opts = opts diff --git a/contrib/config/apollo/apollo.go b/contrib/config/apollo/apollo.go index 866c0ea3f..2b69ecfd4 100644 --- a/contrib/config/apollo/apollo.go +++ b/contrib/config/apollo/apollo.go @@ -99,7 +99,7 @@ func WithLogger(logger log.Logger) Option { func NewSource(opts ...Option) config.Source { op := options{ - logger: log.DefaultLogger, + logger: log.GetLogger(), } for _, o := range opts { o(&op) @@ -167,7 +167,7 @@ func resolve(key string, value interface{}, target map[string]interface{}) { // current exists, then check existing value type, if it's not map // that means duplicate keys, and at least one is not map instance. if cursor, ok = v.(map[string]interface{}); !ok { - _ = log.DefaultLogger.Log(log.LevelWarn, + _ = log.GetLogger().Log(log.LevelWarn, "msg", fmt.Sprintf("duplicate key: %v\n", strings.Join(keys[:i+1], ".")), ) diff --git a/contrib/config/apollo/watcher.go b/contrib/config/apollo/watcher.go index c36d30046..6ada4d958 100644 --- a/contrib/config/apollo/watcher.go +++ b/contrib/config/apollo/watcher.go @@ -63,7 +63,7 @@ func (c *customChangeListener) OnNewestChange(changeEvent *storage.FullChangeEve func newWatcher(a *apollo, logger log.Logger) (config.Watcher, error) { if logger == nil { - logger = log.DefaultLogger + logger = log.GetLogger() } changeCh := make(chan []*config.KeyValue) diff --git a/log/global.go b/log/global.go new file mode 100644 index 000000000..ab993ba08 --- /dev/null +++ b/log/global.go @@ -0,0 +1,126 @@ +package log + +import ( + "fmt" + "os" + "sync" +) + +// globalLogger is designed as a global logger in current process. +var ( + global = &loggerAppliance{} +) + +type loggerAppliance struct { + lock sync.Mutex + logger Logger + helper *Helper +} + +func init() { + global.SetLogger(DefaultLogger) +} + +func (a *loggerAppliance) SetLogger(in Logger) { + a.lock.Lock() + defer a.lock.Unlock() + a.logger = in + a.helper = NewHelper(a.logger) +} + +func (a *loggerAppliance) GetLogger() Logger { + return a.logger +} + +// SetLogger should be called before any other log call. +// And it is NOT THREAD SAFE. +func SetLogger(logger Logger) { + global.SetLogger(logger) +} + +func GetLogger() Logger { + return global.GetLogger() +} + +// Log Print log by level and keyvals. +func Log(level Level, keyvals ...interface{}) { + _ = global.logger.Log(level, keyvals...) +} + +// Debug logs a message at debug level. +func Debug(a ...interface{}) { + global.helper.Log(LevelDebug, global.helper.msgKey, fmt.Sprint(a...)) +} + +// Debugf logs a message at debug level. +func Debugf(format string, a ...interface{}) { + global.helper.Log(LevelDebug, global.helper.msgKey, fmt.Sprintf(format, a...)) +} + +// Debugw logs a message at debug level. +func Debugw(keyvals ...interface{}) { + global.helper.Log(LevelDebug, keyvals...) +} + +// Info logs a message at info level. +func Info(a ...interface{}) { + global.helper.Log(LevelInfo, global.helper.msgKey, fmt.Sprint(a...)) +} + +// Infof logs a message at info level. +func Infof(format string, a ...interface{}) { + global.helper.Log(LevelInfo, global.helper.msgKey, fmt.Sprintf(format, a...)) +} + +// Infow logs a message at info level. +func Infow(keyvals ...interface{}) { + global.helper.Log(LevelInfo, keyvals...) +} + +// Warn logs a message at warn level. +func Warn(a ...interface{}) { + global.helper.Log(LevelWarn, global.helper.msgKey, fmt.Sprint(a...)) +} + +// Warnf logs a message at warnf level. +func Warnf(format string, a ...interface{}) { + global.helper.Log(LevelWarn, global.helper.msgKey, fmt.Sprintf(format, a...)) +} + +// Warnw logs a message at warnf level. +func Warnw(keyvals ...interface{}) { + global.helper.Log(LevelWarn, keyvals...) +} + +// Error logs a message at error level. +func Error(a ...interface{}) { + global.helper.Log(LevelError, global.helper.msgKey, fmt.Sprint(a...)) +} + +// Errorf logs a message at error level. +func Errorf(format string, a ...interface{}) { + global.helper.Log(LevelError, global.helper.msgKey, fmt.Sprintf(format, a...)) +} + +// Errorw logs a message at error level. +func Errorw(keyvals ...interface{}) { + global.helper.Log(LevelError, keyvals...) +} + +// Fatal logs a message at fatal level. +func Fatal(a ...interface{}) { + global.helper.Log(LevelFatal, global.helper.msgKey, fmt.Sprint(a...)) + os.Exit(1) +} + +// Fatalf logs a message at fatal level. +func Fatalf(format string, a ...interface{}) { + global.helper.Log(LevelFatal, global.helper.msgKey, fmt.Sprintf(format, a...)) + os.Exit(1) +} + +// Fatalw logs a message at fatal level. +func Fatalw(keyvals ...interface{}) { + global.helper.Log(LevelFatal, keyvals...) + os.Exit(1) +} diff --git a/log/global_test.go b/log/global_test.go new file mode 100644 index 000000000..9e2d95f12 --- /dev/null +++ b/log/global_test.go @@ -0,0 +1,68 @@ +package log + +import ( + "bytes" + "fmt" + "strings" + "testing" +) + +func TestGlobalLog(t *testing.T) { + buffer := &bytes.Buffer{} + SetLogger(NewStdLogger(buffer)) + + testCases := []struct { + level Level + content []interface{} + }{ + { + LevelDebug, + []interface{}{"test debug"}, + }, + { + LevelInfo, + []interface{}{"test info"}, + }, + { + LevelInfo, + []interface{}{"test %s", "info"}, + }, + { + LevelWarn, + []interface{}{"test warn"}, + }, + { + LevelError, + []interface{}{"test error"}, + }, + { + LevelError, + []interface{}{"test %s", "error"}, + }, + } + + expected := []string{} + for _, tc := range testCases { + msg := fmt.Sprintf(tc.content[0].(string), tc.content[1:]...) + switch tc.level { + case LevelDebug: + Debugf(tc.content[0].(string), tc.content[1:]...) + expected = append(expected, fmt.Sprintf("%s msg=%s", "DEBUG", msg)) + case LevelInfo: + Infof(tc.content[0].(string), tc.content[1:]...) + expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", msg)) + case LevelWarn: + Warnf(tc.content[0].(string), tc.content[1:]...) + expected = append(expected, fmt.Sprintf("%s msg=%s", "WARN", msg)) + case LevelError: + Errorf(tc.content[0].(string), tc.content[1:]...) + expected = append(expected, fmt.Sprintf("%s msg=%s", "ERROR", msg)) + } + } + expected = append(expected, "") + + t.Logf("Content: %s", buffer.String()) + if buffer.String() != strings.Join(expected, "\n") { + t.Errorf("Expected: %s, got: %s", strings.Join(expected, "\n"), buffer.String()) + } +} diff --git a/middleware/recovery/recovery.go b/middleware/recovery/recovery.go index 4637bf021..298d75319 100644 --- a/middleware/recovery/recovery.go +++ b/middleware/recovery/recovery.go @@ -40,7 +40,7 @@ func WithLogger(logger log.Logger) Option { // Recovery is a server middleware that recovers from any panics. func Recovery(opts ...Option) middleware.Middleware { op := options{ - logger: log.DefaultLogger, + logger: log.GetLogger(), handler: func(ctx context.Context, req, err interface{}) error { return ErrUnknownRequest }, diff --git a/middleware/recovery/recovery_test.go b/middleware/recovery/recovery_test.go index 11a9cacf8..aa77ab9f4 100644 --- a/middleware/recovery/recovery_test.go +++ b/middleware/recovery/recovery_test.go @@ -19,7 +19,7 @@ func TestOnce(t *testing.T) { next := func(ctx context.Context, req interface{}) (interface{}, error) { panic("panic reason") } - _, e := Recovery(WithLogger(log.DefaultLogger))(next)(context.Background(), "panic") + _, e := Recovery(WithLogger(log.GetLogger()))(next)(context.Background(), "panic") t.Logf("succ and reason is %v", e) } diff --git a/transport/grpc/resolver/discovery/builder.go b/transport/grpc/resolver/discovery/builder.go index de8dccf2a..d0acf646d 100644 --- a/transport/grpc/resolver/discovery/builder.go +++ b/transport/grpc/resolver/discovery/builder.go @@ -48,7 +48,7 @@ type builder struct { func NewBuilder(d registry.Discovery, opts ...Option) resolver.Builder { b := &builder{ discoverer: d, - logger: log.DefaultLogger, + logger: log.GetLogger(), timeout: time.Second * 10, insecure: false, } diff --git a/transport/grpc/resolver/discovery/resolver_test.go b/transport/grpc/resolver/discovery/resolver_test.go index e48c542c7..ae8f1b891 100644 --- a/transport/grpc/resolver/discovery/resolver_test.go +++ b/transport/grpc/resolver/discovery/resolver_test.go @@ -56,7 +56,7 @@ func TestWatch(t *testing.T) { r := &discoveryResolver{ w: &testWatch{}, cc: &testClientConn{te: t}, - log: log.NewHelper(log.DefaultLogger), + log: log.NewHelper(log.GetLogger()), ctx: ctx, cancel: cancel, insecure: false, @@ -75,7 +75,7 @@ func TestWatchError(t *testing.T) { r := &discoveryResolver{ w: &testWatch{err: errors.New("bad")}, cc: &testClientConn{te: t}, - log: log.NewHelper(log.DefaultLogger), + log: log.NewHelper(log.GetLogger()), ctx: ctx, cancel: cancel, } @@ -93,7 +93,7 @@ func TestWatchContextCancel(t *testing.T) { r := &discoveryResolver{ w: &testWatch{err: context.Canceled}, cc: &testClientConn{te: t}, - log: log.NewHelper(log.DefaultLogger), + log: log.NewHelper(log.GetLogger()), ctx: ctx, cancel: cancel, } diff --git a/transport/grpc/server.go b/transport/grpc/server.go index 0ae458d6a..c7ebbaa1e 100644 --- a/transport/grpc/server.go +++ b/transport/grpc/server.go @@ -122,7 +122,7 @@ func NewServer(opts ...ServerOption) *Server { address: ":0", timeout: 1 * time.Second, health: health.NewServer(), - log: log.NewHelper(log.DefaultLogger), + log: log.NewHelper(log.GetLogger()), } for _, o := range opts { o(srv) diff --git a/transport/http/resolver.go b/transport/http/resolver.go index b4110476b..37c2dec2f 100644 --- a/transport/http/resolver.go +++ b/transport/http/resolver.go @@ -57,7 +57,7 @@ func newResolver(ctx context.Context, discovery registry.Discovery, target *Targ r := &resolver{ target: target, watcher: watcher, - logger: log.NewHelper(log.DefaultLogger), + logger: log.NewHelper(log.GetLogger()), rebalancer: rebalancer, insecure: insecure, } diff --git a/transport/http/server.go b/transport/http/server.go index f30268cfe..8002624b9 100644 --- a/transport/http/server.go +++ b/transport/http/server.go @@ -143,7 +143,7 @@ func NewServer(opts ...ServerOption) *Server { enc: DefaultResponseEncoder, ene: DefaultErrorEncoder, strictSlash: true, - log: log.NewHelper(log.DefaultLogger), + log: log.NewHelper(log.GetLogger()), } for _, o := range opts { o(srv)