Dawn's Blogs

分享技术 记录成长

0%

Go Web编程学习笔记 (8) 日志

Go 语言中可以使用第三方日志系统:logrus 和 seelog,它们实现了很强大的日志功能。

logrus

介绍

logrus 是用 Go 语言实现的一个日志系统,安装 logrus:

1
go get -u github.com/sirupsen/logrus

六个日志等级(从低到高):

  • Debug
  • Info
  • Warn
  • Error
  • Fatal,随后触发 os.Exit(1)
  • Panic,随后触发 panic()

使用

设置 logrus 参数

可以设置 logrus 的参数:

1
2
3
4
5
6
7
8
9
10
func init() {
// 日志格式化为 JSON,而不是默认的 ASCII
logrus.SetFormatter(&logrus.JSONFormatter{})

// 输出 stdout 而不是默认的 stderr,也可以是一个文件
logrus.SetOutput(os.Stdout)

// 只记录 Warn 以及以上的错误等级
logrus.SetLevel(logrus.WarnLevel)
}

Feilds

可以添加 Fields 来自定义输出:

1
2
3
4
5
6
7
logrus.WithFields(logrus.Fields{
"name": "zh",
"age": 23,
}).Warn("This is a warn level log.")

// output:
// {"age":23,"level":"warning","msg":"This is a warn level log.","name":"zh","time":"2022-05-20T13:11:48+08:00"}

有时候我们需要固定的 Fields,只需要生成一个 log.Entry 就可以复用 Fields

1
2
3
4
5
6
7
// 生成 log.Entry 复用 Feilds
contextLogger := logrus.WithFields(logrus.Fields{
"common": "this is a common field",
})

contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")

Logger

如果想在一个应用里面向多个地方记录日志,可以创建 Logger实例,Logger 结构如下:

1
2
3
4
5
6
7
8
9
10
11
type Logger struct {
Out io.Writer
Hooks LevelHooks
Formatter Formatter
//最小级别
Level Level
//被用来同步写入,比如两个地方同时log.默认是被锁住的
mu MutexWrap
// Reusable empty entry
entryPool sync.Pool
}

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"os"
"github.com/sirupsen/logrus"
)

// 你可以创建很多instance
var log = logrus.New()

func main() {
file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
if err == nil {
log.Out = file
} else {
log.Info("Failed to log to file, using default stderr")
}
log.WithFields(logrus.Fields{
"filename": "123.txt",
}).Info("打开文件失败")
}

seelog

介绍

seelog 主要有以下特性:

  • XML 的动态配置,支持动态改变配置而不需要重新启动应用。
  • 支持多输出流,能够同时把日志输出到多种流中、例如文件流、网络流等。
  • 日志级别:trace、debug、info、warn、error、critical、off

安装 seelog:

1
go get -u github.com/cihub/seelog

使用

基本使用

1
2
3
4
5
6
7
8
9
package main
import (
log "github.com/cihub/seelog"
)

func main() {
defer log.Flush()
log.Info("hello world")
}

配置文件说明

seelog 的配置文件通过 XML 进行配置,其说明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 
1. type: 日志输出类型,有4中类型,分别是:sync,asyncloop(default),asynctimer,adaptive
type=“sync”:在同一个go中处理日志消息,仅当调用日志函数时才被执行。
type=“asyncloop”:在单独的go中独自处理日志消息,循环从日志队列中读取日志并消费(输出到控制台或者文件)。
type="asynctimer":在单独的go中独自处理日志消息,在指定时间间隔去读取日志队列消息,所以该类型还需要配置一个间隔时间(纳秒)。
type="adaptive":在单独的go中独自处理日志消息,但是不是固定的每隔指定时间去读取日志消息,间隔时间与队列剩余的日志量有关,如果剩余日志量多,则间隔时间短,反之亦然
2. minlevel: 全局最低输出日志级别
3. maxlevel: 全局最高输出日志级别
4. exceptions: 日志的特殊处理情况,可根据指定文件或者函数进行日志输出
5. formatid: 输出格式标签,可以在formats中找到对应的标签
6. console: 将日志输出到控制台
7. splitter: 用于细分outputs日志格式,支持: file(文件), rollingfile(滚动文件), buffered(缓存到内存再输出到文件), smtp(发送日志邮件), con(网络转发)
8. rollingfile: 滚动文件,可基于日期(type="date")或者文件大小(type="size")进行日志切割,maxsize: 单个日志文件最大size,如果设置为100M,则maxsize=100*1024*1024,maxrolls: 最大文件数量,超出的日志文件数量会被滚动删除
9. buffered: 将日志先存在内存中,定期写入文件,适合日志并发量较大或 IO 比较紧张的场合,size:缓存大小, flushperiod:缓存时间
10. filter: 单独处理某级别的日志
11. formats: 日志输出格式
-->

自定义配置文件

seelog 支持自定义日志处理,可以通过配置文件进行配置:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package logs

import (
"fmt"

seelog "github.com/cihub/seelog"
)

var Logger seelog.LoggerInterface

func loadAppConfig() {
appConfig := `
<seelog minlevel="warn">
<outputs formatid="common">
<rollingfile type="size" filename="/data/logs/roll.log" maxsize="100000" maxrolls="5"/>
<filter levels="critical">
<file path="/data/logs/critical.log" formatid="critical"/>
<smtp formatid="criticalemail" senderaddress="astaxie@gmail.com" sendername="ShortUrl API" hostname="smtp.gmail.com" hostport="587" username="mailusername" password="mailpassword">
<recipient address="xiemengjun@gmail.com"/>
</smtp>
</filter>
</outputs>
<formats>
<format id="common" format="%Date/%Time [%LEV] %Msg%n" />
<format id="critical" format="%File %FullPath %Func %Msg%n" />
<format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/>
</formats>
</seelog>
`
logger, err := seelog.LoggerFromConfigAsBytes([]byte(appConfig))
if err != nil {
fmt.Println(err)
return
}
UseLogger(logger)
}

func init() {
DisableLog()
loadAppConfig()
}

// DisableLog disables all library log output
func DisableLog() {
Logger = seelog.Disabled
}

// UseLogger uses a specified seelog.LoggerInterface to output library log.
// Use this func if you are using Seelog logging system in your app.
func UseLogger(newLogger seelog.LoggerInterface) {
Logger = newLogger
}

上面实现了三个函数:

  • DisableLog 函数:初始化全局变量 Logger 为 seelog 的禁用状态,主要为了防止 Logger 被多次初始化
  • loadAppConfig 函数:根据配置文件初始化 seelog 的配置信息,配置文件说明如下:
    • seelog:minlevel 参数可选,如果被配置,高于或等于此级别的日志会被记录,同理 maxlevel。
    • outputs:输出信息的目的地,这里分成了两份数据,一份记录到 log rotate 文件里面。另一份设置了 filter,如果这个错误级别是 critical,那么将发送报警邮件。
    • formats:定义了日志的格式。
  • UseLogger 函数:设置当前的日志器为相应的日志处理。