Go语言系列之自定义实现日志库
Posted zhangyafei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言系列之自定义实现日志库相关的知识,希望对你有一定的参考价值。
日志库需求分析
1. 支持往不同的地方输出日志
2. 日志分级别
- Debug
- Trace
- Info
- Warning
- Error
- Fatal
3. 日志要支持开关控制
4. 完整的日志记录要包含时间、行号、文件名、日志级别、日志信息
5. 日志文件要切割
- 按文件大小切割
- 按riq切割
具体实现
目录结构
├─mylogger │ console.go │ file.go │ myLogger.go
myLogger.go
package mylogger import ( "errors" "fmt" "runtime" "strings" ) type logLevel uint8 type Logger interface { Debug(format string, a ...interface{}) Trace(format string, a ...interface{}) Info(format string, a ...interface{}) Warning(format string, a ...interface{}) Error(format string, a ...interface{}) Fatal(format string, a ...interface{}) SetPrefix(prefix string) } const ( UnKnow logLevel = iota DebugLevel TraceLevel InfoLevel WarningLevel ErrorLevel FatalLevel ) var ( levels = map[logLevel]string{ DebugLevel: "Debug", TraceLevel: "Trace", InfoLevel: "Info", WarningLevel: "Warning", ErrorLevel: "Error", FatalLevel: "Fatal", } ) func (ll logLevel) String () string { return levels[ll] } func parseLogLevel(s string) (logLevel, error) { s = strings.ToLower(s) switch s { case "debug": return DebugLevel, nil case "trace": return TraceLevel, nil case "info": return InfoLevel, nil case "warning": return WarningLevel, nil case "error": return ErrorLevel, nil case "fatal": return FatalLevel, nil default: return UnKnow, errors.New("无效的日志级别") } } func getInfo(n int) (string, error) { pc, fileName, lineNo, ok := runtime.Caller(n) if !ok { return "", errors.New("runtime.Caller() failed ") } funcName := runtime.FuncForPC(pc).Name() funcName = strings.Split(funcName, ".")[1] return fmt.Sprintf("%s:%s:%d", fileName, funcName, lineNo), nil }
console.go
package mylogger import ( "fmt" "time" ) type ConsoleLogger struct { Level logLevel Prefix string } func (cl *ConsoleLogger) Print(level logLevel, format string, a ...interface{}) { if cl.Level <= level { msg := fmt.Sprintf(format, a...) now := time.Now() info, err := getInfo(3) if err != nil { fmt.Println(err.Error()) }else if cl.Prefix == "" { fmt.Printf("%s ? %s %s %s ", now.Format("2006/01/02 - 15:04:05"), level, info, msg) }else { fmt.Printf("%s %s ? %s %s %s ", cl.Prefix, now.Format("2006/01/02 - 15:04:05"), level, info, msg) } } } func (cl *ConsoleLogger) SetPrefix(prefix string) { cl.Prefix = prefix } // ConsoleLogger 构造函数 func NewConsoleLog(Level string) *ConsoleLogger { level, err := parseLogLevel(Level) if err != nil { panic(err) } return &ConsoleLogger{Level:level} } func (cl *ConsoleLogger) Debug(format string, a ...interface{}) { cl.Print(DebugLevel, format, a...) } func (cl *ConsoleLogger) Trace(format string, a ...interface{}) { cl.Print(TraceLevel, format, a...) } func (cl *ConsoleLogger) Info(format string, a ...interface{}) { cl.Print(InfoLevel, format, a...) } func (cl *ConsoleLogger) Warning(format string, a ...interface{}) { cl.Print(WarningLevel, format, a...) } func (cl *ConsoleLogger) Error(format string, a ...interface{}) { cl.Print(ErrorLevel, format, a...) } func (cl *ConsoleLogger) Fatal(format string, a ...interface{}) { cl.Print(FatalLevel, format, a...) }
file.go
package mylogger import ( "fmt" "os" "path" "time" ) type FileLogger struct { // 日志级别 比该级别高的打印的时候会显示 Level logLevel `json:"level"` // 前缀 打印日志信息的前缀 Prefix string `json:"prefix"` // 日志文件夹路径 FilePath string `json:"file_path"` // 日志文件名 FileName string `json:"file_name"` // 最大文件大小 分割日志文件的时候会用到 MaxFileSize int64 `json:"max_file_size"` // 最大时间间隔 按时间分割文件的时候会用到 MaxAge int `json:"max_age"` // 记录上次切割文件的时间 LastSplitTime time.Time // 日志文件对象 FileObj *os.File // Error日志文件对象 ErrFileObj *os.File } // FileLogger 构造函数 func NewFileLog(Level string, fp, fn string, maxSize int64) *FileLogger { level, err := parseLogLevel(Level) if err != nil { panic(err) } fl := &FileLogger{ Level: level, FilePath: fp, FileName: fn, MaxFileSize: maxSize, } err = fl.initFile() if err != nil { panic(err) } return fl } // FileLogger 构造函数 func NewFileLogWithMaxAge(Level string, fp, fn string, maxSize int64, maxAge int) *FileLogger { level, err := parseLogLevel(Level) if err != nil { panic(err) } fl := &FileLogger{ Level: level, FilePath: fp, FileName: fn, MaxFileSize: maxSize, MaxAge:maxAge, LastSplitTime:time.Now(), } err = fl.initFile() if err != nil { panic(err) } return fl } func (fl *FileLogger) CheckFileSize (file *os.File) bool { fileInfo, err := file.Stat() if err != nil { fmt.Printf("get file info failed,err:%v ", err) return false } return fileInfo.Size() >= fl.MaxFileSize } func (fl *FileLogger) CheckFileLastAge (now time.Time) bool { if fl.LastSplitTime == (time.Time{}){ return false } return int(now.Sub(fl.LastSplitTime).Hours()) > fl.MaxAge } func (fl *FileLogger) SetPrefix(prefix string) { fl.Prefix = prefix } func (fl *FileLogger) Print(level logLevel, format string, a ...interface{}) { if fl.Level <= level { msg := fmt.Sprintf(format, a...) now := time.Now() info, err := getInfo(3) if err != nil { fmt.Println(err.Error()) }else { if fl.CheckFileSize(fl.FileObj) || fl.CheckFileLastAge(now) { newFileObje, err := fl.splitFile(fl.FileObj) if err != nil { return } fl.FileObj = newFileObje } if fl.Prefix == "" { fmt.Fprintf(fl.FileObj, "%s ? %s %s %s ", now.Format("2006/01/02 - 15:04:05"), level, info, msg) }else { fmt.Fprintf(fl.FileObj, "%s %s ? %s %s %s ", fl.Prefix, now.Format("2006/01/02 - 15:04:05"), level, info, msg) } if level >= ErrorLevel { if fl.CheckFileSize(fl.ErrFileObj) || fl.CheckFileLastAge(now){ newFileObje, err := fl.splitFile(fl.ErrFileObj) if err != nil { return } fl.ErrFileObj = newFileObje } if fl.Prefix == "" { fmt.Fprintf(fl.ErrFileObj, "%s ? %s %s %s ", now.Format("2006/01/02 - 15:04:05"), level, info, msg) } else { fmt.Fprintf(fl.ErrFileObj, "%s %s ? %s %s %s ", fl.Prefix, now.Format("2006/01/02 - 15:04:05"), level, info, msg) } } } } } func (fl *FileLogger) initFile() error { fullFileName := path.Join(fl.FilePath, fl.FileName) fileObj, err := os.OpenFile(fullFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Printf("open log file failed, err:%v", err) return err } errFileObj, err := os.OpenFile(fullFileName+".err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Printf("open err log file failed, err:%v", err) return err } fl.FileObj = fileObj fl.ErrFileObj = errFileObj return nil } func (fl *FileLogger) splitFile(file *os.File) (*os.File, error) { nowStr := time.Now().Format("20060102150405000") fileInfo, err := file.Stat() if err != nil { fmt.Printf("get file info failed,err:%v ", err) return nil, err } logName := path.Join(fl.FilePath, fileInfo.Name()) newlogName := fmt.Sprintf("%s.%s.bak", logName, nowStr) // 1. 关闭当前文件 file.Close() // 2. 备份一个 rename os.Rename(logName, newlogName) // 3. 打开一个新的日志文件 fileObj, err := os.OpenFile(logName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Printf("open log file failed, err:%v", err) return nil, err } // 4. 将打开的文件赋值给 fl.FileObj return fileObj, nil } func (fl *FileLogger) Debug(format string, a ...interface{}) { fl.Print(DebugLevel, format, a...) } func (fl *FileLogger) Trace(format string, a ...interface{}) { fl.Print(TraceLevel, format, a...) } func (fl *FileLogger) Info(format string, a ...interface{}) { fl.Print(InfoLevel, format, a...) } func (fl *FileLogger) Warning(format string, a ...interface{}) { fl.Print(WarningLevel, format, a...) } func (fl *FileLogger) Error(format string, a ...interface{}) { fl.Print(ErrorLevel, format, a...) } func (fl *FileLogger) Fatal(format string, a ...interface{}) { fl.Print(FatalLevel, format, a...) }
使用示例
1. console打印
package main import "go_Logger/mylogger" var myLog mylogger.Logger func main() { myLog := mylogger.NewConsoleLog("debug") myLog.SetPrefix("[MY-LOG]") myLog.Debug("这是一条Debug日志") myLog.Trace("这是一条Trace日志") myLog.Info("这是一条Info日志") myLog.Warning("这是一条Warning日志") name := "zhangyafei" myLog.Error("这是一条Error日志, name:%s", name) myLog.Fatal("这是一条Fatal日志") }
终端
E:goprojectsrcgo_Logger>go_Logger.exe [MY-LOG] 2020/06/11 - 12:22:04 ? Debug E:/go/project/src/go_Logger/main.go:main:25 这是一条Debug日志 [MY-LOG] 2020/06/11 - 12:22:04 ? Trace E:/go/project/src/go_Logger/main.go:main:26 这是一条Trace日志 [MY-LOG] 2020/06/11 - 12:22:04 ? Info E:/go/project/src/go_Logger/main.go:main:27 这是一条Info日志 [MY-LOG] 2020/06/11 - 12:22:04 ? Warning E:/go/project/src/go_Logger/main.go:main:28 这是一条Warning日志 [MY-LOG] 2020/06/11 - 12:22:04 ? Error E:/go/project/src/go_Logger/main.go:main:30 这是一条Error日志, name:zhangyafei [MY-LOG] 2020/06/11 - 12:22:04 ? Fatal E:/go/project/src/go_Logger/main.go:main:31 这是一条Fatal日志
2. 写入文件-按文件大小切割
package main import "go_Logger/mylogger" var myLog mylogger.Logger func main() { myLog = mylogger.NewFileLog("info", "log", "mylog.log", 10*1024*1024) myLog.SetPrefix("[MY-LOG]") myLog.Debug("这是一条Debug日志") myLog.Trace("这是一条Trace日志") myLog.Info("这是一条Info日志") myLog.Warning("这是一条Warning日志") name := "zhangyafei" myLog.Error("这是一条Error日志, name:%s", name) myLog.Fatal("这是一条Fatal日志") }
log/mylog.log
[MY-LOG] 2020/06/11 - 12:24:20 ? Info E:/go/project/src/go_Logger/main.go:main:27 这是一条Info日志 [MY-LOG] 2020/06/11 - 12:24:20 ? Warning E:/go/project/src/go_Logger/main.go:main:28 这是一条Warning日志 [MY-LOG] 2020/06/11 - 12:24:20 ? Error E:/go/project/src/go_Logger/main.go:main:30 这是一条Error日志, name:zhangyafei [MY-LOG] 2020/06/11 - 12:24:20 ? Fatal E:/go/project/src/go_Logger/main.go:main:31 这是一条Fatal日志
log/mylog.log.err
[MY-LOG] 2020/06/11 - 12:24:20 ? Error E:/go/project/src/go_Logger/main.go:main:30 这是一条Error日志, name:zhangyafei [MY-LOG] 2020/06/11 - 12:24:20 ? Fatal E:/go/project/src/go_Logger/main.go:main:31 这是一条Fatal日志
文件大小切割
package main import "go_Logger/mylogger" var myLog mylogger.Logger func main() { myLog = mylogger.NewFileLog("info", "log", "mylog.log", 10*1024*1024) myLog.SetPrefix("[MY-LOG]") for { myLog.Debug("这是一条Debug日志") myLog.Trace("这是一条Trace日志") myLog.Info("这是一条Info日志") myLog.Warning("这是一条Warning日志") name := "zhangyafei" myLog.Error("这是一条Error日志, name:%s", name) myLog.Fatal("这是一条Fatal日志") } }
3. 写入文件-按文件大小和时间切割
package main import "go_Logger/mylogger" var myLog mylogger.Logger func main() { myLog = mylogger.NewFileLogWithMaxAge("Debug", "log", "mylog.log", 10*1024*1024, 24) myLog.SetPrefix("[MY-LOG]") for { myLog.Debug("这是一条Debug日志") myLog.Trace("这是一条Trace日志") myLog.Info("这是一条Info日志") myLog.Warning("这是一条Warning日志") name := "zhangyafei" myLog.Error("这是一条Error日志, name:%s", name) myLog.Fatal("这是一条Fatal日志") } }
以上是关于Go语言系列之自定义实现日志库的主要内容,如果未能解决你的问题,请参考以下文章