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

181 lines
5.0 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}