日志是现代编程中必不可少的手段,除了处理基本的错误之外,通过记录日志,也可以帮助我们完成一些基本的功能,比如开发及测试期间的Debug,记录请求的上下文,排除故障原因,数据统计及分析等等。

Logrus 是一个结构化、可插拔的Go日志库,并且完全兼容官方的log库,具有很强的灵活性,有 TEXT 和 JSON 两种可选的日志输出格式,同时还提供了自定义格式的插件功能,支持 Feild 机制和可扩展的 Hook 机制。

安装Logrus

logrus 并不是Go的标准库,所以我们得通过 go get github.com/sirupsen/logrus 的形式安装到项目中。

$ go get github.com/sirupsen/logrus
go: downloading golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
go: added github.com/sirupsen/logrus v1.9.0
go: added golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8

安装完成后,可以执行下示例:

package main
import (
    "github.com/sirupsen/logrus"
func main() {
    // 创建一个Logrus的实例
    logger := logrus.New()
    // 设置输出格式为JSON
    logger.SetFormatter(&logrus.JSONFormatter{})
    // 设置日志级别为debug
    logger.SetLevel(logrus.DebugLevel)
    // 记录日志
    logger.WithFields(logrus.Fields{
        "animal": "walrus",
        "number": 1,
    }).Info("A walrus appears")
// Output:
// {"animal":"walrus","level":"info","msg":"A walrus appears","number":1,"time":"2023-03-15T15:02:30+08:00"}

示例中,我们首先创建了一个 logrus 实例,输出格式设置为 JSON,通过 SetLevel 设置日志级别,再设置 Fields。

Logrus 支持的日志级别

相比 Go 语文的标准log库,Logrus支持多达七种的日志级别:

  • Panic:恐慌级别,也是最高级别的日志,会打印出错误堆栈。
  • Fatal:致命错误,输出日志后,执行 exit(1) 退出
  • Error:错误日志,必须记录与跟踪的日志
  • Warn:警告日志,主要记录需要提醒开发者的日志
  • Info:主要是提供一些必要的日志信息,在业务出现问题时,可以结合error日志快速定位问题,一般会默认使用该级别的日志。
  • Debug:调试信息,方便开发测试阶段的问题定位
  • Trace:比 debug 级别还低,一般很少用。
  • 日志级别由 Panic 级别最高, Trace 级别最低。Logrus 默认级别为 Info,低于该级别的日志不会输出,所以默认情况下,使用Trace和Debug是不会打印出来日志的。

    package main
    import (
        "github.com/sirupsen/logrus"
    func main() {
        logrus.SetLevel(logrus.TraceLevel)
        logrus.Trace("Trace Level")   // TRAC[0000] Trace Level  
        logrus.Debug("Debug Level")   // DEBU[0000] Debug Level  
        logrus.Info("Info Level")     // INFO[0000] Info Level
        logrus.Warn("Warn Level")     // WARN[0000] Warn Level 
        logrus.Error("Error Level")   // ERRO[0000] Error Level
        logrus.Fatal("Fatal Level")   // FATA[0000] Fatal Level      // 退出
        logrus.Panic("Panic Level")
    

    其结果不言而喻,唯一需要注意的是,Fatal级别后的日志不会再打印出来了。

    设置日志级别

    如上所述,Logrus 默认的日志级别是 InfoLevel,但是我们可以通过 logrus.SetLevel(Level)来设置我们需要的级别。

    logrus.SetLevel(logrus.DebugLevel)
    

    设置日志打印和记录的介质

    Logrus 支持设置将日志输出到不同的介质:

  • 日志打印到控制台
  • 日志打印到文件
  • 日志同时打印到控制台和文件中。
  • 日志打印到控制台

    通过 SetOutput设置为 os.Stdout或者 os.Stderr

    package main
    import (
    	"github.com/sirupsen/logrus"
    func main() {
    	// 创建一个Logrus的实例
    	logger := logrus.New()
    	logger.SetOutput(os.Stdout)
    

    日志打印到文件

    使用 logrus.SetOutput(file)来实现将日志打印到文件中。

    package main
    import (
    	"fmt"
    	log "github.com/sirupsen/logrus"
    func main() {
    	logFile := "log.txt"
    	f, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
    	if err != nil {
    		fmt.Println("Failed to create logfile" + logFile)
    		panic(err)
    	defer f.Close()
    	log.SetOutput(f)
    	log.SetLevel(log.DebugLevel)
    	log.Info("Info Level")
    	log.Warn("Warn Level")
    	log.Error("Error Level")
    	log.Fatal("Fatal Level")
    

    打印到控制台及日志

    通过 io.MultiWriter来实现同时打印到控制台及日志里。

    package main
    import (
    	"fmt"
    	"github.com/sirupsen/logrus"
    func main() {
    	logFile := "log.txt"
    	f, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
    	if err != nil {
    		fmt.Println("Failed to create logfile" + logFile)
    		panic(err)
    	defer f.Close()
    	log := &logrus.Logger{
    		Out:       io.MultiWriter(f, os.Stdout),
    		Level:     logrus.DebugLevel,
    		Formatter: &logrus.TextFormatter{},
    	log.Info("Info Level")
    	log.Warn("Warn Level")
    	log.Error("Error Level")
    	log.Fatal("Fatal Level")
    

    设定输出格式

    logrus 支持两种日志格式: 文本类型(TextFormatter)与 json 类型(JSONFormatter),默认是文本类型。

    我们也可以通过 SetFormatter函数来设置日志格式。

    设置 TextFormatter格式:

    logrus.SetFormatter(&logrus.TextFormatter{
        FullTimestamp:   true,
        TimestampFormat: "2006-01-02 15:04:05",
        ForceColors:     true,
    

    FullTimestamp表示展示日期,TimestampFormat表示展示日期的格式,ForceColors表示控制台输出的日志带有颜色,其结果如下:

    TextFormatter还支持其他很多属性,比如ForceQuote表示强制给使用WithFields 的值加上引号,默认logrus会输出日志等级前四个字母,使用 DisableLevelTruncation可以设置全部展示等等。

    更多的你可以查看下源码:pkg.go.dev/github.com/…

    JSONFormatterTextFormatter类似,但是不支持 ForceColors颜色。

    设置日志输出文件及行号

    默认 logrus 输出的日志是不带有文件及行号的,如果我们需要可以通过 logrus.SetReportCaller(true)来设置显示,但是这个会打印出文件的绝对路径。

    显示效果如下:

    field 机制

    logrus不推荐使用冗长的消息来记录运行过程中产生的信息,它推荐使用 Fields来进行精细化、结构化的日志。

    有时候,我们需要在特定场景下记录一些特定的参数,而 logrus 则可以通过使用WithField或者WithFields()实现。

    WithFields接收logrus.Fields类型的参数,而 logrus.Fields的底层为map[string]interface{}

    package main
    import (
    	"fmt"
    	"github.com/sirupsen/logrus"
    func main() {
        logger := logrus.New()
    	logger.SetOutput(os.Stdout)
    	logger.WithFields(logrus.Fields{
    		"request_id": "887B4F43-D330-5213-94DF-1B14FA3388C",
    		"ip":         "101.202.112.12",
    		"user_id":    10022,
    	}).Info("Info Level")
        // 或者
        logger.WithField("request_id", "887B4F43-D330-5213-94DF-1B14FA3388C").WithField("ip", "101.202.112.12").WithField("user_id", 10022).Info("Info Level");
    

    我们还可以创建一个logrus.Entry实例,为这个实例设置默认Fields,把logrus.Entry实例设置到记录器Logger,再记录日志时每次都会附带上这些默认的字段。

    logger := log.WithFields(log.Fields{"request_id": "887B4F43-D330-5213-94DF-1B14FA3388C"})
    logger.Info("Info Level") // 也会记录request_id
    logger.Warn("Warn Levle")
    

    上述的内容主要介绍了 logrus 的基础用法,而 logrus 还支持日志自定义输出格式以及 Hook 能力来允许我们自定义一些日志处理逻辑。

  • 为什么很多公司都开始使用Go语言了?
  •