Golang logrus的高级配置(hook, logrotate)
前言:
这次聊下最近使用logrus的心得,没有高深的源码,只是一些相对高级的配置吧。golang默认的log模块略显简陋,连基本的info, warn, error 打印方法都没有,不是太适用。 在使用logrus模块之前,我先前都在使用自己封装的log模块,虽然比不上logrus那么多功能,够用是没问题的。
话说,golang社区里排名最高的日志模块就那么几个,logrus,log4j … 单看github的活跃度logrus显得更显一筹了。国内外比较出名的golang开源项目,大多都是自己实现的日志磨矿。
该文章后续会有更新, 原文地址 xiaorui.cc/?p=4963
logrus日志切分功能 ?
对的,logrus默认是没有像logrotate那样的日志切割功能,我曾经在issue里问过sirupsen, 为毛没有日志基本的logrotate ? 他的回复说,不需要切分,首先该功能不是核心功能,最重要的是 作者建议大家把日志输出到其他的日志server里,比如 filebeat, logstash, syslog, rsyslog, Fluentd 等等。 logrus官方支持的日志服务列表有很多,只要我们能想到的基本都有。
那么,如果我现在logrus实现日志切分,该如何操作?两种方法.
第一种,借助于 linux logrotate命令 。
/devops/app/go/src/task_dispatcher/logs/*.log
{
daily
sharedscripts
dateext
nocompress
size 1G
missingok
notifempty
copytruncate
rotate 3
第二种方法, 使用logrus的hook来加载 github.com/lestrrat/go-file-rotatelogs 模块. 每次当我们写入日志的时候,logrus都会调用 go-file-rotatelogs 来判断日志是否要进行切分… go-file-rotatelogs 可以实现linux logratate的大多数功能。
package context
import (
"github.com/lestrrat/go-file-rotatelogs"
"github.com/rifflock/lfshook"
log "github.com/sirupsen/logrus"
"github.com/pkg/errors"
"path"
"time"
"os"
"fmt"
"bufio"
)
func InitLogger() {
baseLogPath := path.Join(GlobalConfig.LogConf.Logdir,
GlobalConfig.LogConf.Filename)
writer, err := rotatelogs.New(
baseLogPath+".%Y%m%d%H%M",
rotatelogs.WithLinkName(baseLogPath), // 生成软链,指向最新日志文件
rotatelogs.WithMaxAge(7*24*time.Hour), // 文件最大保存时间
rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔
)
if err != nil {
log.Errorf("config local file system logger error. %v", errors.WithStack(err))
}
//log.SetFormatter(&log.TextFormatter{})
switch level := GlobalConfig.LogConf.LogLevel; level {
/*
如果日志级别不是debug就不要打印日志到控制台了
*/
case "debug":
log.SetLevel(log.DebugLevel)
log.SetOutput(os.Stderr)
case "info":
setNull()
log.SetLevel(log.InfoLevel)
case "warn":
setNull()
log.SetLevel(log.WarnLevel)
case "error":
setNull()
log.SetLevel(log.ErrorLevel)
default:
setNull()
log.SetLevel(log.InfoLevel)
}
lfHook := lfshook.NewHook(lfshook.WriterMap{
log.DebugLevel: writer, // 为不同级别设置不同的输出目的
log.InfoLevel: writer,
log.WarnLevel: writer,
log.ErrorLevel: writer,
log.FatalLevel: writer,
log.PanicLevel: writer,
})
log.AddHook(lfHook)
}
func setNull() {
src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err!= nil{
fmt.Println("err", err)
}
writer := bufio.NewWriter(src)
log.SetOutput(writer)
}
既然作者这么推荐大家使用logrus hook的方式来扩展日志功能,那么怎么自己写一个hook模块? 其实很简单,只需要实现这几个方法就可以了,主要看 Fire 函数… 当我们调用logrus.addHook把自定义的hook加载后,每次我们写日志的时候,都会调用Fire方法。
logrus hook 是一个值得深入学习的设计,你可以轻易适用hook来实现多文件写入。 比如,error级别的日志独立输出到error.log文件里,其他都放在一起。
package syslog
import (
"fmt"
"log/syslog"
"os"
"github.com/sirupsen/logrus"
)
type SyslogHook struct {
Writer *syslog.Writer
SyslogNetwork string
SyslogRaddr string
}
func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
w, err := syslog.Dial(network, raddr, priority, tag)
return &SyslogHook{w, network, raddr}, err
}
func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
line, err := entry.String()
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
return err
}
switch entry.Level {
case logrus.PanicLevel:
return hook.Writer.Crit(line)
case logrus.FatalLevel:
return hook.Writer.Crit(line)
case logrus.ErrorLevel:
return hook.Writer.Err(line)
case logrus.WarnLevel:
return hook.Writer.Warning(line)
case logrus.InfoLevel:
return hook.Writer.Info(line)
case logrus.DebugLevel:
return hook.Writer.Debug(line)
default:
return nil
}
}
func (hook *SyslogHook) Levels() []logrus.Level {
return logrus.AllLevels
}
总结,个人对logrus的功能不是太满意,压根用不到的功能贼多,真正会用到的功能不够简单干练。 针对logrus日志切割的问题,我给作者提过两次了,作者忽忽悠悠把issue给关了。
END.
转载自:https://juejin.cn/post/6844903549881909256