// Package logger... // // Description : config 日志配置 // // Author : go_developer@163.com<白茶清欢> // // Date : 2021-01-02 3:07 下午 package logger import ( "fmt" "os" "path/filepath" "strings" "time" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/websocket/storage" "go.uber.org/zap/zapcore" ) // TimeIntervalType 日志时间间隔类型 type TimeIntervalType uint const ( // DefaultDivisionChar 默认的时间格式分隔符 DefaultDivisionChar = "-" ) // RotateLogConfig 日志切割的配置 // // Author : go_developer@163.com<白茶清欢> // // Date : 3:08 下午 2021/1/2 type RotateLogConfig struct { TimeIntervalType consts.LogSplit `json:"time_interval_type" yaml:"time_interval_type"` // 日志切割的时间间隔类型 0 - 小时 1 - 天 2 - 月 3 - 年 TimeInterval time.Duration `json:"time_interval" yaml:"time_interval"` // 日志切割的时间间隔 LogPath string `json:"log_path" yaml:"log_path"` // 存储日志的路径 LogFileName string `json:"log_file_name" yaml:"log_file_name"` // 日志文件名 DivisionChar string `json:"division_char" yaml:"division_char"` // 日志文件拼时间分隔符 FullLogFormat string `json:"full_log_format" yaml:"full_log_format"` // 完整的日志格式 MaxAge time.Duration `json:"max_age" yaml:"max_age"` // 日志最长保存时间 } // SetRotateLogConfigFunc 设置日志切割的选项 // // Author : go_developer@163.com<白茶清欢> // // Date : 3:13 下午 2021/1/2 type SetRotateLogConfigFunc func(rlc *RotateLogConfig) // WithTimeIntervalType 设置日志切割时间间隔 // // Author : go_developer@163.com<白茶清欢> // // Date : 3:34 下午 2021/1/2 func WithTimeIntervalType(timeIntervalType consts.LogSplit) SetRotateLogConfigFunc { return func(rlc *RotateLogConfig) { rlc.TimeIntervalType = timeIntervalType } } // WithDivisionChar 设置分隔符 // // Author : go_developer@163.com<白茶清欢> // // Date : 3:49 下午 2021/1/2 func WithDivisionChar(divisionChar string) SetRotateLogConfigFunc { return func(rlc *RotateLogConfig) { rlc.DivisionChar = divisionChar } } // WithMaxAge 设置日志保存时间 // // Author : go_developer@163.com<白茶清欢> // // Date : 5:03 下午 2021/1/2 func WithMaxAge(maxAge time.Duration) SetRotateLogConfigFunc { return func(rlc *RotateLogConfig) { rlc.MaxAge = maxAge } } // NewRotateLogConfig 生成日志切割的配置 // // Author : go_developer@163.com<白茶清欢> // // Date : 3:53 下午 2021/1/2 func NewRotateLogConfig(logPath string, logFile string, option ...SetRotateLogConfigFunc) (*RotateLogConfig, error) { if len(logPath) == 0 || len(logFile) == 0 { return nil, LogPathEmptyError() } c := &RotateLogConfig{ TimeIntervalType: consts.LogSplitHour, LogPath: logPath, LogFileName: logFile, DivisionChar: "", } for _, o := range option { o(c) } if err := formatConfig(c); nil != err { return nil, err } return c, nil } // formatConfig 格式化配置 // // Author : go_developer@163.com<白茶清欢> // // Date : 4:23 下午 2021/1/2 func formatConfig(c *RotateLogConfig) error { if len(c.DivisionChar) == 0 { c.DivisionChar = DefaultDivisionChar } // 格式化路径 c.LogPath = strings.TrimRight(c.LogPath, string(filepath.Separator)) + string(filepath.Separator) // 检测路径是否存在,不存在自动创建 if _, err := os.Stat(c.LogPath); nil != err { if !os.IsNotExist(err) { // 异常不是路径不存在,抛异常 return DealLogPathError(err, c.LogPath) } if err = os.Mkdir(c.LogPath, os.ModePerm); nil != err { return DealLogPathError(err, "创建日志目录失败") } } c.TimeIntervalType = consts.LogSplit(strings.ToUpper(c.TimeIntervalType.String())) if !c.TimeIntervalType.IsValid() { // 非法的日志切割规则,默认按天切 c.TimeIntervalType = consts.LogSplitDay } // 生成格式化日志全路径 switch c.TimeIntervalType { case consts.LogSplitHour: c.TimeInterval = time.Hour c.FullLogFormat = c.LogPath + "%Y" + c.DivisionChar + "%m" + c.DivisionChar + "%d" + c.DivisionChar + "%H" + c.DivisionChar + c.LogFileName case consts.LogSplitDay: c.TimeInterval = time.Hour * 24 c.FullLogFormat = c.LogPath + "%Y" + c.DivisionChar + "%m" + c.DivisionChar + "%d" + c.DivisionChar + c.LogFileName case consts.LogSplitMonth: c.TimeInterval = time.Hour * 24 * 30 c.FullLogFormat = c.LogPath + "%Y" + c.DivisionChar + "%m" + c.DivisionChar + c.LogFileName case consts.LogSplitYear: c.TimeInterval = time.Hour * 24 * 365 c.FullLogFormat = c.LogPath + "%Y" + c.DivisionChar + c.LogFileName default: return LogSplitTypeError(c.TimeIntervalType.String()) } return nil } // ============== 以下为zap相关配置 const ( // defaultMessageKey 默认的message key defaultMessageKey = "message" // defaultLevelKey 默认的level defaultLevelKey = "level" // defaultTimeKey 默认时间key defaultTimeKey = "time" // defaultCallerKey 默认的文件key defaultCallerKey = "file" // defaultUserShortCaller 是否使用短的文件调用格式 defaultUseShortCaller = true // defaultUseJsonFormat 日志默认使用json格式 defaultUseJsonFormat = true ) // defaultTimeEncoder 默认的时间处理 // // Author : go_developer@163.com<白茶清欢> // // Date : 11:53 下午 2021/1/2 func defaultTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { sec := t.UnixNano() / 1e9 ms := t.UnixNano() / 1e6 % 1e3 ns := t.UnixNano() % 1e6 enc.AppendString(time.Unix(sec, ns).Format("2006-01-02 15:04:05") + "." + fmt.Sprintf("%v", ms) + "+" + fmt.Sprintf("%v", ns)) } // SecondTimeEncoder 秒级时间戳格式化 // // Author : go_developer@163.com<白茶清欢> // // Date : 8:34 下午 2021/1/3 func SecondTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05")) } // MsTimeEncoder 毫秒时间格式化方法 // // Author : go_developer@163.com<白茶清欢> // // Date : 8:35 下午 2021/1/3 func MsTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { sec := t.UnixNano() / 1e9 ms := t.UnixNano() / 1e6 % 1e3 enc.AppendString(time.Unix(sec, 0).Format("2006-01-02 15:04:05") + "." + fmt.Sprintf("%v", ms)) } // defaultEncodeDuration 默认的原始时间处理 // // Author : go_developer@163.com<白茶清欢> // // Date : 11:56 下午 2021/1/2 func defaultEncodeDuration(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { enc.AppendInt64(int64(d) / 1000000) } // OptionLogger 日志配置的选项 // // Author : go_developer@163.com<白茶清欢> // // Date : 11:41 下午 2021/1/2 type OptionLogger struct { UseJsonFormat bool // 日志使用json格式 MessageKey string // message 字段 LevelKey string // level 字段 TimeKey string // 时间字段 CallerKey string // 记录日志的文件的代码行数 UseShortCaller bool // 使用短的调用文件格式 TimeEncoder zapcore.TimeEncoder // 格式化时间的函数 EncodeDuration zapcore.DurationEncoder // 原始时间信息 WithCaller bool // 是否打印文件行号 WithCallerSkip int // 跳过的调用数 ConsoleOutput bool // 控制台输出 Encoder zapcore.Encoder // 编码函数 WsLoggerConnect storage.IConnection `json:"-"` // ws 日志连接管理实例 } // SetLoggerOptionFunc 设置日志配置 type SetLoggerOptionFunc func(o *OptionLogger) // WithCaller 打开文件行号记录 func WithCaller() SetLoggerOptionFunc { return func(o *OptionLogger) { o.WithCaller = true } } // WithCallerSkip ... func WithCallerSkip(skip int) SetLoggerOptionFunc { return func(o *OptionLogger) { o.WithCallerSkip = skip } } // WithConsoleOutput 日志控制台输出 func WithConsoleOutput() SetLoggerOptionFunc { return func(o *OptionLogger) { o.ConsoleOutput = true } } // WithEncoder ... func WithEncoder(encoder zapcore.Encoder) SetLoggerOptionFunc { return func(o *OptionLogger) { o.Encoder = encoder } } // WithUseJsonFormat 日志是否使用json格式数据 // // Author : go_developer@163.com<白茶清欢> // // Date : 12:30 上午 2021/1/3 func WithUseJsonFormat(isJsonFormat bool) SetLoggerOptionFunc { return func(o *OptionLogger) { o.UseJsonFormat = isJsonFormat } } // WithMessageKey 使用message key // // Author : go_developer@163.com<白茶清欢> // // Date : 12:32 上午 2021/1/3 func WithMessageKey(messageKey string) SetLoggerOptionFunc { return func(o *OptionLogger) { messageKey = strings.Trim(messageKey, " ") if len(messageKey) == 0 { return } o.MessageKey = messageKey } } // WithLevelKey 设置level key // // Author : go_developer@163.com<白茶清欢> // // Date : 12:33 上午 2021/1/3 func WithLevelKey(levelKey string) SetLoggerOptionFunc { return func(o *OptionLogger) { levelKey = strings.Trim(levelKey, " ") if len(levelKey) == 0 { return } o.LevelKey = levelKey } } // WithTimeKey 设置time key ... // // Author : go_developer@163.com<白茶清欢> // // Date : 12:34 上午 2021/1/3 func WithTimeKey(timeKey string) SetLoggerOptionFunc { return func(o *OptionLogger) { timeKey = strings.Trim(timeKey, " ") if len(timeKey) == 0 { return } o.TimeKey = timeKey } } // WithCallerKey 设置caller key // // Author : go_developer@163.com<白茶清欢> // // Date : 12:37 上午 2021/1/3 func WithCallerKey(callerKey string) SetLoggerOptionFunc { return func(o *OptionLogger) { callerKey = strings.Trim(callerKey, " ") if len(callerKey) == 0 { return } o.CallerKey = callerKey } } // WithShortCaller 是否使用短caller格式 // // Author : go_developer@163.com<白茶清欢> // // Date : 12:39 上午 2021/1/3 func WithShortCaller(useShortCaller bool) SetLoggerOptionFunc { return func(o *OptionLogger) { o.UseShortCaller = useShortCaller } } // WithTimeEncoder 设置格式化时间方法 // // Author : go_developer@163.com<白茶清欢> // // Date : 12:41 上午 2021/1/3 func WithTimeEncoder(encoder zapcore.TimeEncoder) SetLoggerOptionFunc { return func(o *OptionLogger) { if nil == encoder { return } o.TimeEncoder = encoder } } // WithEncodeDuration 原始时间 // // Author : go_developer@163.com<白茶清欢> // // Date : 12:42 上午 2021/1/3 func WithEncodeDuration(encoder zapcore.DurationEncoder) SetLoggerOptionFunc { return func(o *OptionLogger) { if nil == encoder { return } o.EncodeDuration = encoder } } // GetEncoder 获取空中台输出的encoder // // Author : go_developer@163.com<白茶清欢> // // Date : 6:24 下午 2021/1/2 func GetEncoder(option ...SetLoggerOptionFunc) zapcore.Encoder { ol := &OptionLogger{ UseJsonFormat: defaultUseJsonFormat, MessageKey: defaultMessageKey, LevelKey: defaultLevelKey, TimeKey: defaultTimeKey, TimeEncoder: defaultTimeEncoder, CallerKey: defaultCallerKey, EncodeDuration: defaultEncodeDuration, UseShortCaller: defaultUseShortCaller, } for _, o := range option { o(ol) } ec := zapcore.EncoderConfig{ MessageKey: ol.MessageKey, LevelKey: ol.LevelKey, EncodeLevel: zapcore.CapitalLevelEncoder, TimeKey: ol.TimeKey, EncodeTime: ol.TimeEncoder, CallerKey: ol.CallerKey, EncodeCaller: zapcore.ShortCallerEncoder, EncodeDuration: ol.EncodeDuration, } if !ol.UseShortCaller { ec.EncodeCaller = zapcore.FullCallerEncoder } if !ol.UseJsonFormat { return zapcore.NewConsoleEncoder(ec) } return zapcore.NewJSONEncoder(ec) }