开源日志框架-logrus详解

miloyang
0 评论
/ /
721 阅读
/
5551 字
22 2023-07

日志的重要性,自不必多说。系统log库,仅仅提供三组接口(print/fatal/panic),功能过于简单,今天来学习目前Github上star数量最多的日志包。很多优秀的开源项目,例如:docker、prometheus等都使用了logrus。

logrus简介

源码地址

logrus是一个流行的Go语言日志库,用于在Go应用程序中记录日志信息,提供了丰富的特征和灵活的配置选项,使开发人员能够轻松的生成格式化的日志,并将其输出到不同的目标,如标准输出、文件、远程服务器等以下为关键特征:

  • 结构化日志记录:意味着可以使用字段来描述日志事件的各个方面,每条日志都可以包含一系列字段,提供更多的上下文信息。
  • 多种日志级别:最基本的框架功能,级别包括:Debug、Info、Warning、Error、Fatal,选择适当的级别记录不同程度的日志。
  • 定制的输出格式:轻松地自定义日志输出的格式,包括时间戳、级别、消息和其他字段。
  • 多种输出目标:允许将日志输出到多个不同的目标,如标准输出、文件、远程服务等。
  • 钩子(Hooks):允许在记录日志之前或之后执行自定义操作,可以用于发送告警、记录额外信息等。
  • 字段、上下文和附加数据:允许在每条日志中添加附加的字段和上下文信息,以帮助更好地理解日志事件,对于debug非常有用。

话不多说,开干

快速使用

go get -u github.com/sirupsen/logrus


package main

import (
    "os"
    log "github.com/sirupsen/logrus"
)

func init() {
    log.SetFormatter(&log.JSONFormatter{})

    log.SetOutput(os.Stdout)

    log.SetLevel(log.DebugLevel)
}

func main() {
    log.WithFields(log.Fields{
        "module": "login",
    }).Info("User Login")

    log.WithFields(log.Fields{
        "module": "login",
        "fields": "user.name",
    }).Warn("parameter does not match the rules")

    log.WithFields(log.Fields{
        "module": "login",
    }).Fatal("user is nil")

    contextLogger := log.WithFields(log.Fields{
        "common": "this is a common field",
        "other":  "I also should be logged always",
    })
    contextLogger.Info("I'll be logged with common and other field")
}

运行后输出:

{"level":"info","module":"login","msg":"User Login","time":"2023-08-21T15:16:21+08:00"}
{"fields":"user.name","level":"warning","module":"login","msg":"parameter does not match the rules","time":"2023-08-21T15:16:21+08:00"}
{"level":"fatal","module":"login","msg":"user is nil","time":"2023-08-21T15:16:21+08:00"}

源码分析:

  • 输出中有三个关键信息:time、msg和level,也可以定制化字段输出信息。
  • init里面的SetFormatrer,可以根据自身业务,设置为json格式(如上输出),还可以设置为&log.TextFormatter{},则如下:
time="2023-08-21T15:19:39+08:00" level=info msg="User Login" module=login
  • init里面的SetOutput,接收所有的io.writer,如stderr或stdout,根据自身配置输出到哪。
  • init里面的SetLevel,设置日志级别,从0-7分别为:PanicLevel、FatalLevel、ErrorLevel、WarnLevel、InfoLevel、DebugLevel、TraceLevel,值得注意的是,如果设置为InfoLevel级别,则后面的DebugLevel、TraceLevel级别的日志不输出。
  • 由于logrus.Fatal会导致程序退出(查看源码,调用了os.Exit),则下面的所有日志都不会执行到,多说一句,除main函数外,其余任何地方都禁止调用该方法。
  • 由上可以看出,日志只是有msg并无具体行号,可以通过 log.SetReportCaller(true) 来显示出具体位置,如下:
    time="2023-08-21T15:45:12+08:00" level=fatal msg="user is nil" func=main.main file="E:/workspaces/goland/study_demo/go_logrus/main.go:36" module=login
    

    Hook应用

    设置钩子,每条日志输出前都会执行特定方法,比如可以将日志输出到其他云平台等等。
type myHook struct {
}

// Levels 表示该hook,需要挂载在哪些level上面
func (h *myHook) Levels() []log.Level {
    return log.AllLevels
}

// Fire 表示在打印之前先触发当前方法
func (h *myHook) Fire(entry *log.Entry) error {
    fmt.Println()
    fmt.Printf("Fire:%+v \n", entry.Message)
    return nil
}

func init() {
    log.SetFormatter(&log.TextFormatter{})
    log.SetLevel(log.DebugLevel)
    log.AddHook(&myHook{})
}

func main() {
    log.WithFields(log.Fields{
        "module": "login",
    }).Info("User Login")
}

输出为:

Fire:User Login 
time="2023-08-21T16:06:40+08:00" level=info msg="User Login" module=login

当然,应用场景多种多样,如可以将我们的日志发送到redis、MongoDB等等,这样可以借助第三方的Hook,如:
mgorus:将日志发送到mongodb。
logrus-redis:将日志发送到redis。
logrus-amqp:将日志发送到ActiveMQ。

设置输出至文件

这里可以引用另外一个专门做日志切割的SDK go get -u github.com/lestrrat-go/file-rotatelogs

const LogPath = "runtime/logs/cos.log"

func init() {
    log.SetFormatter(&log.TextFormatter{})
    log.SetLevel(log.DebugLevel)
    writer, _ := getWriter()
    log.SetOutput(writer)
}

func getWriter() (io.Writer, error) {
    filePath := LogPath
    return rotatelogs.New(
        filePath+".%Y%m%d%H%M",                    //指定文件名称
        rotatelogs.WithMaxAge(time.Hour*24*30),    //日志保存最长时间,如一个月
        rotatelogs.WithRotationTime(time.Hour*24)) // 日志多久分割一次,如每天
}

输出至云服务器

推荐使用:"tencent cloud cls log sdk" 是专门为cls量身打造日志上传SDK。 一切只为满足您的需求~ 为什么要使用CLS Log SDK

  • 异步发送:发送日志立即返回,无须等待,支持传入callback function。
  • 优雅关闭:通过调用close方法,producer会将所有其缓存的数据进行发送,防止日志丢失。
  • 感知每一条日志的成功状态: 用户可以自定义CallBack方法的实现,监控每一条日志的状态
  • 使用简单: 通过简单配置,就可以实现复杂的日志上传聚合、失败重试等逻辑
  • 失败重试: 429、500 等服务端错误,都会进行重试
  • 高性能: 得益于go语言的高并发能力
    tencent CLS Log 我们可以结合hook,与SDK进行通讯,把日志传送至云。 代码,详参sdk。
人未眠
工作数十年
脚步未曾歇,学习未曾停
乍回首
路程虽丰富,知识未记录
   借此博客,与之共进步