1. 程式人生 > >go web: 2 封裝日誌包log

go web: 2 封裝日誌包log

在web專案中,記日誌是非常重要的。所以,我做的第一件事,就是向log包動手。
和Python相比,log包功能上遜色不少,但它給我們提供了基礎的構架,讓我們能自己稍微封裝下。

需求

對日誌包我的要求很低,只要滿足:
1. 提供Error, Info方法即可
2. 日誌按天分割,即每隔一天,把昨天的日誌儲存為 logname.20170823這樣的檔案

程式碼

在原來的基礎上,我們在src中建立資料夾logger,在裡面建立檔案logger.go
現在檔案結構如下:

src--|
     handlers--|
               test--|
                     test.go
     logger--|
logger.go | main.go

這個檔案程式碼有點長,所以放附錄了。
要使用,只需要在main.go裡呼叫:

logger.InitLogging("8080", logger.DEBUG)
logger.Errorln("%s %s", "hi", "my boy")

然後,在bin檔案的同級,手工建立logs資料夾。執行程式,日誌功能就開始執行了。
測試了一下效率,在mac pro上。10萬行日誌大概400毫秒。湊合著用還行。

附錄logger.go程式碼

// Package logger 是系統日誌的封裝,主要在之上封裝了Error,Info兩個函式。並提供了跨日期
// 自動分割日誌檔案的功能。 // 可以在InitLogging 後直接使用logger.Error, logger.Info操作預設的日誌物件。 // 也可以用logger.New 建立一個自己的日誌物件。 package logger import ( "bytes" "fmt" "io" "log" "os" "path/filepath" "runtime" "strconv" "time" ) //logging 是一個預設的日誌物件,提供全域性的Error, Info函式供使用,必須呼叫InitLogging //函式進行初始化
var logging *Logger var DEBUG = 0 var INFO = 3 var ERROR = 5 //InitLogging 初始化預設的日誌物件,初始化後,就能使用Error,Info函式記錄日誌 func InitLogging(inputfilename string, level int) { logging = New(inputfilename, true, false, level, 3) } //Error 預設日誌物件方法,記錄一條錯誤日誌,需要先初始化 func Error(format string, v ...interface{}) { logging.Error(format, v...) } //Errorln 預設日誌物件方法,記錄一條訊息日誌,需要先初始化 func Errorln(args ...interface{}) { logging.Errorln(args...) } //Info 預設日誌物件方法,記錄一條訊息日誌,需要先初始化 func Info(format string, v ...interface{}) { logging.Info(format, v...) } //Infoln 預設日誌物件方法,記錄一條訊息日誌,需要先初始化 func Infoln(args ...interface{}) { logging.Infoln(args...) } //Debug 預設日誌物件方法,記錄一條訊息日誌,需要先初始化 func Debug(format string, v ...interface{}) { logging.Debug(format, v...) } //Debugln 預設日誌物件方法,記錄一條除錯日誌,需要先初始化 func Debugln(args ...interface{}) { logging.Debugln(args...) } type Logger struct { level int // debug 0 info 3 err 5 innerLogger *log.Logger curFile *os.File todaydate string filename string runtimeCaller int logFilePath bool logFunc bool msgQueue chan string // 所有的日誌先到這來 closed bool } //New 建立一個自己的日誌物件。 // filename:在logs資料夾下建立的檔名 // logFilePath: 日誌中記錄檔案路徑 // logFunc: 日誌中記錄呼叫函式 // level: 列印等級。DEBUG, INFO, ERROR // runtimeCaller: 檔案路徑深度,設定適當的值,否則檔案路徑不正確 func New(filename string, logFilePath bool, logFunc bool, level int, runtimeCaller int) *Logger { // result := newLogger(logFile, flag) result := new(Logger) result.msgQueue = make(chan string, 1000) result.closed = false var multi io.Writer if filename != "" { dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) logFile, err := os.OpenFile(dir+"/logs/"+filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { fmt.Println(err.Error()) } result.curFile = logFile fmt.Println("newLogger use MultiWriter") multi = io.MultiWriter(logFile, os.Stdout) } else { result.curFile = nil fmt.Println("newLogger use stdout") multi = os.Stdout } result.innerLogger = log.New(multi, "", 0) result.filename = filename result.runtimeCaller = runtimeCaller result.logFilePath = logFilePath result.logFunc = logFunc result.level = level result.todaydate = time.Now().Format("2006-01-02") // 啟動日誌切換 go result.logworker() return result } // Close 關閉這一個日誌物件 func (logobj *Logger) Close() error { logobj.closed = true return nil } func (logobj *Logger) getFormat(prefix, format string) string { var buf bytes.Buffer // 增加時間 buf.WriteString(time.Now().Format("2006-01-02 15:04:05 ")) buf.WriteString(prefix) // 增加檔案和行號 funcName, file, line, ok := runtime.Caller(logobj.runtimeCaller) if ok { if logobj.logFilePath { buf.WriteString(filepath.Base(file)) buf.WriteString(":") buf.WriteString(strconv.Itoa(line)) buf.WriteString(" ") } if logobj.logFunc { buf.WriteString(runtime.FuncForPC(funcName).Name()) buf.WriteString(" ") } buf.WriteString(format) format = buf.String() } return format } //Error 記錄一條錯誤日誌 func (logobj *Logger) Error(format string, v ...interface{}) { if logging.level > 5 { return } format = logobj.getFormat("ERROR ", format) logobj.msgQueue <- fmt.Sprintf(format, v...) } //Errorln 列印一行錯誤日誌 func (logobj *Logger) Errorln(args ...interface{}) { if logging.level > 5 { return } prefix := logobj.getFormat("ERROR ", "") logobj.msgQueue <- fmt.Sprintln(append([]interface{}{prefix}, args...)...) } //Info 記錄一條訊息日誌 func (logobj *Logger) Info(format string, v ...interface{}) { if logging.level > 3 { return } format = logobj.getFormat("INFO ", format) logobj.msgQueue <- fmt.Sprintf(format, v...) } //Infoln 列印一行訊息日誌 func (logobj *Logger) Infoln(args ...interface{}) { if logging.level > 3 { return } prefix := logobj.getFormat("INFO ", "") logobj.msgQueue <- fmt.Sprintln(append([]interface{}{prefix}, args...)...) } //Debug 記錄一條訊息日誌 func (logobj *Logger) Debug(format string, v ...interface{}) { if logging.level > 0 { return } format = logobj.getFormat("DEBUG ", format) logobj.msgQueue <- fmt.Sprintf(format, v...) } //Debugln 列印一行除錯日誌 func (logobj *Logger) Debugln(args ...interface{}) { if logging.level > 0 { return } prefix := logobj.getFormat("DEBUG ", "") logobj.msgQueue <- fmt.Sprintln(append([]interface{}{prefix}, args...)...) } func (logobj *Logger) logworker() { for logobj.closed == false { msg := <-logobj.msgQueue logobj.innerLogger.Println(msg) //跨日改時間,後臺啟動 nowDate := time.Now().Format("2006-01-02") if nowDate != logobj.todaydate { logobj.Debug("doRotate run %v %v", nowDate, logging.todaydate) logobj.doRotate() } } } func (logobj *Logger) doRotate() { // 日誌按天切換檔案,日誌物件記錄了程式啟動時的時間,噹噹前時間和程式啟動的時間不一致 // 則會啟動到這個函式來改變檔案 // 首先關閉檔案控制代碼,把當前日誌改名為昨天,再建立新的檔案控制代碼,將這個檔案控制代碼賦值給log物件 // 最後嘗試刪除5天前的日誌 fmt.Println("doRotate run") defer func() { rec := recover() if rec != nil { fmt.Printf("doRotate %v", rec) } }() if logobj.curFile == nil { fmt.Println("doRotate curfile nil, return") return } dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) prefile := logobj.curFile _, err := prefile.Stat() if err == nil { filePath := dir + "/logs/" + logobj.filename err := prefile.Close() fmt.Printf("doRotate close err %v", err) nowTime := time.Now() time1dAgo := nowTime.Add(-1 * time.Hour * 24) err = os.Rename(filePath, filePath+"."+time1dAgo.Format("2006-01-02")) fmt.Printf("doRotate rename err %v", err) } if logobj.filename != "" { nextfile, err := os.OpenFile(dir+"/logs/"+logobj.filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) if err != nil { fmt.Println(err.Error()) } logobj.curFile = nextfile fmt.Println("newLogger use MultiWriter") multi := io.MultiWriter(nextfile, os.Stdout) logobj.innerLogger.SetOutput(multi) } fmt.Println("doRotate ending") // 更新標記,這個標記決定是否會啟動檔案切換 nowDate := time.Now().Format("2006-01-02") logobj.todaydate = nowDate logobj.deleteHistory() } func (logobj *Logger) deleteHistory() { // 嘗試刪除5天前的日誌 fmt.Println("deleteHistory run") nowTime := time.Now() time5dAgo := nowTime.Add(-1 * time.Hour * 24 * 5) dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) filePath := dir + "/logs/" + logobj.filename + "." + time5dAgo.Format("2006-01-02") _, err := os.Stat(filePath) if err == nil { os.Remove(filePath) } }