logrus 剖析之滾動日志


在實際開發過程中,為了節省磁盤,日志需要按照時間或者大小維度進行切割分成多分,歸檔過期的日志,刪除久遠的日志.這個就是在日常開發中經常遇見的日志滾動(log rotation)

那么在 logrus 中我們該如何實現這個功能呢? logrus本身並沒有實現滾動日志功能,但是我們可以使用第三方滾動插件實現.

滾動日志

我們需要使用lumberjack實現logrus的滾動日志,具體實現如下:

package main

import (
	log "github.com/Sirupsen/logrus"
	"gopkg.in/natefinch/lumberjack.v2"
)

func main() {
	logger := &lumberjack.Logger{
    // 日志輸出文件路徑
		Filename:   "/var/log/myapp/foo.log", 
    // 日志文件最大 size, 單位是 MB
		MaxSize:    500, // megabytes
    // 最大過期日志保留的個數
		MaxBackups: 3,
    // 保留過期文件的最大時間間隔,單位是天
		MaxAge:     28,   //days
    // 是否需要壓縮滾動日志, 使用的 gzip 壓縮
		Compress:   true, // disabled by default
	}
  log.SetOutput(logger) //調用 logrus 的 SetOutput()函數
}

滾動日志的各項參數如注釋所示, logrussetOutput()函數的入參是io.Writer類型, lumberjack.Logger實現了該接口.下文我們將對lumberjack.Logger的部分重要結構和函數稍作解釋.

lumberjack

logger 結構

type Logger struct {
	// 日志文件路徑
	Filename string `json:"filename" yaml:"filename"`
	// 最大文件 size, 默認 100MB
	MaxSize int `json:"maxsize" yaml:"maxsize"`
  // 保留過期文件的最大時間間隔,單位是天(24h)
	MaxAge int `json:"maxage" yaml:"maxage"`
	// 最大過期日志保留的個數,默認都保留
	MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
	// 是否使用時間戳命名 backup 日志, 默認使用 UTC 格式
	LocalTime bool `json:"localtime" yaml:"localtime"`
	// 是否壓縮過期日志
	Compress bool `json:"compress" yaml:"compress"`
	size int64
	file *os.File
	mu   sync.Mutex

	millCh    chan bool
	startMill sync.Once
}

寫日志操作

func (l *Logger) Write(p []byte) (n int, err error) {
	l.mu.Lock()
	defer l.mu.Unlock()

	writeLen := int64(len(p))
	if writeLen > l.max() {
		return 0, fmt.Errorf(
			"write length %d exceeds maximum file size %d", writeLen, l.max(),
		)
	}

	if l.file == nil {
		if err = l.openExistingOrNew(len(p)); err != nil {
			return 0, err
		}
	}
	// 比較文件 size,超過指定最大 size 就需要滾動一次
	if l.size+writeLen > l.max() {
    // rotate 其實就是重新創建了一個新文件
		if err := l.rotate(); err != nil {
			return 0, err
		}
	}

	n, err = l.file.Write(p)
	l.size += int64(n)

	return n, err
}

滾動日志:

func (l *Logger) rotate() error {
  // 關閉當前文件句柄
	if err := l.close(); err != nil {
		return err
	}
  // 創建並打開新文件
	if err := l.openNew(); err != nil {
		return err
	}
  // 壓縮日志文件,刪除過期日志文件,內部是個協程去做過期日志清理任務
	l.mill()
	return nil
}

關閉日志文件

func (l *Logger) Close() error {
	l.mu.Lock()
	defer l.mu.Unlock()
	return l.close()
}

因為本人水平有限,有什么問題或者疑問歡迎在評論區指正


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM