當前位置:
首頁 > 知識 > log4go的日誌滾動處理——適應生產環境的需要

log4go的日誌滾動處理——適應生產環境的需要

日誌處理有三類使用環境,開發環境DE,測試環境TE,生產環境PE。

前兩類可以看成是一類,重要的是屏幕顯示——termlog。生產環境中主要用的是socklog 和 filelog,即網路傳輸日誌和文件日誌。

基本框架

網路和文件日誌的基本框架非常簡單:

  1. Open file

  2. Write log message

  3. Close file

golang log 都支持。

// New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}

設置不同的io.Writer而已。

type FileWriter struct {
filename string
fileflush int

file *os.File
bufWriter *bufio.Writer
writer io.Writer
}

func (fw *FileWriter) openFile(flag int) (*os.File, error) {
fd, err := os.OpenFile(fw.filename, flag, DefaultFilePerm)
if err != nil {
return nil, err
}

fw.file = fd
fw.writer = fw.file

if fw.fileflush > 0 {
fw.bufWriter = bufio.NewWriterSize(fw.file, fw.fileflush)
fw.writer = fw.bufWriter
}
return fd, nil
}

當然,帶緩衝寫文件的 bufio,一次寫入4k或者8k位元組,效率更高。在另一篇文章中已經討論過了。詳見:

原來的日誌滾動處理

接下來要考慮是日誌文件內容日積月累,越來越大怎麼辦?

在開發和測試環境中,這不是問題。因此常常被忽略,結果進入生產環境後磁碟滿溢,系統癱瘓。

記得還是上世紀94年的時候,半夜坐火車到客戶那裡,在 Novell 伺服器上的執行 purge命令,運行了半個多小時……

所以,日誌的滾動處理非常重要。假設滾動日誌文件數為1。

  1. 日誌文件超過一定的大小,觸發滾動處理。

  2. 將原來存在的文件log.1刪除

  3. 將當前文件重命名為log.1

  4. 等寫入新的日誌時,再判斷文件狀態,建立並打開新的日誌文件。

於是,日誌文件的大小被自動控制在一定範圍內。使伺服器的磁碟自動保持清潔高效的狀態。

log4go v4的觸發滾動處理如下:

func (w *FileLogWriter) LogWrite(rec *LogRecord) {
now := time.Now

if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
(w.daily && now.Day != w.daily_opendate.Day) {
// open the file for the first time
if err := w.intRotate; err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s
", w.filename, err)
return
}
}
}

這意味著:

  1. 精確控制日誌文件的大小

  2. 每次寫入日誌信息都要做一系列複雜判斷

  3. 隨時可能進行滾動日誌處理

  4. rotate = 0時,不進行日誌滾動處理。這可能是個坑。開發和測試的時候,日誌文件的滾動處理可能被忽略。

其實,在生產環境中,什麼時候進行滾動處理,才是真正重要的。通常都會選擇每天凌晨系統較為空閑的時候。

如果是一個24小時滿載的系統,或者對系統穩定性要求特別高,或者對日誌的可靠性要求特別高,建議用socklog。

將日誌信息發送給專門的日誌服務程序進行處理。參照log4go的示常式序,SimpleNetLogServer.go

按照生產環境的要求重寫
  • 始終進行日誌滾動處理。當日誌滾動數為0時,超過預設的文件大小,關閉並刪除當前的日誌文件。

  • 按照生產環境的要求設置預設文件名,目錄,大小,滾動處理的時間和間隔。

  • 精確控制滾動處理的時間。詳見日誌:http://www.cnblogs.com/ccpaging/p/7203431.html

  • 支持任意的時間間隔檢查日誌文件大小。可以每次都轉存日誌。

  • 平時寫入日誌時不再判斷文件大小。

  • 簡化處理流程。寫信息時判斷並建立打開當前日誌文件。進行滾動處理時,關閉當前日誌文件。

  • 繼續寫新的日誌與滾動日誌文件處理,可並行。為將來壓縮日誌文件提供了可能。

保證只啟動一個日誌滾動處理常式

調用日誌處理就一句話:

func (f *FileLogWriter) intRotate {
f.Lock
defer f.Unlock

if n, _ := f.SeekFile(0, os.SEEK_CUR); n <= f.maxsize {
return
}

// File existed and File size > maxsize

if len(f.footer) > 0 { // Append footer
f.WriteString(FormatLogRecord(f.footer, &LogRecord{Created: time.Now}))
}

f.CloseFile

if f.rotate <= 0 {
os.Remove(f.filename)
return
}

// File existed. File size > maxsize. Rotate
newLog := f.filename + time.Now.Format(".20060102-150405")
err := os.Rename(f.filename, newLog)
if err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): Rename to %s. %v
", f.filename, newLog, err)
return
}

go FileRotate(f.filename, f.rotate, newLog) // 調用日誌滾動處理
}

總覺得哪裡不對?如果滾動日誌檢查的時間間隔短,處理的時間意外地長,就有可能出現同時調用兩個常式的情況。 這種情況肯定很少發生。一旦發生,就是個深坑。運維的童鞋要罵娘了……此處省略若干字。

好吧。趕緊做一段程序壓壓驚。

package main

import (
"fmt"
"time"
)

type FileRotate struct {
rotCount int
rotFiles chan string
}

var (
DefaultRotateLen = 5
)

func (r *FileRotate) InitRot {
r.rotCount = 0
r.rotFiles = make(chan string, DefaultRotateLen)
}

func (r *FileRotate) RotFile(filename string, rotate int, newLog string) {
r.rotFiles <- newLog
if r.rotCount > 0 {
fmt.Println("queued", newLog)
return
}

r.rotCount++
fmt.Println("start")
for len(r.rotFiles) > 0 {
file, _ := <- r.rotFiles
fmt.Println("handle", file)
time.Sleep(2 * time.Second)
}
fmt.Println("quit")
r.rotCount--
}

func (r *FileRotate) CloseRot {
for i := 10; i > 0; i-- {
if r.rotCount <= 0 {
break
}
time.Sleep(1 * time.Second)
}

close(r.rotFiles)

// drain the files not rotated
for file := range r.rotFiles {
fmt.Println(file)
}
}

func main {
var r FileRotate;
r.InitRot
for i := 0; i < 5; i++ {
go r.RotFile("filename", 10, fmt.Sprintf("file%d", i))
time.Sleep(1 * time.Second)
}
time.Sleep(5 * time.Second)
for i := 5; i < 10; i++ {
go r.RotFile("filename", 10, fmt.Sprintf("file%d", i))
time.Sleep(1 * time.Second)
}
r.CloseRot
}

希望這段程序能達到以下目的:

  1. 需要時啟動常式。

  2. 只有一個啟動常式。

  3. 退出系統時,等待常式結束,最多10秒。

newLog的格式為:

newLog := f.filename + time.Now.Format(".20060102-150405")

即使滾動日誌處理出現問題,日誌也能保存下來。

那麼,最後的問題是,log4go可以進入生產環境嗎?不試一試?

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

設計模式解密(17)- 備忘錄模式
php nginx反向代理
九大排序演算法整合
Android Binder機制詳解:手寫IPC通信
[Android FrameWork 6.0源碼學習] View的重繪過程之Layout

TAG:達人科技 |

您可能感興趣

cosplay是需要演技的
穿Vetements tabi的人從不需要知道Martin Margiela是誰
python開發利器,python shell和vim中都需要的tab補全方法
魔性的土酷時尚,不需要向任何人解釋的too cool(下)
魔性的土酷時尚,不需要向任何人解釋的too cool(上)
Tomaso Poggio:深度學習需要從鍊金術走向化學
時時科技-Apple 的HomePod 需要iOS 裝置來協同運作
急停變向?你可能需要一雙adidas Harden Vol.2
Forest: 節後收心,你需要這個專註助手 | Hello App
需要Windows才能在發布時運行Oculus Rift
收到學校的waitlist之後,你需要做些什麼
做一個好的設計,需要考慮那些因素byFoster+partners設計事務所
大風降溫,你的Chill-cool知識庫需要更新了
想要讓Xbox One X連接Kinect 需要非常複雜的工程
關於Oculus Rift 2虛擬現實設備我們需要知道的一切
seo需要學習python嗎?學習python有什麼好處?
Google兩步驗證下載Google身份驗證器掃描二維碼時提示需要更新 GooglePlay又無法正常訪問 GoolgePlay
你可能需要一款運動耳機了!我選繽特力BackBeat FIT Boost
Learning Dharma Requires Good Companions 學佛需要善友
Xbox老大Phil Spencer:微軟需要一個復甦的機會