最近看golang1.21的发布时,看到已经有结构化日志库提供了,不过在项目中我经常用的到日志库是logrus。本文总结logrus的使用,并用笔记记录。logrus除了是结构化日志以外,我更看重的是有很多外部的hook支持,最初选择是因为可以向logstash发送日志。
1. 使用方法
日志最基础的使用就是文件日志,这里使用了另外两个库,以协助logrus进行日志的分割。一个是按照每小时进行滚动切割,这样在找问题时,可以按照一小时内去查找,另外如果一个小时内所产生的日志条数过多,也可以限制日志条数,超过条数要求日志文件切割。
1 | func newLfsHook(logName string, logLevel log.Level, |
初始化logger,设置日志的等级,一般情况是错误日志单独一个文件处理。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import (
log "github.com/sirupsen/logrus"
)
func initLogger(logFilename string, level log.Level) error {
log.SetLevel(level)
fileHook, err := newLfsHook(logFilename, level, 1024)
if err != nil {
fmt.Printf("log init fatal!")
return err
}
log.AddHook(fileHook)
// 一般情况可以给错误日志专门开一个hook
errHook, err := newLfsHook("logs/error.log",
log.ErrorLevel, 1024)
if err != nil {
fmt.Printf("errlog init fatal!")
return err
}
log.AddHook(errHook)
return nil
}
2. 原理简介
2.1 最重要的三个结构。以下结构中列出重要的字段,部分省略
- 第一个就是logger结构
1
2
3
4
5
6
7type Logger struct {
// 写日志的处理器
Out io.Writer
// Reusable empty entry
entryPool sync.Pool
// ...
} - 第二个类就是entry结构,表示日志的一个条目
1
2
3
4
5
6
7
8type Entry struct {
// ...
Logger *Logger
//...
// When formatter is called in entry.log(), a Buffer may be set to entry
Buffer *bytes.Buffer
// ...
} - 第三个类是hook,就是外部可以添加的各种支持的日志记录处理器.
1
2
3
4
5
6
7type Hook interface {
Levels() []Level
Fire(*Entry) error
}
// Internal type for storing the hooks on a logger instance.
type LevelHooks map[Level][]Hook
2.2 日志处理逻辑
主要的逻辑是先复制entry条目,如果要报告调用的函数,则要加锁获取Buffer,这个buffer主要用于结构化。
在这里优先钩子上的记录处理器先处理,在进行默认处理。
1 | func (entry *Entry) log(level Level, msg string) { |
newEntry.write函数最后调用logger结构体的Write来处理日志。这里加锁的原因是,写日志有可能是不同的线程里,为了保持其输出的一致性,在这里加锁。这里要指出来的是,hook的线程安全由hook组件自己承担。1
2
3
4
5
6
7
8
9
10
11
12func (entry *Entry) write() {
entry.Logger.mu.Lock()
defer entry.Logger.mu.Unlock()
serialized, err := entry.Logger.Formatter.Format(entry)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
return
}
if _, err := entry.Logger.Out.Write(serialized); err != nil {
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
}
}
3. 结语
最简单的组件,使用最常见,对性能的要求也比较苛刻,通常在前期的使用日志文件,到后期使用日志中心,日志组件要能够灵活支持,平滑过度,项目之初就要制定好日志结构化的一些基本字段,规则也相当重要,以便于日志采集,日志分析,日志提取相关工作可以顺利开展,这也是日志系统之外的事情了。