日志组件
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
8.5 KiB

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() + "]")
}