1. 程式人生 > >go開發屬於自己的日誌庫-日誌庫優化

go開發屬於自己的日誌庫-日誌庫優化

日誌庫基本上寫完了,但是就完成了,還沒有。目前來說,我們的檔案寫日誌的方式採用同步方式,如果在大量日誌寫入的時候,同步寫入的缺陷就暴露出來了。而且日誌跟我們的業務邏輯也沒有什麼太大的關係,哪怕掉了幾條也沒什麼影響,所以這裡可以將同步寫改為非同步寫。

這裡採用go的channel進行非同步寫日誌。

  1. 在業務呼叫寫日誌時,將日誌資料寫入channel中
  2. 起一個後臺執行緒不斷的從channel中取日誌,然後寫入到檔案中。

首先,我們定義一個channel的結構體用來傳遞日誌資料,在util.go中新建一個結構體:

type LogData struct {
	Message  string
TimeStr string LevelStr string FileName string FuncName string LineNo int IsWarn bool //是否寫入錯誤日誌檔案 } 複製程式碼

有些這個資料後,我們就可以定義一個channel來進行操作。在file.go中增加一個channel:

type FileLog struct {
	logPath       string
	logName       string
	file          *os.File
	warnFile      *os.File
	logDataChan   chan
*LogData } func NewFileLog(config map[string]string) (logFile Log, err error) { ... logChanSize, ok := config["log_chan_size"] if !ok { logChanSize = "50000" } chanSize, e := strconv.Atoi(logChanSize) // string to int if e != nil { chanSize = 50000 // channel最大容量 } logFile = &FileLog{ logPath: logPath, logName: logName, logDataChan: make
(chan *LogData, chanSize),// 初始化channel } logFile.Init() return } func (f *FileLog) Init() { ... go f.writeLogBackGround() // go 關鍵字啟動一個執行緒 } func (f *FileLog) writeLogBackGround() { for logData := range f.logDataChan { var file = f.file // 切換日誌檔案 if logData.IsWarn { file = f.warnFile } fmt.Fprintf(file, "%s %s [%s/%s:%d] %s\n", logData.TimeStr, logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message) } } func (f *FileLog) Debug(format string, args ...interface{}) { logData := writeLog(DebugLevel, format, args) select { case f.logDataChan <- logData: // 將logData放入f.logDataChan中 default: } } ... func (f *FileLog) Info(format string, args ...interface{}) { logData := writeLog(InfoLevel, format, args) select { case f.logDataChan <- logData: default: } } ... 複製程式碼

將之前的writeLog改進一下.

func writeLog(level int, format string, args ...interface{}) *LogData {
	now := time.Now()
	nowStr := now.Format("2006-01-02 15:04:05.999")
	levelStr := LogLevelString(level)
	fileName, funcName, lineNo := GetLineInfo()
	fileName = path.Base(fileName)
	funcName = path.Base(funcName)
	msg := fmt.Sprintf(format, args...)
  isWarn := level >= WarnLevel && level <= FatalLevel
	return &LogData{
		Message:  msg,
		TimeStr:  nowStr,
		LevelStr: levelStr,
		FileName: fileName,
		FuncName: funcName,
		LineNo:   lineNo,
		IsWarn:   isWarn,
	}
}
複製程式碼

這樣就把同步改為了非同步,我們日誌庫又健壯了一些。 由於writeLog有改動,所以還需要修改一下console.go的內容。

func (c *ConsoleLog) Debug(format string, args ...interface{}) {
	logData := MsgInfo(DebugLevel, format, args)
	fmt.Fprintf(os.Stdout, "%s %s [%s/%s:%d] %s\n", logData.TimeStr, logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message)
}
...
func (c *ConsoleLog) Error(format string, args ...interface{}) {
	logData := MsgInfo(ErrorLevel, format, args)
	fmt.Fprintf(os.Stdout, "%s %s [%s/%s:%d] %s\n", logData.TimeStr, logData.LevelStr, logData.FileName, logData.FuncName, logData.LineNo, logData.Message)
}
...
複製程式碼

雖然我們修改了那麼多,但是在使用日誌庫的時候沒有什麼不同,只是我們的日誌庫更完善了一下。

func initLog(logPath, logName string) {
   //log := hm_log.NewFileLog(logPath, logName)
	config := make(map[string]string, 8)
	config["log_path"] = "."
	config["log_name"] = "server"
	config["log_chan_size"] = "50000" //chan size 可以不用
   err := InitLog("file", config)
	 if err != nil {
		 return
	 }
}

複製程式碼

OK,我們再來執行一下,日誌正常寫入,終端也正常列印。