| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You 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 |
| // |
| // http://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. |
| |
| package log |
| |
| import ( |
| "fmt" |
| "os" |
| "strconv" |
| "time" |
| |
| "github.com/apache/incubator-eventmesh/eventmesh-server-go/log/rollwriter" |
| "go.uber.org/zap" |
| "go.uber.org/zap/zapcore" |
| ) |
| |
| var defaultConfig = []OutputConfig{ |
| { |
| Writer: "console", |
| Level: "debug", |
| Formatter: "console", |
| }, |
| } |
| |
| // Some ZapCore constants. |
| const ( |
| ConsoleZapCore = "console" |
| FileZapCore = "file" |
| ) |
| |
| // Levels is the map from string to zapcore.Level. |
| var Levels = map[string]zapcore.Level{ |
| "": zapcore.DebugLevel, |
| "debug": zapcore.DebugLevel, |
| "info": zapcore.InfoLevel, |
| "warn": zapcore.WarnLevel, |
| "error": zapcore.ErrorLevel, |
| "fatal": zapcore.FatalLevel, |
| } |
| |
| var levelToZapLevel = map[Level]zapcore.Level{ |
| LevelTrace: zapcore.DebugLevel, |
| LevelDebug: zapcore.DebugLevel, |
| LevelInfo: zapcore.InfoLevel, |
| LevelWarn: zapcore.WarnLevel, |
| LevelError: zapcore.ErrorLevel, |
| LevelFatal: zapcore.FatalLevel, |
| } |
| |
| var zapLevelToLevel = map[zapcore.Level]Level{ |
| zapcore.DebugLevel: LevelDebug, |
| zapcore.InfoLevel: LevelInfo, |
| zapcore.WarnLevel: LevelWarn, |
| zapcore.ErrorLevel: LevelError, |
| zapcore.FatalLevel: LevelFatal, |
| } |
| |
| // NewZapLog creates a workflow default Logger from zap whose caller skip is set to 2. |
| func NewZapLog(c Config) Logger { |
| return NewZapLogWithCallerSkip(c, 2) |
| } |
| |
| // NewZapLogWithCallerSkip creates a workflow default Logger from zap. |
| func NewZapLogWithCallerSkip(c Config, callerSkip int) Logger { |
| var ( |
| cores []zapcore.Core |
| levels []zap.AtomicLevel |
| ) |
| for _, o := range c { |
| writer := GetWriter(o.Writer) |
| if writer == nil { |
| panic("log: writer core: " + o.Writer + " no registered") |
| } |
| decoder := &Decoder{OutputConfig: &o} |
| if err := writer.Setup(o.Writer, decoder); err != nil { |
| panic("log: writer core: " + o.Writer + " setup fail: " + err.Error()) |
| } |
| cores = append(cores, decoder.Core) |
| levels = append(levels, decoder.ZapLevel) |
| } |
| return &zapLog{ |
| levels: levels, |
| logger: zap.New( |
| zapcore.NewTee(cores...), |
| zap.AddCallerSkip(callerSkip), |
| zap.AddCaller(), |
| ), |
| } |
| } |
| |
| func newEncoder(c *OutputConfig) zapcore.Encoder { |
| encoderCfg := zapcore.EncoderConfig{ |
| TimeKey: GetLogEncoderKey("T", c.FormatConfig.TimeKey), |
| LevelKey: GetLogEncoderKey("L", c.FormatConfig.LevelKey), |
| NameKey: GetLogEncoderKey("N", c.FormatConfig.NameKey), |
| CallerKey: GetLogEncoderKey("C", c.FormatConfig.CallerKey), |
| FunctionKey: GetLogEncoderKey(zapcore.OmitKey, c.FormatConfig.FunctionKey), |
| MessageKey: GetLogEncoderKey("M", c.FormatConfig.MessageKey), |
| StacktraceKey: GetLogEncoderKey("S", c.FormatConfig.StacktraceKey), |
| LineEnding: zapcore.DefaultLineEnding, |
| EncodeLevel: zapcore.CapitalLevelEncoder, |
| EncodeTime: NewTimeEncoder(c.FormatConfig.TimeFmt), |
| EncodeDuration: zapcore.StringDurationEncoder, |
| EncodeCaller: zapcore.ShortCallerEncoder, |
| } |
| switch c.Formatter { |
| case "console": |
| return zapcore.NewConsoleEncoder(encoderCfg) |
| case "json": |
| return zapcore.NewJSONEncoder(encoderCfg) |
| default: |
| return zapcore.NewConsoleEncoder(encoderCfg) |
| } |
| } |
| |
| // GetLogEncoderKey gets user defined log output name, uses defKey if empty. |
| func GetLogEncoderKey(defKey, key string) string { |
| if key == "" { |
| return defKey |
| } |
| return key |
| } |
| |
| func newConsoleCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel) { |
| lvl := zap.NewAtomicLevelAt(Levels[c.Level]) |
| return zapcore.NewCore( |
| newEncoder(c), |
| zapcore.Lock(os.Stdout), |
| lvl), lvl |
| } |
| |
| func newFileCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel, error) { |
| opts := []rollwriter.Option{ |
| rollwriter.WithMaxAge(c.WriteConfig.MaxAge), |
| rollwriter.WithMaxBackups(c.WriteConfig.MaxBackups), |
| rollwriter.WithCompress(c.WriteConfig.Compress), |
| rollwriter.WithMaxSize(c.WriteConfig.MaxSize), |
| } |
| // roll by time. |
| if c.WriteConfig.RollType != RollBySize { |
| opts = append(opts, rollwriter.WithRotationTime(c.WriteConfig.TimeUnit.Format())) |
| } |
| writer, err := rollwriter.NewRollWriter(c.WriteConfig.Filename, opts...) |
| if err != nil { |
| return nil, zap.AtomicLevel{}, err |
| } |
| |
| // write mod. |
| var ws zapcore.WriteSyncer |
| if c.WriteConfig.WriteMode == WriteSync { |
| ws = zapcore.AddSync(writer) |
| } else { |
| dropLog := c.WriteConfig.WriteMode == WriteFast |
| ws = rollwriter.NewAsyncRollWriter(writer, |
| rollwriter.WithDropLog(dropLog), |
| ) |
| } |
| |
| // log level. |
| lvl := zap.NewAtomicLevelAt(Levels[c.Level]) |
| return zapcore.NewCore( |
| newEncoder(c), |
| ws, lvl, |
| ), lvl, nil |
| } |
| |
| // NewTimeEncoder creates a time format encoder. |
| func NewTimeEncoder(format string) zapcore.TimeEncoder { |
| switch format { |
| case "": |
| return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { |
| enc.AppendByteString(DefaultTimeFormat(t)) |
| } |
| case "seconds": |
| return zapcore.EpochTimeEncoder |
| case "milliseconds": |
| return zapcore.EpochMillisTimeEncoder |
| case "nanoseconds": |
| return zapcore.EpochNanosTimeEncoder |
| default: |
| return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { |
| enc.AppendString(CustomTimeFormat(t, format)) |
| } |
| } |
| } |
| |
| // CustomTimeFormat customize time format. |
| func CustomTimeFormat(t time.Time, format string) string { |
| return t.Format(format) |
| } |
| |
| // DefaultTimeFormat returns the default time format. |
| func DefaultTimeFormat(t time.Time) []byte { |
| t = t.Local() |
| year, month, day := t.Date() |
| hour, minute, second := t.Clock() |
| micros := t.Nanosecond() / 1000 |
| |
| buf := make([]byte, 23) |
| buf[0] = byte((year/1000)%10) + '0' |
| buf[1] = byte((year/100)%10) + '0' |
| buf[2] = byte((year/10)%10) + '0' |
| buf[3] = byte(year%10) + '0' |
| buf[4] = '-' |
| buf[5] = byte((month)/10) + '0' |
| buf[6] = byte((month)%10) + '0' |
| buf[7] = '-' |
| buf[8] = byte((day)/10) + '0' |
| buf[9] = byte((day)%10) + '0' |
| buf[10] = ' ' |
| buf[11] = byte((hour)/10) + '0' |
| buf[12] = byte((hour)%10) + '0' |
| buf[13] = ':' |
| buf[14] = byte((minute)/10) + '0' |
| buf[15] = byte((minute)%10) + '0' |
| buf[16] = ':' |
| buf[17] = byte((second)/10) + '0' |
| buf[18] = byte((second)%10) + '0' |
| buf[19] = '.' |
| buf[20] = byte((micros/100000)%10) + '0' |
| buf[21] = byte((micros/10000)%10) + '0' |
| buf[22] = byte((micros/1000)%10) + '0' |
| return buf |
| } |
| |
| // ZapLogWrapper delegates zapLogger which was introduced in this |
| // By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the caller |
| // information can be set correctly. |
| type ZapLogWrapper struct { |
| l *zapLog |
| } |
| |
| // GetLogger returns interval zapLog. |
| func (z *ZapLogWrapper) GetLogger() Logger { |
| return z.l |
| } |
| |
| // Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print. |
| func (z *ZapLogWrapper) Trace(args ...interface{}) { |
| z.l.Trace(args...) |
| } |
| |
| // Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf. |
| func (z *ZapLogWrapper) Tracef(format string, args ...interface{}) { |
| z.l.Tracef(format, args...) |
| } |
| |
| // Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print. |
| func (z *ZapLogWrapper) Debug(args ...interface{}) { |
| z.l.Debug(args...) |
| } |
| |
| // Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf. |
| func (z *ZapLogWrapper) Debugf(format string, args ...interface{}) { |
| z.l.Debugf(format, args...) |
| } |
| |
| // Info logs to INFO log. Arguments are handled in the manner of fmt.Print. |
| func (z *ZapLogWrapper) Info(args ...interface{}) { |
| z.l.Info(args...) |
| } |
| |
| // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. |
| func (z *ZapLogWrapper) Infof(format string, args ...interface{}) { |
| z.l.Infof(format, args...) |
| } |
| |
| // Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print. |
| func (z *ZapLogWrapper) Warn(args ...interface{}) { |
| z.l.Warn(args...) |
| } |
| |
| // Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. |
| func (z *ZapLogWrapper) Warnf(format string, args ...interface{}) { |
| z.l.Warnf(format, args...) |
| } |
| |
| // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print. |
| func (z *ZapLogWrapper) Error(args ...interface{}) { |
| z.l.Error(args...) |
| } |
| |
| // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. |
| func (z *ZapLogWrapper) Errorf(format string, args ...interface{}) { |
| z.l.Errorf(format, args...) |
| } |
| |
| // Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Print. |
| func (z *ZapLogWrapper) Fatal(args ...interface{}) { |
| z.l.Fatal(args...) |
| } |
| |
| // Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf. |
| func (z *ZapLogWrapper) Fatalf(format string, args ...interface{}) { |
| z.l.Fatalf(format, args...) |
| } |
| |
| // Sync calls the zap logger's Sync method, and flushes any buffered log entries. |
| // Applications should take care to call Sync before exiting. |
| func (z *ZapLogWrapper) Sync() error { |
| return z.l.Sync() |
| } |
| |
| // SetLevel set output log level. |
| func (z *ZapLogWrapper) SetLevel(output string, level Level) { |
| z.l.SetLevel(output, level) |
| } |
| |
| // GetLevel gets output log level. |
| func (z *ZapLogWrapper) GetLevel(output string) Level { |
| return z.l.GetLevel(output) |
| } |
| |
| // WithFields set some user defined data to logs, such as uid, imei, etc. |
| // Use this function at the beginning of each request. The returned new Logger should be used to |
| // print logs. |
| // Fields must be paired. |
| // Deprecated: use With instead. |
| func (z *ZapLogWrapper) WithFields(fields ...string) Logger { |
| return z.l.WithFields(fields...) |
| } |
| |
| // With add user defined fields to Logger. Fields support multiple values. |
| func (z *ZapLogWrapper) With(fields ...Field) Logger { |
| return z.l.With(fields...) |
| } |
| |
| // zapLog is a Logger implementation based on zaplogger. |
| type zapLog struct { |
| levels []zap.AtomicLevel |
| logger *zap.Logger |
| } |
| |
| // WithFields set some user defined data to logs, such as uid, imei, etc. |
| // Use this function at the beginning of each request. The returned new Logger should be used to |
| // print logs. |
| // Fields must be paired. |
| // Deprecated: use With instead. |
| func (l *zapLog) WithFields(fields ...string) Logger { |
| zapFields := make([]zap.Field, len(fields)/2) |
| for i := range zapFields { |
| zapFields[i] = zap.String(fields[2*i], fields[2*i+1]) |
| } |
| |
| // By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the |
| // caller information can be set correctly. |
| return &ZapLogWrapper{ |
| l: &zapLog{ |
| levels: l.levels, |
| logger: l.logger.With(zapFields...)}} |
| } |
| |
| // With add user defined fields to Logger. Fields support multiple values. |
| func (l *zapLog) With(fields ...Field) Logger { |
| zapFields := make([]zap.Field, len(fields)) |
| for i := range fields { |
| zapFields[i] = zap.Any(fields[i].Key, fields[i].Value) |
| } |
| |
| // By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the |
| // caller information can be set correctly. |
| return &ZapLogWrapper{ |
| l: &zapLog{ |
| levels: l.levels, |
| logger: l.logger.With(zapFields...)}} |
| } |
| |
| func getLogMsg(args ...interface{}) string { |
| msg := fmt.Sprint(args...) |
| return msg |
| } |
| |
| func getLogMsgf(format string, args ...interface{}) string { |
| msg := fmt.Sprintf(format, args...) |
| return msg |
| } |
| |
| // Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print. |
| func (l *zapLog) Trace(args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.DebugLevel) { |
| l.logger.Debug(getLogMsg(args...)) |
| } |
| } |
| |
| // Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf. |
| func (l *zapLog) Tracef(format string, args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.DebugLevel) { |
| l.logger.Debug(getLogMsgf(format, args...)) |
| } |
| } |
| |
| // Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print. |
| func (l *zapLog) Debug(args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.DebugLevel) { |
| l.logger.Debug(getLogMsg(args...)) |
| } |
| } |
| |
| // Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf. |
| func (l *zapLog) Debugf(format string, args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.DebugLevel) { |
| l.logger.Debug(getLogMsgf(format, args...)) |
| } |
| } |
| |
| // Info logs to INFO log. Arguments are handled in the manner of fmt.Print. |
| func (l *zapLog) Info(args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.InfoLevel) { |
| l.logger.Info(getLogMsg(args...)) |
| } |
| } |
| |
| // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. |
| func (l *zapLog) Infof(format string, args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.InfoLevel) { |
| l.logger.Info(getLogMsgf(format, args...)) |
| } |
| } |
| |
| // Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print. |
| func (l *zapLog) Warn(args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.WarnLevel) { |
| l.logger.Warn(getLogMsg(args...)) |
| } |
| } |
| |
| // Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. |
| func (l *zapLog) Warnf(format string, args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.WarnLevel) { |
| l.logger.Warn(getLogMsgf(format, args...)) |
| } |
| } |
| |
| // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print. |
| func (l *zapLog) Error(args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.ErrorLevel) { |
| l.logger.Error(getLogMsg(args...)) |
| } |
| } |
| |
| // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. |
| func (l *zapLog) Errorf(format string, args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.ErrorLevel) { |
| l.logger.Error(getLogMsgf(format, args...)) |
| } |
| } |
| |
| // Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Print. |
| func (l *zapLog) Fatal(args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.FatalLevel) { |
| l.logger.Fatal(getLogMsg(args...)) |
| } |
| } |
| |
| // Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf. |
| func (l *zapLog) Fatalf(format string, args ...interface{}) { |
| if l.logger.Core().Enabled(zapcore.FatalLevel) { |
| l.logger.Fatal(getLogMsgf(format, args...)) |
| } |
| } |
| |
| // Sync calls the zap logger's Sync method, and flushes any buffered log entries. |
| // Applications should take care to call Sync before exiting. |
| func (l *zapLog) Sync() error { |
| return l.logger.Sync() |
| } |
| |
| // SetLevel sets output log level. |
| func (l *zapLog) SetLevel(output string, level Level) { |
| i, e := strconv.Atoi(output) |
| if e != nil { |
| return |
| } |
| if i < 0 || i >= len(l.levels) { |
| return |
| } |
| l.levels[i].SetLevel(levelToZapLevel[level]) |
| } |
| |
| // GetLevel gets output log level. |
| func (l *zapLog) GetLevel(output string) Level { |
| i, e := strconv.Atoi(output) |
| if e != nil { |
| return LevelDebug |
| } |
| if i < 0 || i >= len(l.levels) { |
| return LevelDebug |
| } |
| return zapLevelToLevel[l.levels[i].Level()] |
| } |