package logger import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" "path" "path/filepath" "runtime" "strings" ) var Log *zap.Logger var SLog *zap.SugaredLogger const DefaultLogPath = "/logs" type ( // FileConfig 日志文件配置 FileConfig struct { Level string // 日志打印级别 debug info warning error Format string // 输出日志格式 console, json Enabled bool // 是否开启 Path string // 输出日志文件路径 FileName string // 输出日志文件名称 FileMaxSize int // 【日志分割】单个日志文件最多存储量 单位(mb) FileMaxBackups int // 【日志分割】日志备份文件最多数量 MaxAge int // 日志保留时间,单位: 天 (day) Compress bool // 是否压缩日志 } // ConsoleConfig 控制台日志配置 ConsoleConfig struct { Level string // 日志打印级别 debug info warning error Format string // 输出日志格式 console, json } ) var logLevel = map[string]zapcore.Level{ "debug": zapcore.DebugLevel, "info": zapcore.InfoLevel, "warn": zapcore.WarnLevel, "error": zapcore.ErrorLevel, } func Sync() { if SLog != nil { _ = SLog.Sync() } if Log != nil { _ = Log.Sync() } } // InitLogger 初始化 log func InitLogger(fileConf *FileConfig, consoleConf *ConsoleConfig) error { cores := make([]zapcore.Core, 0, 2) consoleCore := zapcore.NewCore(getEncoder(consoleConf), zapcore.AddSync(os.Stdout), getLogLevel(consoleConf.Level)) cores = append(cores, consoleCore) if fileConf.Enabled { writeSyncer, err := getLogWriter(fileConf) // 日志文件配置 文件位置和切割 if err != nil { return err } fileCore := zapcore.NewCore(getEncoder(fileConf), writeSyncer, getLogLevel(fileConf.Level)) cores = append(cores, fileCore) } // 控制台/文件 配置分离 core := zapcore.NewTee(cores...) logger := zap.New(core, zap.AddCaller()) //zap.AddCaller() 输出日志打印文件和行数如: logger/logger_test.go:33 SLog = logger.Sugar() Log = logger return nil } // getLogLevel 获取日志打印级别 func getLogLevel(level string) zapcore.Level { l, ok := logLevel[level] // 日志打印级别 if !ok { l = logLevel["info"] } return l } // getLogWriter 获取日志输出方式 日志文件 控制台 func getLogWriter(conf *FileConfig) (zapcore.WriteSyncer, error) { // 判断日志路径是否存在,如果不存在就创建 if conf.Path == "" { conf.Path = getCurrentAbPath() + DefaultLogPath } if exist := isExist(conf.Path); !exist { if err := os.MkdirAll(conf.Path, os.ModePerm); err != nil { conf.Path = getCurrentAbPath() + DefaultLogPath if err := os.MkdirAll(conf.Path, os.ModePerm); err != nil { return nil, err } } } // 日志文件 与 日志切割 配置 lumberJackLogger := &lumberjack.Logger{ Filename: filepath.Join(conf.Path, conf.FileName), // 日志文件路径 MaxSize: conf.FileMaxSize, // 单个日志文件最大多少 mb MaxBackups: conf.FileMaxBackups, // 日志备份数量 MaxAge: conf.MaxAge, // 日志最长保留时间 Compress: conf.Compress, // 是否压缩日志 } return zapcore.AddSync(lumberJackLogger), nil } // getEncoder 编码器(如何写入日志) func getEncoder(conf interface{}) zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05.000Z07") // log 时间格式 例如: 2021-09-11t20:05:54.852+0800 encoderConfig.EncodeCaller = zapcore.FullCallerEncoder var format string switch conf.(type) { case FileConfig: format = conf.(FileConfig).Format encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder case ConsoleConfig: format = conf.(ConsoleConfig).Format // 输出level序列化为全大写字符串,如 INFO DEBUG ERROR 彩色 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder } if format == "json" { return zapcore.NewJSONEncoder(encoderConfig) // 以json格式写入 } return zapcore.NewConsoleEncoder(encoderConfig) // 以默认console格式写入 } // isExist 判断文件或者目录是否存在 func isExist(path string) bool { _, err := os.Stat(path) return err == nil || os.IsExist(err) } // 最终方案-全兼容 func getCurrentAbPath() string { dir := getCurrentAbPathByExecutable() if strings.Contains(dir, getTmpDir()) { return getCurrentAbPathByCaller() } return dir } // 获取系统临时目录,兼容go run func getTmpDir() string { dir := os.Getenv("TEMP") if dir == "" { dir = os.Getenv("TMP") } res, _ := filepath.EvalSymlinks(dir) return res } // 获取当前执行文件绝对路径 func getCurrentAbPathByExecutable() string { exePath, _ := os.Executable() res, _ := filepath.EvalSymlinks(filepath.Dir(exePath)) return res } // 获取当前执行文件绝对路径(go run) func getCurrentAbPathByCaller() string { var abPath string _, filename, _, ok := runtime.Caller(0) if ok { abPath = path.Dir(filename) } return abPath }