From a631297a0eee925945dddef00e42490d0a859b1d Mon Sep 17 00:00:00 2001 From: zhaoshukai Date: Fri, 8 Oct 2021 09:54:00 +0800 Subject: [PATCH] add a new logger type, log in file --- cmd/cmd.go | 46 ++-- go.mod | 3 + go.sum | 7 + service/logger/default.go | 408 ++++++++++++++++---------------- service/logger/helper.go | 273 +++++++++++---------- service/logger/level.go | 292 ++++++++++++----------- service/logger/logByLogrus.go | 226 ++++++++++++++++++ service/logger/logger.go | 167 ++++++++----- service/logger/logger_test.go | 90 +++---- service/logger/option4logrus.go | 56 +++++ 10 files changed, 975 insertions(+), 593 deletions(-) create mode 100644 service/logger/logByLogrus.go create mode 100644 service/logger/option4logrus.go diff --git a/cmd/cmd.go b/cmd/cmd.go index cfb661c7..e67d3d77 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -15,19 +15,19 @@ import ( "time" "github.com/micro-community/micro/v3/cmd/cli/util" - "github.com/micro-community/micro/v3/plugin" _ "github.com/micro-community/micro/v3/cmd/usage" + "github.com/micro-community/micro/v3/plugin" "github.com/micro-community/micro/v3/profile" "github.com/micro-community/micro/v3/service/auth" "github.com/micro-community/micro/v3/service/broker" "github.com/micro-community/micro/v3/service/client" "github.com/micro-community/micro/v3/service/config" "github.com/micro-community/micro/v3/service/logger" + "github.com/micro-community/micro/v3/service/network" "github.com/micro-community/micro/v3/service/registry" "github.com/micro-community/micro/v3/service/runtime" "github.com/micro-community/micro/v3/service/server" "github.com/micro-community/micro/v3/service/store" - "github.com/micro-community/micro/v3/service/network" mConfigCli "github.com/micro-community/micro/v3/service/config/client" mConfigStore "github.com/micro-community/micro/v3/service/config/store" @@ -329,7 +329,7 @@ func (c *command) Options() Options { // Before is executed before any subcommand func (c *command) Before(ctx *cli.Context) error { - //micro server or micro service .. + //micro server or micro service .. // set the config file if specified if cf := ctx.String("c"); len(cf) > 0 { inConfig.SetConfig(cf) @@ -393,25 +393,25 @@ func (c *command) Before(ctx *cli.Context) error { ) // default wrapper if ctx.Bool("default_wrapper") { - onceBefore.Do(func() { - // wrap the client - client.DefaultClient = wrapper.AuthClient(client.DefaultClient) - // client.DefaultClient = wrapper.CacheClient(client.DefaultClient) - client.DefaultClient = wrapper.TraceCall(client.DefaultClient) - // client.DefaultClient = wrapper.FromService(client.DefaultClient) - client.DefaultClient = wrapper.LogClient(client.DefaultClient) - client.DefaultClient = wrapper.OpentraceClient(client.DefaultClient) - - // wrap the server - server.DefaultServer.Init( - server.WrapHandler(wrapper.AuthHandler()), - server.WrapHandler(wrapper.TraceHandler()), - server.WrapHandler(wrapper.HandlerStats()), - server.WrapHandler(wrapper.LogHandler()), - server.WrapHandler(wrapper.MetricsHandler()), - server.WrapHandler(wrapper.OpenTraceHandler()), - ) - }) + onceBefore.Do(func() { + // wrap the client + client.DefaultClient = wrapper.AuthClient(client.DefaultClient) + // client.DefaultClient = wrapper.CacheClient(client.DefaultClient) + client.DefaultClient = wrapper.TraceCall(client.DefaultClient) + // client.DefaultClient = wrapper.FromService(client.DefaultClient) + client.DefaultClient = wrapper.LogClient(client.DefaultClient) + client.DefaultClient = wrapper.OpentraceClient(client.DefaultClient) + + // wrap the server + server.DefaultServer.Init( + server.WrapHandler(wrapper.AuthHandler()), + server.WrapHandler(wrapper.TraceHandler()), + server.WrapHandler(wrapper.HandlerStats()), + server.WrapHandler(wrapper.LogHandler()), + server.WrapHandler(wrapper.MetricsHandler()), + server.WrapHandler(wrapper.OpenTraceHandler()), + ) + }) } @@ -622,6 +622,8 @@ func Register(cmds ...*cli.Command) { // Run the default command func Run() { + // set logger type here, otherwise, use default + //logger.SetLogger("logrus", nil) if err := DefaultCmd.Run(); err != nil { fmt.Println(formatErr(err)) os.Exit(1) diff --git a/go.mod b/go.mod index 72e6b5cb..4960eba5 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,8 @@ require ( github.com/hpcloud/tail v1.0.0 github.com/improbable-eng/grpc-web v0.14.0 github.com/kr/pretty v0.2.1 + github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible + github.com/lestrrat-go/strftime v1.0.5 // indirect github.com/miekg/dns v1.1.42 github.com/nightlyone/lockfile v1.0.0 github.com/olekukonko/tablewriter v0.0.5 @@ -36,6 +38,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rs/cors v1.7.0 // indirect github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e + github.com/sirupsen/logrus v1.4.2 github.com/stoewer/go-strcase v1.2.0 github.com/stretchr/objx v0.3.0 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 4ecbc0fb..ec1fe20f 100644 --- a/go.sum +++ b/go.sum @@ -273,6 +273,7 @@ github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -286,6 +287,11 @@ github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HK github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= +github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= +github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= +github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -389,6 +395,7 @@ github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e/go.mod h1:Yow6lPLS github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= diff --git a/service/logger/default.go b/service/logger/default.go index 5b11cddf..431c9585 100644 --- a/service/logger/default.go +++ b/service/logger/default.go @@ -1,199 +1,209 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Original source: github.com/micro/go-micro/v3/logger/default.go - -package logger - -import ( - "context" - "fmt" - "os" - "runtime" - "sort" - "strings" - "sync" - "time" - - dlog "github.com/micro-community/micro/v3/service/debug/log" -) - -func init() { - lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL")) - if err != nil { - lvl = InfoLevel - } - - DefaultLogger = NewHelper(NewLogger(WithLevel(lvl))) -} - -type defaultLogger struct { - sync.RWMutex - opts Options -} - -// Init(opts...) should only overwrite provided options -func (l *defaultLogger) Init(opts ...Option) error { - for _, o := range opts { - o(&l.opts) - } - return nil -} - -func (l *defaultLogger) String() string { - return "default" -} - -func (l *defaultLogger) Fields(fields map[string]interface{}) Logger { - l.Lock() - l.opts.Fields = copyFields(fields) - l.Unlock() - return l -} - -func copyFields(src map[string]interface{}) map[string]interface{} { - dst := make(map[string]interface{}, len(src)) - for k, v := range src { - dst[k] = v - } - return dst -} - -// logCallerfilePath returns a package/file:line description of the caller, -// preserving only the leaf directory name and file name. -func logCallerfilePath(loggingFilePath string) string { - // To make sure we trim the path correctly on Windows too, we - // counter-intuitively need to use '/' and *not* os.PathSeparator here, - // because the path given originates from Go stdlib, specifically - // runtime.Caller() which (as of Mar/17) returns forward slashes even on - // Windows. - // - // See https://github.com/golang/go/issues/3335 - // and https://github.com/golang/go/issues/18151 - // - // for discussion on the issue on Go side. - idx := strings.LastIndexByte(loggingFilePath, '/') - if idx == -1 { - return loggingFilePath - } - idx = strings.LastIndexByte(loggingFilePath[:idx], '/') - if idx == -1 { - return loggingFilePath - } - return loggingFilePath[idx+1:] -} - -func (l *defaultLogger) Log(level Level, v ...interface{}) { - // TODO decide does we need to write message if log level not used? - if !l.opts.Level.Enabled(level) { - return - } - - l.RLock() - fields := copyFields(l.opts.Fields) - l.RUnlock() - - fields["level"] = level.String() - - if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { - fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) - } - - rec := dlog.Record{ - Timestamp: time.Now(), - Message: fmt.Sprint(v...), - Metadata: make(map[string]string, len(fields)), - } - - keys := make([]string, 0, len(fields)) - for k, v := range fields { - keys = append(keys, k) - rec.Metadata[k] = fmt.Sprintf("%v", v) - } - - sort.Strings(keys) - metadata := "" - - for _, k := range keys { - metadata += fmt.Sprintf(" %s=%v", k, fields[k]) - } - - t := rec.Timestamp.Format("2006-01-02 15:04:05") - fmt.Fprintf(l.opts.Out, "%s %s %v\n", t, metadata, rec.Message) -} - -func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) { - // TODO decide does we need to write message if log level not used? - if level < l.opts.Level { - return - } - - l.RLock() - fields := copyFields(l.opts.Fields) - l.RUnlock() - - fields["level"] = level.String() - - if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { - fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file), line) - } - - rec := dlog.Record{ - Timestamp: time.Now(), - Message: fmt.Sprintf(format, v...), - Metadata: make(map[string]string, len(fields)), - } - - keys := make([]string, 0, len(fields)) - for k, v := range fields { - keys = append(keys, k) - rec.Metadata[k] = fmt.Sprintf("%v", v) - } - - sort.Strings(keys) - metadata := "" - - for _, k := range keys { - metadata += fmt.Sprintf(" %s=%v", k, fields[k]) - } - - t := rec.Timestamp.Format("2006-01-02 15:04:05") - fmt.Fprintf(l.opts.Out, "%s %s %v\n", t, metadata, rec.Message) -} - -func (l *defaultLogger) Options() Options { - // not guard against options Context values - l.RLock() - opts := l.opts - opts.Fields = copyFields(l.opts.Fields) - l.RUnlock() - return opts -} - -// NewLogger builds a new logger based on options -func NewLogger(opts ...Option) Logger { - // Default options - options := Options{ - Level: InfoLevel, - Fields: make(map[string]interface{}), - Out: os.Stderr, - CallerSkipCount: 2, - Context: context.Background(), - } - - l := &defaultLogger{opts: options} - if err := l.Init(opts...); err != nil { - l.Log(FatalLevel, err) - } - - return l -} +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Original source: github.com/micro/go-micro/v3/logger/default.go + +package logger + +import ( + "context" + "fmt" + "os" + "runtime" + "sort" + "strings" + "sync" + "time" + + dlog "github.com/micro-community/micro/v3/service/debug/log" +) + +// func init() { +// lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL")) +// if err != nil { +// lvl = InfoLevel +// } + +// DefaultLogger = NewHelper(NewLogger(WithLevel(lvl))) +// } + +type defaultLogger struct { + sync.RWMutex + opts Options +} + +// Init(opts...) should only overwrite provided options +func (l *defaultLogger) Init(opts ...interface{}) error { + for _, o := range opts { + if o1, ok := o.(Option); ok { + o1(&l.opts) + } + } + return nil +} + +func (l *defaultLogger) String() string { + return "default" +} + +func (l *defaultLogger) Fields(fields map[string]interface{}) Logger { + l.Lock() + l.opts.Fields = copyFields(fields) + l.Unlock() + return l +} + +func copyFields(src map[string]interface{}) map[string]interface{} { + dst := make(map[string]interface{}, len(src)) + for k, v := range src { + dst[k] = v + } + return dst +} + +// logCallerfilePath returns a package/file:line description of the caller, +// preserving only the leaf directory name and file name. +func logCallerfilePath(loggingFilePath string, isFile bool) string { + // To make sure we trim the path correctly on Windows too, we + // counter-intuitively need to use '/' and *not* os.PathSeparator here, + // because the path given originates from Go stdlib, specifically + // runtime.Caller() which (as of Mar/17) returns forward slashes even on + // Windows. + // + // See https://github.com/golang/go/issues/3335 + // and https://github.com/golang/go/issues/18151 + // + // for discussion on the issue on Go side. + idx := strings.LastIndexByte(loggingFilePath, '/') + if idx == -1 { + return loggingFilePath + } + // if isFile is not true, used by get method + if !isFile { + return loggingFilePath[idx+1:] + } + idx = strings.LastIndexByte(loggingFilePath[:idx], '/') + if idx == -1 { + return loggingFilePath + } + return loggingFilePath[idx+1:] +} + +func (l *defaultLogger) Log(level Level, v ...interface{}) { + // TODO decide does we need to write message if log level not used? + if !l.opts.Level.Enabled(level) { + return + } + + l.RLock() + fields := copyFields(l.opts.Fields) + l.RUnlock() + + fields["level"] = level.String() + + if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { + fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file, true), line) + } + + rec := dlog.Record{ + Timestamp: time.Now(), + Message: fmt.Sprint(v...), + Metadata: make(map[string]string, len(fields)), + } + + keys := make([]string, 0, len(fields)) + for k, v := range fields { + keys = append(keys, k) + rec.Metadata[k] = fmt.Sprintf("%v", v) + } + + sort.Strings(keys) + metadata := "" + + for _, k := range keys { + metadata += fmt.Sprintf(" %s=%v", k, fields[k]) + } + + t := rec.Timestamp.Format("2006-01-02 15:04:05") + fmt.Fprintf(l.opts.Out, "%s %s %v\n", t, metadata, rec.Message) +} + +func (l *defaultLogger) Logf(level Level, format string, v ...interface{}) { + // TODO decide does we need to write message if log level not used? + if level < l.opts.Level { + return + } + + l.RLock() + fields := copyFields(l.opts.Fields) + l.RUnlock() + + fields["level"] = level.String() + + if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok { + fields["file"] = fmt.Sprintf("%s:%d", logCallerfilePath(file, true), line) + } + + rec := dlog.Record{ + Timestamp: time.Now(), + Message: fmt.Sprintf(format, v...), + Metadata: make(map[string]string, len(fields)), + } + + keys := make([]string, 0, len(fields)) + for k, v := range fields { + keys = append(keys, k) + rec.Metadata[k] = fmt.Sprintf("%v", v) + } + + sort.Strings(keys) + metadata := "" + + for _, k := range keys { + metadata += fmt.Sprintf(" %s=%v", k, fields[k]) + } + + t := rec.Timestamp.Format("2006-01-02 15:04:05") + fmt.Fprintf(l.opts.Out, "%s %s %v\n", t, metadata, rec.Message) +} + +func (l *defaultLogger) Options() interface{} { + // not guard against options Context values + l.RLock() + opts := l.opts + opts.Fields = copyFields(l.opts.Fields) + l.RUnlock() + return opts +} + +// NewLogger builds a new logger based on options +func NewLogger(opts ...Option) Logger { + // Default options + options := Options{ + Level: InfoLevel, + Fields: make(map[string]interface{}), + Out: os.Stderr, + CallerSkipCount: 2, + Context: context.Background(), + } + + l := &defaultLogger{opts: options} + var o []interface{} + for _, item := range opts { + o = append(o, item) + } + if err := l.Init(o...); err != nil { + l.Log(FatalLevel, err) + } + + return l +} diff --git a/service/logger/helper.go b/service/logger/helper.go index 96924b4c..520d7b8c 100644 --- a/service/logger/helper.go +++ b/service/logger/helper.go @@ -1,130 +1,143 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Original source: github.com/micro/go-micro/v3/logger/helper.go - -package logger - -import ( - "os" -) - -//Helper for logger -type Helper struct { - Logger - fields map[string]interface{} -} - -func NewHelper(log Logger) *Helper { - return &Helper{Logger: log} -} - -func (h *Helper) Info(args ...interface{}) { - if !h.Logger.Options().Level.Enabled(InfoLevel) { - return - } - h.Logger.Fields(h.fields).Log(InfoLevel, args...) -} - -func (h *Helper) Infof(template string, args ...interface{}) { - if !h.Logger.Options().Level.Enabled(InfoLevel) { - return - } - h.Logger.Fields(h.fields).Logf(InfoLevel, template, args...) -} - -func (h *Helper) Trace(args ...interface{}) { - if !h.Logger.Options().Level.Enabled(TraceLevel) { - return - } - h.Logger.Fields(h.fields).Log(TraceLevel, args...) -} - -func (h *Helper) Tracef(template string, args ...interface{}) { - if !h.Logger.Options().Level.Enabled(TraceLevel) { - return - } - h.Logger.Fields(h.fields).Logf(TraceLevel, template, args...) -} - -func (h *Helper) Debug(args ...interface{}) { - if !h.Logger.Options().Level.Enabled(DebugLevel) { - return - } - h.Logger.Fields(h.fields).Log(DebugLevel, args...) -} - -func (h *Helper) Debugf(template string, args ...interface{}) { - if !h.Logger.Options().Level.Enabled(DebugLevel) { - return - } - h.Logger.Fields(h.fields).Logf(DebugLevel, template, args...) -} - -func (h *Helper) Warn(args ...interface{}) { - if !h.Logger.Options().Level.Enabled(WarnLevel) { - return - } - h.Logger.Fields(h.fields).Log(WarnLevel, args...) -} - -func (h *Helper) Warnf(template string, args ...interface{}) { - if !h.Logger.Options().Level.Enabled(WarnLevel) { - return - } - h.Logger.Fields(h.fields).Logf(WarnLevel, template, args...) -} - -func (h *Helper) Error(args ...interface{}) { - if !h.Logger.Options().Level.Enabled(ErrorLevel) { - return - } - h.Logger.Fields(h.fields).Log(ErrorLevel, args...) -} - -func (h *Helper) Errorf(template string, args ...interface{}) { - if !h.Logger.Options().Level.Enabled(ErrorLevel) { - return - } - h.Logger.Fields(h.fields).Logf(ErrorLevel, template, args...) -} - -func (h *Helper) Fatal(args ...interface{}) { - if !h.Logger.Options().Level.Enabled(FatalLevel) { - return - } - h.Logger.Fields(h.fields).Log(FatalLevel, args...) - os.Exit(1) -} - -func (h *Helper) Fatalf(template string, args ...interface{}) { - if !h.Logger.Options().Level.Enabled(FatalLevel) { - return - } - h.Logger.Fields(h.fields).Logf(FatalLevel, template, args...) - os.Exit(1) -} - -func (h *Helper) WithError(err error) *Helper { - fields := copyFields(h.fields) - fields["error"] = err - return &Helper{Logger: h.Logger, fields: fields} -} - -//WithFields write logger fields -func (h *Helper) WithFields(fields map[string]interface{}) *Helper { - nFields := copyFields(fields) - for k, v := range h.fields { - nFields[k] = v - } - return &Helper{Logger: h.Logger, fields: nFields} -} +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Original source: github.com/micro/go-micro/v3/logger/helper.go + +package logger + +import ( + "os" +) + +//Helper for logger +type Helper struct { + Logger + fields map[string]interface{} +} + +func NewHelper(log Logger) *Helper { + return &Helper{Logger: log} +} + +func (h *Helper) isLevel() bool { + if h.Logger.String() == "default" { + if o, ok := h.Logger.Options().(Options); ok { + if !o.Level.Enabled(InfoLevel) { + return false + } + } else { + return false + } + } + return true +} + +func (h *Helper) Info(args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Log(InfoLevel, args...) +} + +func (h *Helper) Infof(template string, args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Logf(InfoLevel, template, args...) +} + +func (h *Helper) Trace(args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Log(TraceLevel, args...) +} + +func (h *Helper) Tracef(template string, args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Logf(TraceLevel, template, args...) +} + +func (h *Helper) Debug(args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Log(DebugLevel, args...) +} + +func (h *Helper) Debugf(template string, args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Logf(DebugLevel, template, args...) +} + +func (h *Helper) Warn(args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Log(WarnLevel, args...) +} + +func (h *Helper) Warnf(template string, args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Logf(WarnLevel, template, args...) +} + +func (h *Helper) Error(args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Log(ErrorLevel, args...) +} + +func (h *Helper) Errorf(template string, args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Logf(ErrorLevel, template, args...) +} + +func (h *Helper) Fatal(args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Log(FatalLevel, args...) + os.Exit(1) +} + +func (h *Helper) Fatalf(template string, args ...interface{}) { + if !h.isLevel() { + return + } + h.Logger.Fields(h.fields).Logf(FatalLevel, template, args...) + os.Exit(1) +} + +func (h *Helper) WithError(err error) *Helper { + fields := copyFields(h.fields) + fields["error"] = err + return &Helper{Logger: h.Logger, fields: fields} +} + +//WithFields write logger fields +func (h *Helper) WithFields(fields map[string]interface{}) *Helper { + nFields := copyFields(fields) + for k, v := range h.fields { + nFields[k] = v + } + return &Helper{Logger: h.Logger, fields: nFields} +} diff --git a/service/logger/level.go b/service/logger/level.go index ce751439..fd4dc159 100644 --- a/service/logger/level.go +++ b/service/logger/level.go @@ -1,140 +1,152 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Original source: github.com/micro/go-micro/v3/logger/level.go - -package logger - -import ( - "fmt" - "os" -) - -type Level int8 - -const ( - // TraceLevel level. Designates finer-grained informational events than the Debug. - TraceLevel Level = iota - 2 - // DebugLevel level. Usually only enabled when debugging. Very verbose logging. - DebugLevel - // InfoLevel is the default logging priority. - // General operational entries about what's going on inside the application. - InfoLevel - // WarnLevel level. Non-critical entries that deserve eyes. - WarnLevel - // ErrorLevel level. Logs. Used for errors that should definitely be noted. - ErrorLevel - // FatalLevel level. Logs and then calls `logger.Exit(1)`. highest level of severity. - FatalLevel -) - -func (l Level) String() string { - switch l { - case TraceLevel: - return "trace" - case DebugLevel: - return "debug" - case InfoLevel: - return "info" - case WarnLevel: - return "warn" - case ErrorLevel: - return "error" - case FatalLevel: - return "fatal" - } - return "" -} - -// Enabled returns true if the given level is at or above this level. -func (l Level) Enabled(lvl Level) bool { - return lvl >= l -} - -// GetLevel converts a level string into a logger Level value. -// returns an error if the input string does not match known values. -func GetLevel(levelStr string) (Level, error) { - switch levelStr { - case TraceLevel.String(): - return TraceLevel, nil - case DebugLevel.String(): - return DebugLevel, nil - case InfoLevel.String(): - return InfoLevel, nil - case WarnLevel.String(): - return WarnLevel, nil - case ErrorLevel.String(): - return ErrorLevel, nil - case FatalLevel.String(): - return FatalLevel, nil - } - return InfoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to InfoLevel", levelStr) -} - -func Info(args ...interface{}) { - DefaultLogger.Log(InfoLevel, args...) -} - -func Infof(template string, args ...interface{}) { - DefaultLogger.Logf(InfoLevel, template, args...) -} - -func Trace(args ...interface{}) { - DefaultLogger.Log(TraceLevel, args...) -} - -func Tracef(template string, args ...interface{}) { - DefaultLogger.Logf(TraceLevel, template, args...) -} - -func Debug(args ...interface{}) { - DefaultLogger.Log(DebugLevel, args...) -} - -func Debugf(template string, args ...interface{}) { - DefaultLogger.Logf(DebugLevel, template, args...) -} - -func Warn(args ...interface{}) { - DefaultLogger.Log(WarnLevel, args...) -} - -func Warnf(template string, args ...interface{}) { - DefaultLogger.Logf(WarnLevel, template, args...) -} - -func Error(args ...interface{}) { - DefaultLogger.Log(ErrorLevel, args...) -} - -func Errorf(template string, args ...interface{}) { - DefaultLogger.Logf(ErrorLevel, template, args...) -} - -func Fatal(args ...interface{}) { - DefaultLogger.Log(FatalLevel, args...) - os.Exit(1) -} - -func Fatalf(template string, args ...interface{}) { - DefaultLogger.Logf(FatalLevel, template, args...) - os.Exit(1) -} - -// Returns true if the given level is at or lower the current logger level -func V(lvl Level, log Logger) bool { - l := DefaultLogger - if log != nil { - l = log - } - return l.Options().Level <= lvl -} +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Original source: github.com/micro/go-micro/v3/logger/level.go + +package logger + +import ( + "fmt" + "os" +) + +type Level int8 + +const ( + // TraceLevel level. Designates finer-grained informational events than the Debug. + TraceLevel Level = iota - 2 + // DebugLevel level. Usually only enabled when debugging. Very verbose logging. + DebugLevel + // InfoLevel is the default logging priority. + // General operational entries about what's going on inside the application. + InfoLevel + // WarnLevel level. Non-critical entries that deserve eyes. + WarnLevel + // ErrorLevel level. Logs. Used for errors that should definitely be noted. + ErrorLevel + // FatalLevel level. Logs and then calls `logger.Exit(1)`. highest level of severity. + FatalLevel +) + +func (l Level) String() string { + switch l { + case TraceLevel: + return "trace" + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warn" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + } + return "" +} + +// Enabled returns true if the given level is at or above this level. +func (l Level) Enabled(lvl Level) bool { + return lvl >= l +} + +// GetLevel converts a level string into a logger Level value. +// returns an error if the input string does not match known values. +func GetLevel(levelStr string) (Level, error) { + switch levelStr { + case TraceLevel.String(): + return TraceLevel, nil + case DebugLevel.String(): + return DebugLevel, nil + case InfoLevel.String(): + return InfoLevel, nil + case WarnLevel.String(): + return WarnLevel, nil + case ErrorLevel.String(): + return ErrorLevel, nil + case FatalLevel.String(): + return FatalLevel, nil + } + return InfoLevel, fmt.Errorf("Unknown Level String: '%s', defaulting to InfoLevel", levelStr) +} + +func Info(args ...interface{}) { + DefaultLogger.Log(InfoLevel, args...) +} + +func Infof(template string, args ...interface{}) { + DefaultLogger.Logf(InfoLevel, template, args...) +} + +func Trace(args ...interface{}) { + DefaultLogger.Log(TraceLevel, args...) +} + +func Tracef(template string, args ...interface{}) { + DefaultLogger.Logf(TraceLevel, template, args...) +} + +func Debug(args ...interface{}) { + DefaultLogger.Log(DebugLevel, args...) +} + +func Debugf(template string, args ...interface{}) { + DefaultLogger.Logf(DebugLevel, template, args...) +} + +func Warn(args ...interface{}) { + DefaultLogger.Log(WarnLevel, args...) +} + +func Warnf(template string, args ...interface{}) { + DefaultLogger.Logf(WarnLevel, template, args...) +} + +func Error(args ...interface{}) { + DefaultLogger.Log(ErrorLevel, args...) +} + +func Errorf(template string, args ...interface{}) { + DefaultLogger.Logf(ErrorLevel, template, args...) +} + +func Fatal(args ...interface{}) { + DefaultLogger.Log(FatalLevel, args...) + os.Exit(1) +} + +func Fatalf(template string, args ...interface{}) { + DefaultLogger.Logf(FatalLevel, template, args...) + os.Exit(1) +} + +// Returns true if the given level is at or lower the current logger level +func V(lvl Level, log Logger) bool { + l := DefaultLogger + if log != nil { + l = log + } + + if log.String() == "default" { + if o, ok := l.Options().(Options); ok { + // if !o.Level.Enabled(InfoLevel) { + // return false + // } + return o.Level <= lvl + } else { + return true + } + } + return true + //return l.Options().Level <= lvl +} diff --git a/service/logger/logByLogrus.go b/service/logger/logByLogrus.go new file mode 100644 index 00000000..c102f33b --- /dev/null +++ b/service/logger/logByLogrus.go @@ -0,0 +1,226 @@ +package logger + +import ( + "bytes" + "fmt" + "log" + "os" + "runtime" + "strings" + "time" + + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/sirupsen/logrus" +) + +type logrusLogger struct { + opts LogrusOptions + log *logrus.Logger +} + +func (l *logrusLogger) Init(opts ...interface{}) error { + for _, o := range opts { + if o1, ok := o.(LogrusOption); ok { + o1(&l.opts) + } + } + return nil +} + +func (l *logrusLogger) String() string { + return "logrus" +} + +func (l *logrusLogger) Fields(fields map[string]interface{}) Logger { + + return l +} + +func (l *logrusLogger) Log(level Level, v ...interface{}) { + file, method, line := getCallerInfo() + entry := l.log.WithFields(logrus.Fields{ + "ServiceName": l.opts.Service, + "File": fmt.Sprintf("%s:%d", logCallerfilePath(file, true), line), + "Method": logCallerfilePath(method, false), + }) + switch level { + case TraceLevel: + go entry.Trace(v) + case DebugLevel: + go entry.Debug(v) + case InfoLevel: + go entry.Info(v) + case WarnLevel: + go entry.Warn(v) + case ErrorLevel: + go entry.Error(v) + case FatalLevel: + go entry.Fatal(v) + default: + go entry.Info(v) + } +} + +func (l *logrusLogger) Logf(level Level, format string, v ...interface{}) { + file, method, line := getCallerInfo() + entry := l.log.WithFields(logrus.Fields{ + "ServiceName": l.opts.Service, + "File": fmt.Sprintf("%s:%d", logCallerfilePath(file, true), line), + "Method": logCallerfilePath(method, false), + }) + switch level { + case TraceLevel: + go entry.Tracef(format, v) + case DebugLevel: + go entry.Debugf(format, v) + case InfoLevel: + go entry.Infof(format, v) + case WarnLevel: + go entry.Warnf(format, v) + case ErrorLevel: + go entry.Errorf(format, v) + case FatalLevel: + go entry.Fatalf(format, v) + default: + go entry.Infof(format, v) + } +} + +func (l *logrusLogger) Options() interface{} { + return l.opts +} + +func NewLogrusLogger(opts *LogrusOptions) Logger { + // Default logrus options + l := logrus.New() + if len(opts.Path) == 0 { + opts.Path = "./logs" + } + if _, err := os.Stat(opts.Path); err != nil { + if os.IsNotExist(err) { + if err = os.Mkdir(opts.Path, os.ModePerm); err != nil { + log.Printf("make dir error. err : %s", err) + } + } else { + log.Printf("test dir is exist occurs error. err : %s", err) + } + } + configRotateLogs(opts, l) + + configLevel(strings.ToUpper(opts.Level.String()), l) + + if opts.Format == "json" { + l.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05.000"}) + } else if opts.Format == "text" { + l.SetFormatter(&IRFormatter{TimestampFormat: "2006-01-02 15:04:05.000"}) + } else { + l.SetFormatter(&logrus.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05.000"}) + } + + log := &logrusLogger{ + opts: *opts, + log: l, + } + return log +} + +// 配置日志文件切割 +func configRotateLogs(option *LogrusOptions, l *logrus.Logger) error { + var opts []rotatelogs.Option + if !strings.HasSuffix(option.Path, "/") { + option.Path += "/" + } + if option.MaxAge == 0 { + option.MaxAge = 3 + } + path := option.Path + switch option.Rotation { + case "M": + path = path + "%Y%m.log" + opts = append(opts, rotatelogs.WithMaxAge(time.Duration(option.MaxAge*30*24)*time.Hour)) + opts = append(opts, rotatelogs.WithRotationTime(time.Duration(30*24)*time.Hour)) + case "H": + path = path + "%Y%m%d%H.log" + opts = append(opts, rotatelogs.WithMaxAge(time.Duration(option.MaxAge)*time.Hour)) + opts = append(opts, rotatelogs.WithRotationTime(time.Hour)) + default: + path = path + "%Y%m%d.log" + opts = append(opts, rotatelogs.WithMaxAge(time.Duration(option.MaxAge*24)*time.Hour)) + opts = append(opts, rotatelogs.WithRotationTime(time.Duration(24)*time.Hour)) + } + if runtime.GOOS == "linux" { // windows 平台不能创建 + opts = append(opts, rotatelogs.WithLinkName(option.Path+option.Service+".log")) + } + writer, err := rotatelogs.New( + path, + opts...) + + if err != nil { + return err + } + l.SetOutput(writer) + return nil +} + +// 配置日志级别 +func configLevel(level string, l *logrus.Logger) { + switch level { + case "TRACE": + l.SetLevel(logrus.TraceLevel) + case "DEBUG": + l.SetLevel(logrus.DebugLevel) + case "INFO": + l.SetLevel(logrus.InfoLevel) + case "WARN": + l.SetLevel(logrus.WarnLevel) + case "ERROR": + l.SetLevel(logrus.ErrorLevel) + case "FATAL": + l.SetLevel(logrus.FatalLevel) + case "PANIC": + l.SetLevel(logrus.PanicLevel) + default: + l.SetLevel(logrus.InfoLevel) + } +} + +// 获取调用者的文件名,方法名,行数 +func getCallerInfo() (string, string, int) { + if pc, file, line, ok := runtime.Caller(3); ok { + method := runtime.FuncForPC(pc).Name() + return file, method, line + } + return "", "", 0 +} + +type IRFormatter struct { + TimestampFormat string +} + +func (ir *IRFormatter) Format(entity *logrus.Entry) ([]byte, error) { + var b *bytes.Buffer + if entity.Buffer != nil { + b = entity.Buffer + } else { + b = &bytes.Buffer{} + } + var service, file, method string = "", "", "" + var line int + if entity.Data != nil { + if v, ok := entity.Data["Service"]; ok { + service = v.(string) + } + if v, ok := entity.Data["File"]; ok { + file = v.(string) + } + if v, ok := entity.Data["Method"]; ok { + method = v.(string) + } + if v, ok := entity.Data["Line"]; ok { + line = v.(int) + } + } + b.WriteString(fmt.Sprintf("%s...,[%s],{%s-%s--%s():%d},----%s", time.Now().Format(ir.TimestampFormat), strings.ToUpper(entity.Level.String()), service, file, method, line, entity.Message)) + b.WriteString("\n") + return b.Bytes(), nil +} diff --git a/service/logger/logger.go b/service/logger/logger.go index b1594dc0..df5d15e7 100644 --- a/service/logger/logger.go +++ b/service/logger/logger.go @@ -1,57 +1,110 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Original source: github.com/micro/go-micro/v3/logger/logger.go - -// Package log provides a log interface -package logger - -var ( - // Default logger - DefaultLogger Logger = NewHelper(NewLogger()) -) - -// Logger is a generic logging interface -type Logger interface { - // Init initializes options - Init(options ...Option) error - // The Logger options - Options() Options - // Fields set fields to always be logged - Fields(fields map[string]interface{}) Logger - // Log writes a log entry - Log(level Level, v ...interface{}) - // Logf writes a formatted log entry - Logf(level Level, format string, v ...interface{}) - // String returns the name of logger - String() string -} - -func Init(opts ...Option) error { - return DefaultLogger.Init(opts...) -} - -func Fields(fields map[string]interface{}) Logger { - return DefaultLogger.Fields(fields) -} - -func Log(level Level, v ...interface{}) { - DefaultLogger.Log(level, v...) -} - -func Logf(level Level, format string, v ...interface{}) { - DefaultLogger.Logf(level, format, v...) -} - -func String() string { - return DefaultLogger.String() -} +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Original source: github.com/micro/go-micro/v3/logger/logger.go + +// Package log provides a log interface +package logger + +import "os" + +var ( + DefaultLogger Logger +) + +func init() { + lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL")) + if err != nil { + lvl = InfoLevel + } + DefaultLogger = NewHelper(NewLogger(WithLevel(lvl))) + // if LogType == "default" { + // DefaultLogger = NewHelper(NewLogger(WithLevel(lvl))) + // } else { + // opts := &LogrusOptions{ + // Path: "./logs", + // Service: "go-service", + // Rotation: "D", + // MaxAge: 3, + // Level: lvl, + // Format: "json", + // } + // DefaultLogger = NewLogrusLogger(opts) + // } + +} + +func SetLogger(logType string, opts interface{}) { + lvl, err := GetLevel(os.Getenv("MICRO_LOG_LEVEL")) + if err != nil { + lvl = InfoLevel + } + if logType == "default" { + if opts != nil { + if o, ok := opts.(Options); ok { + lvl = o.Level + } + } + DefaultLogger = NewHelper(NewLogger(WithLevel(lvl))) + } else { + var o LogrusOptions = LogrusOptions{ + Path: "./logs", + Service: "go-service", + Rotation: "D", + MaxAge: 3, + Level: lvl, + Format: "json", + } + if opts != nil { + if o1, ok := opts.(LogrusOptions); ok { + o = o1 + } + } + DefaultLogger = NewLogrusLogger(&o) + } +} + +// Logger is a generic logging interface +type Logger interface { + // Init initializes options + Init(options ...interface{}) error + // The Logger options + Options() interface{} + // Fields set fields to always be logged + Fields(fields map[string]interface{}) Logger + // Log writes a log entry + Log(level Level, v ...interface{}) + // Logf writes a formatted log entry + Logf(level Level, format string, v ...interface{}) + // String returns the name of logger + String() string +} + +func Init(opts ...interface{}) error { + return DefaultLogger.Init(opts...) +} + +func Fields(fields map[string]interface{}) Logger { + return DefaultLogger.Fields(fields) +} + +func Log(level Level, v ...interface{}) { + DefaultLogger.Log(level, v...) +} + +func Logf(level Level, format string, v ...interface{}) { + DefaultLogger.Logf(level, format, v...) +} + +func String() string { + return DefaultLogger.String() +} diff --git a/service/logger/logger_test.go b/service/logger/logger_test.go index c9716723..2febdcdf 100644 --- a/service/logger/logger_test.go +++ b/service/logger/logger_test.go @@ -1,45 +1,45 @@ -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Original source: github.com/micro/go-micro/v3/logger/logger_test.go - -package logger - -import ( - "bufio" - "bytes" - "strings" - "testing" -) - -func TestLogger(t *testing.T) { - l := NewLogger(WithLevel(TraceLevel)) - h1 := NewHelper(l).WithFields(map[string]interface{}{"key1": "val1"}) - h1.Trace("trace_msg1") - h1.Warn("warn_msg1") - - h2 := NewHelper(l).WithFields(map[string]interface{}{"key2": "val2"}) - h2.Trace("trace_msg2") - h2.Warn("warn_msg2") - - l.Fields(map[string]interface{}{"key3": "val4"}).Log(InfoLevel, "test_msg") -} - -func TestLoggerRedirection(t *testing.T) { - var b bytes.Buffer - wr := bufio.NewWriter(&b) - NewLogger(WithOutput(wr)).Logf(InfoLevel, "test message") - wr.Flush() - if !strings.Contains(b.String(), "level=info test message") { - t.Fatalf("Redirection failed, received '%s'", b.String()) - } -} +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Original source: github.com/micro/go-micro/v3/logger/logger_test.go + +package logger + +import ( + "bufio" + "bytes" + "strings" + "testing" +) + +func TestLogger(t *testing.T) { + l := NewLogger(WithLevel(TraceLevel)) + h1 := NewHelper(l).WithFields(map[string]interface{}{"key1": "val1"}) + h1.Trace("trace_msg1") + h1.Warn("warn_msg1") + + h2 := NewHelper(l).WithFields(map[string]interface{}{"key2": "val2"}) + h2.Trace("trace_msg2") + h2.Warn("warn_msg2") + + l.Fields(map[string]interface{}{"key3": "val4"}).Log(InfoLevel, "test_msg") +} + +func TestLoggerRedirection(t *testing.T) { + var b bytes.Buffer + wr := bufio.NewWriter(&b) + NewLogger(WithOutput(wr)).Logf(InfoLevel, "test message") + wr.Flush() + if !strings.Contains(b.String(), "level=info test message") { + t.Fatalf("Redirection failed, received '%s'", b.String()) + } +} diff --git a/service/logger/option4logrus.go b/service/logger/option4logrus.go new file mode 100644 index 00000000..0f92e23c --- /dev/null +++ b/service/logger/option4logrus.go @@ -0,0 +1,56 @@ +package logger + +type LogrusOption func(*LogrusOptions) + +type LogrusOptions struct { + // 日志文件的根目录 + Path string + // 服务名 + Service string + // 切换日志文件的时间间隔,M-月,D-天,H-小时,默认 D,不建议其它间隔,当选择M时,可能会不准确,因为每月的天数不是固定值 + Rotation string + // 日志文件的最长保留时间,与 Rotation 对应,如 D,则保留时间为该日志文件最后修改时间之后的 天的倍数 + // 如,3天 + MaxAge int + // 日志的级别,可选 { Trace,Debug,Info,Warn,Error,Fatal,Panic },依次增强,如 选择 Info, + // 则 Info 及之后的级别的日志均会记录 + Level Level + // 日志格式,目前支持 text 和 json + Format string +} + +func WithPath(path string) LogrusOption { + return func(args *LogrusOptions) { + args.Path = path + } +} + +func WithServiceName(name string) LogrusOption { + return func(args *LogrusOptions) { + args.Service = name + } +} + +func WithRotation(rotation string) LogrusOption { + return func(args *LogrusOptions) { + args.Rotation = rotation + } +} + +func WithMaxAge(age int) LogrusOption { + return func(args *LogrusOptions) { + args.MaxAge = age + } +} + +func WithLogrusLevel(level Level) LogrusOption { + return func(args *LogrusOptions) { + args.Level = level + } +} + +func WithFormat(format string) LogrusOption { + return func(args *LogrusOptions) { + args.Format = format + } +} -- Gitee