Logrus日志库的使用

警告
本文最后更新于 2023-03-03 22:33,文中内容可能已过时。

设置日志级别

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

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

func main() {
  logrus.SetLevel(logrus.TraceLevel)

  logrus.Trace("trace msg") // Trace级别显示所有的日志
  logrus.Debug("debug msg") // Debug显示debug、info、warn、error、fatal、panic
  logrus.Info("info msg")
  logrus.Warn("warn msg")
  logrus.Error("error msg")
  logrus.Fatal("fatal msg")
  logrus.Panic("panic msg")

  // xx、xxf、xxln的区别
  logrus.Info("hello", "world")
  logrus.Infof("hello %s", "world") // 支持格式化输出
  logrus.Infoln("hello", "world")   // 每个参数用空格分割
}

设置显示代码行数和文件名

方便定位代码位置

1
logrus.SetReportCaller(true)

设置日志格式(json & text)

1
2
3
4
5
6
7
	// 设置为json格式
	logrus.SetFormatter(&logrus.JSONFormatter{})
	logrus.Info("hello", "world")

	// 设置为text格式
	logrus.SetFormatter(&logrus.TextFormatter{})
	logrus.Info("hello", "world")

第三方日志格式

添加自定义字段

注意使用withField或者withFields后,之前设置的就失效了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
	// 在日志中添加指定的字段
	logrus.WithField("service", "myproj").Info("指定自定义字段的日志") // 临时使用
	log := logrus.WithField("service", "myproj")             // 方便后续使用
	log.Info("指定自定义字段的日志")

	// 在日志中添加指定的多个字段
	log = logrus.WithFields(logrus.Fields{
		"key1": "v1",
		"key2": "v2",
	}) // 直接.Info("xxx")也可以直接调用
	log.Info("指定自定义字段的日志")

将日志输出到多个地方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
	// 多个输出目标
	writer1 := &bytes.Buffer{}                                            // 内存
	writer2 := os.Stdout                                                  // 标准输出
	writer3, err := os.OpenFile("log.txt", os.O_WRONLY|os.O_CREATE, 0755) // 文件
	
	if err != nil {
		log.Fatalf("创建日志文件失败: %v", err)
	}

	logrus.SetOutput(io.MultiWriter(writer1, writer2, writer3)) // 设置多个输出目标

	logrus.Info("info msg")     // 写入日志
	io.Copy(os.Stdout, writer1) // 读取buffer中的日志,不过没啥必要

只要实现了Writer接口就可以作为输出目标,Writer接口定义如下 type Writer interface { Write(p []byte) (n int, err error) }

自己创建log实例

默认exported.go中又个std对象,是logrus默认创建好的,上面的代码都是用的std实例,下面我们使用和他一样的方法创建一个log实例

1
2
3
4
	log := logrus.New() // 这里拿到的log和直接使用logrus是相同的方法New出来的
	log.SetFormatter(&myLogFormatter{})
	log.SetLevel(logrus.DebugLevel)
	log.Info("test")

自定义格式

自己实现一个Formatter接口,他的定义如下。

1
2
3
4
5
6
7
8
/*
  Formatter接口需要实现Format方法, 他接收一个Entry类型的参数,这个参数包括日志的所有字段,比如
entry.Message、entry.Time、entry.Level,通过WithField或WithFields添加的字段包含在entry.Data中。
  Format方法的返回值是一个byte类型的切片和一个err.
*/
type Formatter interface {
	Format(*Entry) ([]byte, error)
}

Entry类型包含如下内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type Entry struct {
    Logger *Logger

    // Contains all the fields set by the user.
    Data Fields

    // Time at which the log entry was created
    Time time.Time

    // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
    // This field will be set on entry firing and the value will be equal to the one in Logger struct field.
    Level Level

    // Calling method, with package name
    Caller *runtime.Frame

    // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
    Message string

    // When formatter is called in entry.log(), a Buffer may be set to entry
    Buffer *bytes.Buffer

    // Contains the context set by the user. Useful for hook processing etc.
    Context context.Context

    // err may contain a field formatting error
    err string
}

Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
	"fmt"

	"github.com/sirupsen/logrus"
)

// 声明结构体
type myLogFormatter struct {
}

// 实现Format方法
func (m *myLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	// fmt.Printf("%+v", entry)
	log := fmt.Sprintf("Level %s | Msg %s | Time %s \n", entry.Level, entry.Message, entry.Time)
	return []byte(log), nil
}

func main() {
	log := logrus.New()
	log.SetFormatter(&myLogFormatter{})
	log.Info("test")
}

Hook

添加hook可以再发送日志前执行某些操作,比如遇到Error、panic、fatal时,发送日志到其他地方,添加hook需要实现下面这个接口

1
2
3
4
type Hook interface {
  Levels() []Level  // 哪些级别会触发Hook
  Fire(*Entry) error // 日志输出前会先执行Fire
}

下面是一个例子,将一些错误日志发送到sentry

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// https://github.com/evalphobia/logrus_sentry
import (
  "github.com/sirupsen/logrus"
  "github.com/evalphobia/logrus_sentry"
)

func main() {
  log       := logrus.New()
  hook, err := logrus_sentry.NewSentryHook(YOUR_DSN, []logrus.Level{
    logrus.PanicLevel,
    logrus.FatalLevel,
    logrus.ErrorLevel,
  })  // logrus.AllLevels代表所有级别,具体可看源码

  if err == nil {
    log.Hooks.Add(hook)
  }
}

三方Hook: https://github.com/sirupsen/logrus/wiki/Hooks

集成第三方日志轮转

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
	"fmt"
	"time"

	"github.com/sirupsen/logrus"
	"gopkg.in/natefinch/lumberjack.v2"
)

func main() {

	log := logrus.New()
	// log.SetFormatter(&myLogFormatter{})

	log.SetOutput(&lumberjack.Logger{
		Filename:   "./foo.log",
		MaxSize:    1,     // 单个文件最大大小, 单位M
		MaxBackups: 3,     // 最多保留多少个文件
		MaxAge:     28,    // 每个最多保留多少天
		Compress:   false, // 启用压缩
		LocalTime:  true,  // 默认使用UTC时间, 改为使用本地时间
	})
	for {
		log.Info("testtesttesttesttesttesttesttesttesttesttesttesttesttesttest")
		log.Info("testtesttesttesttesttesttesttesttesttesttesttesttesttesttest")
		time.Sleep(time.Microsecond * 500)
	}

}
请我喝杯水
SoulChild 微信号 微信号
SoulChild 微信打赏 微信打赏
0%