自定義go語言日誌輸出
阿新 • • 發佈:2021-11-25
自定義輸出符合下列需求:
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 }