package logx import ( "os" "strings" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) type LogConfig struct { AppName string `yaml:"app_name" json:"app_name" toml:"app_name"` Level string `yaml:"level" json:"level" toml:"level"` StacktraceLevel string `yaml:"stacktrace_level" json:"stacktrace_level" toml:"stacktrace_level"` IsStdOut bool `yaml:"is_stdout" json:"is_stdout" toml:"is_stdout"` TimeFormat string `yaml:"time_format" json:"time_format" toml:"time_format"` // second, milli, nano, standard, iso, Encoding string `yaml:"encoding" json:"encoding" toml:"encoding"` // console, json Skip int `yaml:"skip" json:"skip" toml:"skip"` IsFileOut bool `yaml:"is_file_out" json:"is_file_out" toml:"is_file_out"` FileDir string `yaml:"file_dir" json:"file_dir" toml:"file_dir"` FileName string `yaml:"file_name" json:"file_name" toml:"file_name"` FileMaxSize int `yaml:"file_max_size" json:"file_max_size" toml:"file_max_size"` FileMaxAge int `yaml:"file_max_age" json:"file_max_age" toml:"file_max_age"` } var ( l *LogX = defaultLogger() conf *LogConfig ) // default logger setting func defaultLogger() *LogX { conf = &LogConfig{ Level: "debug", StacktraceLevel: "error", IsStdOut: true, TimeFormat: "standard", Encoding: "console", Skip: 2, } writers := []zapcore.WriteSyncer{os.Stdout} lg, lv := newZapLogger(setLogLevel(conf.Level), setLogLevel(conf.StacktraceLevel), conf.Encoding, conf.TimeFormat, conf.Skip, zapcore.NewMultiWriteSyncer(writers...)) zap.RedirectStdLog(lg) return &LogX{logger: lg, atomLevel: lv} } // initial standard log, if you don't init, it will use default logger setting func InitDefaultLogger(cfg *LogConfig) { var writers []zapcore.WriteSyncer if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) { writers = append(writers, os.Stdout) } if cfg.IsFileOut { writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge)) } lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...)) zap.RedirectStdLog(lg) if cfg.AppName != "" { lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称 } l = &LogX{logger: lg, atomLevel: lv} } // create a new logger func NewLogger(cfg *LogConfig) *LogX { var writers []zapcore.WriteSyncer if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) { writers = append(writers, os.Stdout) } if cfg.IsFileOut { writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge)) } lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...)) zap.RedirectStdLog(lg) if cfg.AppName != "" { lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称 } return &LogX{logger: lg, atomLevel: lv} } // create a new zaplog logger func newZapLogger(level, stacktrace zapcore.Level, encoding, timeType string, skip int, output zapcore.WriteSyncer) (*zap.Logger, *zap.AtomicLevel) { encCfg := zapcore.EncoderConfig{ TimeKey: "T", LevelKey: "L", NameKey: "N", CallerKey: "C", MessageKey: "M", StacktraceKey: "S", LineEnding: zapcore.DefaultLineEnding, EncodeCaller: zapcore.ShortCallerEncoder, EncodeDuration: zapcore.NanosDurationEncoder, EncodeLevel: zapcore.LowercaseLevelEncoder, } setTimeFormat(timeType, &encCfg) // set time type atmLvl := zap.NewAtomicLevel() // set level atmLvl.SetLevel(level) encoder := zapcore.NewJSONEncoder(encCfg) // 确定encoder格式 if encoding == "console" { encoder = zapcore.NewConsoleEncoder(encCfg) } return zap.New(zapcore.NewCore(encoder, output, atmLvl), zap.AddCaller(), zap.AddStacktrace(stacktrace), zap.AddCallerSkip(skip)), &atmLvl } // set log level func setLogLevel(lvl string) zapcore.Level { switch strings.ToLower(lvl) { case "panic": return zapcore.PanicLevel case "fatal": return zapcore.FatalLevel case "error": return zapcore.ErrorLevel case "warn", "warning": return zapcore.WarnLevel case "info": return zapcore.InfoLevel default: return zapcore.DebugLevel } } // set time format func setTimeFormat(timeType string, z *zapcore.EncoderConfig) { switch strings.ToLower(timeType) { case "iso": // iso8601 standard z.EncodeTime = zapcore.ISO8601TimeEncoder case "sec": // only for unix second, without millisecond z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendInt64(t.Unix()) } case "second": // unix second, with millisecond z.EncodeTime = zapcore.EpochTimeEncoder case "milli", "millisecond": // millisecond z.EncodeTime = zapcore.EpochMillisTimeEncoder case "nano", "nanosecond": // nanosecond z.EncodeTime = zapcore.EpochNanosTimeEncoder default: // standard format z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) } } } func GetLevel() string { switch l.atomLevel.Level() { case zapcore.PanicLevel: return "panic" case zapcore.FatalLevel: return "fatal" case zapcore.ErrorLevel: return "error" case zapcore.WarnLevel: return "warn" case zapcore.InfoLevel: return "info" default: return "debug" } } func SetLevel(lvl string) { l.atomLevel.SetLevel(setLogLevel(lvl)) } // temporary add call skip func AddCallerSkip(skip int) *LogX { l.logger.WithOptions(zap.AddCallerSkip(skip)) return l } // permanent add call skip func AddDepth(skip int) *LogX { l.logger = l.logger.WithOptions(zap.AddCallerSkip(skip)) return l } // permanent add options func AddOptions(opts ...zap.Option) *LogX { l.logger = l.logger.WithOptions(opts...) return l } func AddField(k string, v interface{}) { l.logger.With(zap.Any(k, v)) } func AddFields(fields map[string]interface{}) *LogX { for k, v := range fields { l.logger.With(zap.Any(k, v)) } return l } // Normal log func Debug(e interface{}, args ...interface{}) error { return l.Debug(e, args...) } func Info(e interface{}, args ...interface{}) error { return l.Info(e, args...) } func Warn(e interface{}, args ...interface{}) error { return l.Warn(e, args...) } func Error(e interface{}, args ...interface{}) error { return l.Error(e, args...) } func Panic(e interface{}, args ...interface{}) error { return l.Panic(e, args...) } func Fatal(e interface{}, args ...interface{}) error { return l.Fatal(e, args...) } // Format logs func Debugf(format string, args ...interface{}) error { return l.Debugf(format, args...) } func Infof(format string, args ...interface{}) error { return l.Infof(format, args...) } func Warnf(format string, args ...interface{}) error { return l.Warnf(format, args...) } func Errorf(format string, args ...interface{}) error { return l.Errorf(format, args...) } func Panicf(format string, args ...interface{}) error { return l.Panicf(format, args...) } func Fatalf(format string, args ...interface{}) error { return l.Fatalf(format, args...) } func formatFieldMap(m FieldMap) []Field { var res []Field for k, v := range m { res = append(res, zap.Any(k, v)) } return res }