Go 语言日志库 logrus 基础用法

1,688 阅读5分钟

日志是现代编程中必不可少的手段,除了处理基本的错误之外,通过记录日志,也可以帮助我们完成一些基本的功能,比如开发及测试期间的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"
	"os"
)

func main() {
	// 创建一个Logrus的实例
	logger := logrus.New()
	logger.SetOutput(os.Stdout)
}

日志打印到文件

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

package main

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

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"
	"io"
	"os"
)

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表示控制台输出的日志带有颜色,其结果如下:

image.png

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

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

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

设置日志输出文件及行号

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

显示效果如下: image.png

field 机制

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

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

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

package main

import (
	"fmt"
	"github.com/sirupsen/logrus"
	"io"
	"os"
)

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 能力来允许我们自定义一些日志处理逻辑。