package logx import ( "fmt" "os" "path/filepath" "sync" "time" "base-framework/pkg/config" "base-framework/pkg/types" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( Logger *zap.Logger once sync.Once currentDate string mu sync.Mutex infoFile *os.File warnFile *os.File errorFile *os.File infoCore zapcore.Core warnCore zapcore.Core errorCore zapcore.Core ) const logTmFmtWithMS = "2006-01-02 15:04:05.000" // 彩色日志级别 var levelColor = map[zapcore.Level]string{ zapcore.DebugLevel: "\033[36mDEBUG\033[0m", zapcore.InfoLevel: "\033[32mINFO\033[0m", zapcore.WarnLevel: "\033[33mWARN\033[0m", zapcore.ErrorLevel: "\033[31mERROR\033[0m", zapcore.DPanicLevel: "\033[35mDPANIC\033[0m", zapcore.PanicLevel: "\033[35mPANIC\033[0m", zapcore.FatalLevel: "\033[35mFATAL\033[0m", } // defaultLogConfig 返回默认日志配置 func defaultLogConfig() *types.Log { return &types.Log{ Dir: "logs", Level: "debug", Format: "json", Daily: true, MaxDays: 30, Caller: true, LevelDirs: true, Console: true, } } // InitLogger 初始化全局 Logger func InitLogger() { once.Do(func() { if config.Log == nil { config.Log = defaultLogConfig() } createDirIfNotExist(config.Log.Dir) switchDate(time.Now().Format("2006-01-02")) }) } // 切换日志文件 func switchDate(today string) { currentDate = today // 关闭旧文件 if infoFile != nil { _ = infoFile.Close() } if warnFile != nil { _ = warnFile.Close() } if errorFile != nil { _ = errorFile.Close() } // 文件路径 infoPath, warnPath, errorPath := config.Log.Dir, config.Log.Dir, config.Log.Dir if config.Log.LevelDirs { infoPath = filepath.Join(config.Log.Dir, "info") warnPath = filepath.Join(config.Log.Dir, "warn") errorPath = filepath.Join(config.Log.Dir, "error") createDirIfNotExist(infoPath) createDirIfNotExist(warnPath) createDirIfNotExist(errorPath) } else { createDirIfNotExist(config.Log.Dir) } // 打开文件 infoFile = getOrCreateFile(filepath.Join(infoPath, fmt.Sprintf("info-%s.log", today))) warnFile = getOrCreateFile(filepath.Join(warnPath, fmt.Sprintf("warn-%s.log", today))) errorFile = getOrCreateFile(filepath.Join(errorPath, fmt.Sprintf("error-%s.log", today))) // 文件编码器 var fileEncoder zapcore.Encoder if config.Log.Format == "console" { fileEncoder = newConsoleEncoder(false) } else { fileEncoder = newJSONEncoder() } // 文件核心 infoCore = zapcore.NewCore(fileEncoder, zapcore.AddSync(infoFile), zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl <= zapcore.InfoLevel })) warnCore = zapcore.NewCore(fileEncoder, zapcore.AddSync(warnFile), zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl == zapcore.WarnLevel })) errorCore = zapcore.NewCore(fileEncoder, zapcore.AddSync(errorFile), zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zapcore.ErrorLevel })) cores := []zapcore.Core{infoCore, warnCore, errorCore} // 开启本地console输出 if config.Log.Console { consoleCore := zapcore.NewCore(newConsoleEncoder(true), zapcore.AddSync(os.Stdout), zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { return lvl >= zapcore.DebugLevel })) cores = append(cores, consoleCore) } Logger = zap.New(zapcore.NewTee(cores...)) if config.Log.Caller { Logger = Logger.WithOptions(zap.AddCaller()) } else { Logger = Logger.WithOptions(zap.AddCallerSkip(1)) } Logger = Logger.WithOptions(zap.Hooks(func(entry zapcore.Entry) error { if config.Log.Daily { checkDateSwitch() } return nil })) } func newConsoleEncoder(enableColor bool) zapcore.Encoder { cfg := zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", CallerKey: "caller", MessageKey: "msg", LineEnding: zapcore.DefaultLineEnding, EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format(logTmFmtWithMS)) }, EncodeLevel: func(lvl zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { if enableColor { color, ok := levelColor[lvl] if !ok { color = lvl.CapitalString() } enc.AppendString(color) } else { enc.AppendString(lvl.CapitalString()) } }, //ConsoleSeparator: "", EncodeCaller: func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { pid := os.Getpid() enc.AppendString(fmt.Sprintf("%d --- %s", pid, caller.TrimmedPath())) }, } return zapcore.NewConsoleEncoder(cfg) } func newJSONEncoder() zapcore.Encoder { cfg := zap.NewProductionEncoderConfig() cfg.TimeKey = "ts" cfg.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format(logTmFmtWithMS)) } cfg.EncodeLevel = zapcore.CapitalLevelEncoder cfg.EncodeCaller = zapcore.ShortCallerEncoder return zapcore.NewJSONEncoder(cfg) } func checkDateSwitch() { today := time.Now().Format("2006-01-02") if today != currentDate { mu.Lock() defer mu.Unlock() if today != currentDate { switchDate(today) } } } func createDirIfNotExist(dir string) { if _, err := os.Stat(dir); os.IsNotExist(err) { _ = os.MkdirAll(dir, 0755) } } func getOrCreateFile(file string) *os.File { f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { panic(err) } return f }