前言
为什么需要日志
- 调试开发
- 程序运行日志
- 用户行为日志
不同的目的决定了日志输出的格式、频率。作为开发人员,调试开发阶段打印日志目的是输出尽可能全的信息(如上下文,变量值…),辅助开发测试,因此日志格式要易读,打印频率要高。而在程序运行时,日志格式倾向于结构化(便于分析与搜索),而且为了性能和聚焦于关键信息(如error ),打印频率更偏低。
Go 标准库 Log
使用
我们常使用 Go log 以下三组函数:
Print/Printf/Println
: 打印日志信息Panic/Panicf/Panicln
: 打印日志信息后,以拼装好的字符串为参数调用 PanicFatal/Fatalf/Fatalln
: 打印日志信息后,os.Exit(1)
退出程序
带 f 后缀的是格式化输出,ln 后缀增加换行符,不过在打印日志场景中会自动增加一个换行符,这里 ln 后缀差别不大。Panic 与 Fatal 的区别在于 Panic 可以被捕获。
示例如下:
package main
import "log"
func main() {
log.Println("日志信息1")
log.Print("日志信息2")
log.Panicln("日志信息3")
log.Fatalln("日志信息4") // 运行不到
}
2021/11/09 15:41:34 日志信息1
2021/11/09 15:41:34 日志信息2
2021/11/09 15:41:34 日志信息3
panic: 日志信息3
goroutine 1 [running]:
log.Panicln({
0xc0000bdf60, 0x0, 0x1015ba5})
/usr/local/opt/go/libexec/src/log/log.go:368 +0x65
main.main()
/Users/bytedance/go/src/github.com/he2121/log_test/main.go:8 +0xa8
可以看到日志格式默认是:时间 + msg。为什么是这样,可以改吗?
原理与自定义
func Println(v ...interface{
}) {
std.Output(2, fmt.Sprintln(v...))
}
可以看到前面的方法都是调用 std
对象中 Output
方法。
var std = New(os.Stderr, "", LstdFlags)
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{
out: out, prefix: prefix, flag: flag}
}
type Logger struct {
mu sync.Mutex // ensures atomic writes; protects the following fields
prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
flag int // properties
out io.Writer // destination for output
buf []byte // for accumulating text to write
}
std
使用 log.New
构造出来,三个参数
io.Writer
: 任何实现了Writer
接口的结构,日志会写到这个里面