在實際開發過程中,為了節省磁盤,日志需要按照時間或者大小維度進行切割分成多分,歸檔過期的日志,刪除久遠的日志.這個就是在日常開發中經常遇見的日志滾動(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()函數
}
滾動日志的各項參數如注釋所示, logrus
的setOutput()
函數的入參是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()
}
因為本人水平有限,有什么問題或者疑問歡迎在評論區指正