package logging import ( "os" "path/filepath" "sync" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) type ModOptions func(options *Options) // Options 日志文件配置选项 type Options struct { Path string `yaml:"path" json:"path" toml:"path"` // 文件保存地方 Prefix string `yaml:"prefix" json:"prefix" toml:"prefix"` // 日志文件前缀 ErrorFileSuffix string `yaml:"errorFileSuffix" json:"errorFileSuffix" toml:"errorFileSuffix"` // error日志文件后缀 WarnFileSuffix string `yaml:"warnFileSuffix" json:"warnFileSuffix" toml:"warnFileSuffix"` // warn日志文件后缀 InfoFileSuffix string `yaml:"infoFileSuffix" json:"infoFileSuffix" toml:"infoFileSuffix"` // info日志文件后缀 DebugFileSuffix string `yaml:"debugFileSuffix" json:"debugFileSuffix" toml:"debugFileSuffix"` // debug日志文件后缀 Level zapcore.Level `yaml:"level" json:"level" toml:"level"` // 日志等级 MaxSize int `yaml:"maxSize" json:"maxSize" toml:"maxSize"` // 日志文件大小(M) MaxBackups int `yaml:"maxBackups" json:"maxBackups" toml:"maxBackups"` // 最多存在多少个切片文件 MaxAge int `yaml:"maxAge" json:"maxAge" toml:"maxAge"` // 保存的最大天数 Development bool `yaml:"development" json:"development" toml:"development"` // 是否是开发模式 zap.Config } var ( logger *Logger sp = string(filepath.Separator) errWS, warnWS, infoWS, debugWS zapcore.WriteSyncer // IO输出 debugConsoleWS = zapcore.Lock(os.Stdout) // 控制台标准输出 errorConsoleWS = zapcore.Lock(os.Stderr) LogLevel = make(map[string]zapcore.Level) ) type Logger struct { *zap.Logger sync.RWMutex Opts *Options `json:"opts"` zapConfig zap.Config initialized bool } func NewLogger(mod ...ModOptions) *Logger { logger = &Logger{} logger.Lock() defer logger.Unlock() if logger.initialized { logger.Info("[NewLogger] logger initEd") return nil } logger.Opts = &Options{ Path: "", Prefix: "app", ErrorFileSuffix: "error.log", WarnFileSuffix: "warn.log", InfoFileSuffix: "info.log", DebugFileSuffix: "debug.log", Level: zapcore.DebugLevel, MaxSize: 100, MaxBackups: 60, MaxAge: 30, } if logger.Opts.Path == "" { logger.Opts.Path, _ = filepath.Abs(filepath.Dir(filepath.Join("."))) logger.Opts.Path += sp + "logs" + sp } if logger.Opts.Development { logger.zapConfig = zap.NewDevelopmentConfig() logger.zapConfig.EncoderConfig.EncodeTime = timeEncoder } else { logger.zapConfig = zap.NewProductionConfig() logger.zapConfig.EncoderConfig.EncodeTime = timeEncoder } if logger.Opts.OutputPaths == nil || len(logger.Opts.OutputPaths) == 0 { logger.zapConfig.OutputPaths = []string{"stdout"} } if logger.Opts.ErrorOutputPaths == nil || len(logger.Opts.ErrorOutputPaths) == 0 { logger.zapConfig.OutputPaths = []string{"stderr"} } for _, fn := range mod { fn(logger.Opts) } logger.zapConfig.Level.SetLevel(logger.Opts.Level) logger.init() logger.initialized = true return logger } func (logger *Logger) init() { logger.setSyncs() var err error logger.Logger, err = logger.zapConfig.Build(logger.cores()) if err != nil { panic(err) } LogLevel["debug"] = zap.DebugLevel LogLevel["info"] = zap.InfoLevel LogLevel["warn"] = zap.WarnLevel LogLevel["error"] = zap.ErrorLevel LogLevel["dpanic"] = zap.DPanicLevel LogLevel["panic"] = zap.PanicLevel LogLevel["fatal"] = zap.FatalLevel defer logger.Logger.Sync() } func (logger *Logger) setSyncs() { f := func(fN string) zapcore.WriteSyncer { return zapcore.AddSync(&lumberjack.Logger{ Filename: logger.Opts.Path + sp + logger.Opts.Prefix + "-" + fN, MaxSize: logger.Opts.MaxSize, MaxBackups: logger.Opts.MaxBackups, MaxAge: logger.Opts.MaxAge, Compress: true, LocalTime: true, }) } errWS = f(logger.Opts.ErrorFileSuffix) warnWS = f(logger.Opts.WarnFileSuffix) infoWS = f(logger.Opts.InfoFileSuffix) debugWS = f(logger.Opts.DebugFileSuffix) } func SetMaxSize(MaxSize int) ModOptions { return func(option *Options) { option.MaxSize = MaxSize } } func SetMaxBackups(MaxBackups int) ModOptions { return func(option *Options) { option.MaxBackups = MaxBackups } } func SetMaxAge(MaxAge int) ModOptions { return func(option *Options) { option.MaxAge = MaxAge } } func SetPath(Path string) ModOptions { return func(option *Options) { option.Path = Path } } func SetPrefix(Prefix string) ModOptions { return func(option *Options) { option.Prefix = Prefix } } func SetLevel(Level zapcore.Level) ModOptions { return func(option *Options) { option.Level = Level } } func SetErrorFileSuffix(ErrorFileSuffix string) ModOptions { return func(option *Options) { option.ErrorFileSuffix = ErrorFileSuffix } } func SetWarnFileSuffix(WarnFileSuffix string) ModOptions { return func(option *Options) { option.WarnFileSuffix = WarnFileSuffix } } func SetInfoFileSuffix(InfoFileSuffix string) ModOptions { return func(option *Options) { option.InfoFileSuffix = InfoFileSuffix } } func SetDebugFileSuffix(DebugFileSuffix string) ModOptions { return func(option *Options) { option.DebugFileSuffix = DebugFileSuffix } } func SetDevelopment(Development bool) ModOptions { return func(option *Options) { option.Development = Development } } // getDefaultLogger returns the default logger. func getDefaultLogger() *Logger { return logger } // L returns the default logger. func L() *Logger { return getDefaultLogger() } func (logger *Logger) cores() zap.Option { fileEncoder := zapcore.NewJSONEncoder(logger.zapConfig.EncoderConfig) encoderConfig := zap.NewDevelopmentEncoderConfig() encoderConfig.EncodeTime = timeEncoder encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder consoleEncoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller_line", FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: "\n", EncodeLevel: encodeLevel, EncodeTime: encodeTime, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: encodeCaller, }) errPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl == zapcore.ErrorLevel && zapcore.ErrorLevel-logger.zapConfig.Level.Level() > -1 }) warnPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl == zapcore.WarnLevel && zapcore.WarnLevel-logger.zapConfig.Level.Level() > -1 }) infoPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl == zapcore.InfoLevel && zapcore.InfoLevel-logger.zapConfig.Level.Level() > -1 }) debugPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl == zapcore.DebugLevel && zapcore.DebugLevel-logger.zapConfig.Level.Level() > -1 }) cores := []zapcore.Core{ zapcore.NewCore(fileEncoder, errWS, errPriority), zapcore.NewCore(fileEncoder, warnWS, warnPriority), zapcore.NewCore(fileEncoder, infoWS, infoPriority), zapcore.NewCore(fileEncoder, debugWS, debugPriority), } if logger.Opts.Development { cores = append(cores, []zapcore.Core{ zapcore.NewCore(consoleEncoder, errorConsoleWS, errPriority), zapcore.NewCore(consoleEncoder, debugConsoleWS, warnPriority), zapcore.NewCore(consoleEncoder, debugConsoleWS, infoPriority), zapcore.NewCore(consoleEncoder, debugConsoleWS, debugPriority), }...) } return zap.WrapCore(func(c zapcore.Core) zapcore.Core { return zapcore.NewTee(cores...) }) } func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) } // encodeLevel 自定义日志级别显示 func encodeLevel(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString("[" + level.CapitalString() + "]") } // encodeTime 自定义时间格式显示 func encodeTime(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString("[" + t.Format("2006-01-02 15:04:05.000") + "]") } // encodeCaller 自定义行号显示 func encodeCaller(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString("[" + caller.TrimmedPath() + "]") }