自定义输出符合下列需求:
1.含两类日志输出方式:调试模式下输出到控制台;生产环境输出到日志文件
2.调用不同的函数/方法构造不同的输出方式,后续只需调用日志级别对应的函数即可输出该级别日志
工具构造:
- / mylogger
- mylogger.go 类似python的init.py,怎么叫不知道
- console.go 定义日志输出到控制台方式
- writeFile.go 定义日志写入文件方式
mylogger.go:
1 package mylogger 2 3 import ( 4 "errors" 5 "fmt" 6 "path" 7 "runtime" 8 "strings" 9 ) 10 11 // log level variable 12 type logLevel uint16 13 14 // 对外接口 15 type Logger interface { 16 Debug(format string, a ...interface{}) 17 Trace(format string, a ...interface{}) 18 Info(format string, a ...interface{}) 19 Warning(format string, a ...interface{}) 20 Error(format string, a ...interface{}) 21 Fatal(format string, a ...interface{}) 22 } 23 24 // level 25 const ( 26 UNKNOWN logLevel = iota 27 DEBUG 28 TRACE 29 INFO 30 WARNING 31 ERROR 32 FATAL 33 ) 34 35 // 将等级字符串转换成整形 string -> uint16 36 func parseLogLevel(s string) (logLevel, error){ 37 s = strings.ToLower(s) 38 switch s { 39 case "trace": 40 return TRACE, nil 41 case "debug": 42 return DEBUG, nil 43 case "info": 44 return INFO, nil 45 case "warning": 46 return WARNING, nil 47 case "error": 48 return ERROR, nil 49 case "fatal": 50 return FATAL, nil 51 default: 52 err := errors.New("无效的日志级别") 53 return UNKNOWN, err 54 } 55 } 56 57 // 将整形转换成等级字符串 uint16 -> string 58 func unparseLogLevel(level logLevel) string { 59 switch level { 60 case DEBUG: 61 return "DEBUG" 62 case TRACE: 63 return "TRACE" 64 case INFO: 65 return "INFO" 66 case WARNING: 67 return "WARNING" 68 case ERROR: 69 return "ERROR" 70 case FATAL: 71 return "FATAl" 72 default: 73 return "DEBUG" 74 } 75 } 76 77 // 调用runtime.Caller获取调用log打印的具体代码位置 78 func getInfo(skip int) (funcName, fileName string, lineNo int) { 79 pc, file, lineNo, ok := runtime.Caller(skip) 80 if !ok { 81 fmt.Println("runtime.Caller() failed") 82 return 83 } 84 funcName = runtime.FuncForPC(pc).Name() 85 funcName = strings.Split(funcName, ".")[1] 86 fileName = path.Base(file) 87 return funcName, fileName, lineNo 88 }
console.go:
1 package mylogger 2 3 import ( 4 "fmt" 5 "time" 6 ) 7 8 // console log 结构体 9 type consoleLogger struct { 10 level logLevel 11 } 12 13 // 控制台输出log对象构造函数 14 func NewConsoleLogger(levelStr string) consoleLogger { 15 level, err := parseLogLevel(levelStr) 16 if err != nil { 17 panic(err) 18 } 19 return consoleLogger{level:level} 20 } 21 22 // log输出公共函数 23 func (l consoleLogger) enable (level logLevel, format string, a ...interface{}) { 24 if l.level <= level { 25 // 拼接格式化字符串,格式化可有可无 26 msg := fmt.Sprintf(format, a...) 27 now := time.Now() 28 levelStr := unparseLogLevel(level) 29 funcName, fileName, lineNo := getInfo(3) 30 fmt.Printf("[%s] [%s] [%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"), levelStr, fileName, funcName, lineNo, msg) 31 } 32 } 33 34 // Debug输出 35 func (l consoleLogger) Debug (format string, a ...interface{}) { 36 l.enable(DEBUG, format, a...) 37 } 38 39 // Trace 40 func (l consoleLogger) Trace (format string, a ...interface{}) { 41 l.enable(TRACE, format, a...) 42 } 43 44 // Info 45 func (l consoleLogger) Info (format string, a ...interface{}) { 46 l.enable(INFO, format, a...) 47 } 48 49 // Warning 50 func (l consoleLogger) Warning (format string, a ...interface{}) { 51 l.enable(WARNING, format, a...) 52 } 53 54 // Error 55 func (l consoleLogger) Error (format string, a ...interface{}) { 56 l.enable(ERROR, format, a...) 57 } 58 59 // Fatal 60 func (l consoleLogger) Fatal (format string, a ...interface{}) { 61 l.enable(FATAL, format, a...) 62 }
writeFile.go:
1 package mylogger 2 3 import ( 4 "fmt" 5 "os" 6 "path" 7 "time" 8 ) 9 10 // file log结构体 11 type fileLogger struct { 12 level logLevel 13 filePath string 14 fileName string 15 fileObj *os.File 16 errfileObj *os.File 17 maxFileSize int64 18 } 19 20 // 文件日志对象构造函数 21 func NewFileLogger(levelStr, fp, fn string, maxsize int64) *fileLogger { 22 level, err := parseLogLevel(levelStr) 23 if err != nil { 24 panic(err) 25 } 26 f1 := &fileLogger{level:level, filePath:fp, fileName:fn, maxFileSize:maxsize} 27 err = f1.initFile() 28 if err != nil { 29 panic(err) 30 } 31 return f1 32 } 33 34 // 初始化打开日志文件并赋值给file log结构体 35 func (f *fileLogger) initFile() error { 36 fileObj, err1 := os.OpenFile(path.Join(f.filePath, f.fileName), os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0644) 37 if err1 != nil { 38 fmt.Printf("open log file failed, err:%v\n", err1) 39 return err1 40 } 41 f.fileObj = fileObj 42 43 errfileObj, err2 := os.OpenFile(path.Join(f.filePath, fmt.Sprintf("%s.error", f.fileName)), os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0644) 44 //errfileObj, err2 := os.OpenFile(path.Join(f.filePath, "error", f.fileName), os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0644) 45 if err2 != nil { 46 fmt.Printf("open error log file failed, err:%v\n", err2) 47 return err2 48 } 49 f.errfileObj = errfileObj 50 return nil 51 } 52 53 // 关闭文件 54 func (f *fileLogger) Close() { 55 f.fileObj.Close() 56 f.errfileObj.Close() 57 } 58 59 // 切割文件函数 60 func (l *fileLogger) isCuttingFile(f *os.File) (*os.File, error) { 61 fileInfo, err := f.Stat() 62 if err != nil { 63 fmt.Printf("get file info failed, err:%v\n", err) 64 return f, nil 65 } 66 if fileInfo.Size() >= l.maxFileSize { 67 LogName := path.Join(l.filePath, fileInfo.Name()) 68 newLogName := fmt.Sprintf("%s.bak%s", LogName, time.Now().Format("20060102150405")) 69 // 关闭文件 70 f.Close() 71 // 给原文件重命名 72 os.Rename(LogName, newLogName) 73 // 设置为新的文件操作符 74 fileObj, err := os.OpenFile(LogName, os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0644) 75 if err != nil { 76 fmt.Printf("open new file failed, err:%v", err) 77 return nil, err 78 } 79 return fileObj, nil 80 } 81 return f, nil 82 } 83 84 // log输出公共函数 85 func (l *fileLogger) enable (level logLevel, format string, a ...interface{}) { 86 if l.level <= level { 87 // 拼接格式化字符串,格式化可有可无 88 msg := fmt.Sprintf(format, a...) 89 now := time.Now() 90 levelStr := unparseLogLevel(level) 91 funcName, fileName, lineNo := getInfo(3) 92 // 切割文件 93 fileObj, err := l.isCuttingFile(l.fileObj) 94 if err != nil { 95 panic(err) 96 } 97 l.fileObj = fileObj 98 fmt.Fprintf(l.fileObj, "[%s] [%s] [%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"), levelStr, fileName, funcName, lineNo, msg) 99 //如果等级 >= Warning,写入日志到errFile 100 if level >= WARNING { 101 fileObj, err := l.isCuttingFile(l.errfileObj) 102 if err != nil { 103 panic(err) 104 } 105 l.errfileObj = fileObj 106 fmt.Fprintf(l.errfileObj, "[%s] [%s] [%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"), levelStr, fileName, funcName, lineNo, msg) 107 } 108 } 109 } 110 111 // Debug输出 112 func (l *fileLogger) Debug (format string, a ...interface{}) { 113 l.enable(DEBUG, format, a...) 114 } 115 116 // Trace 117 func (l *fileLogger) Trace (format string, a ...interface{}) { 118 l.enable(TRACE, format, a...) 119 } 120 121 // Info 122 func (l *fileLogger) Info (format string, a ...interface{}) { 123 l.enable(INFO, format, a...) 124 } 125 126 // Warning 127 func (l *fileLogger) Warning (format string, a ...interface{}) { 128 l.enable(WARNING, format, a...) 129 } 130 131 // Error 132 func (l *fileLogger) Error (format string, a ...interface{}) { 133 l.enable(ERROR, format, a...) 134 } 135 136 // Fatal 137 func (l *fileLogger) Fatal (format string, a ...interface{}) { 138 l.enable(FATAL, format, a...) 139 }
简单使用:
1 package main 2 3 import ( 4 mylogger "utils/mylogger" 5 "time" 6 ) 7 8 var logger mylogger.Logger 9 10 func main() { 11 // console 12 logger = mylogger.NewConsoleLogger("info") 13 // file 14 //logger := mylogger.NewFileLogger("Info", "/home/xxx/logs/gologs", "2021-11-11.log", 500*1024*1024) 15 for { 16 logger.Trace("这是一条trace记录") 17 logger.Debug("这是一条debug记录") 18 logger.Info("这是一条info记录") 19 // format string 20 name := "唐僧" 21 logger.Warning("%s说:这是一条warning记录", name) 22 logger.Error("这是一条error记录") 23 logger.Fatal("这是一条fatal记录") 24 time.Sleep(time.Second) 25 } 26 }
Comments