Go 语言标准库中的 log 包设计简洁明了,易于上手,可以轻松记录程序运行时的信息、调试错误以及跟踪代码执行过程中的问题等。使用 log 包无需繁琐的配置即可直接使用。本文旨在深入探究 log 包的使用和原理,帮助读者更好地了解和掌握它。
使用
先来看一个 log 包的使用示例:
main.gogo run main.go
$ go run main.go
2023/03/08 22:33:22 Print
2023/03/08 22:33:22 Printf: print
2023/03/08 22:33:22 Println
2023/03/08 22:33:22 Fatal
exit status 1
以上示例代码中使用 log 包提供的 9 个函数分别对日志进行输出,最终得到 4 条打印日志。我们来分析下每个日志函数的作用,来看看为什么出现这样的结果。
log 包提供了 3 类共计 9 种方法来输出日志内容。
函数名 | 作用 | 使用示例 |
---|---|---|
打印日志 | log.Print("Print") | |
Printf | 打印格式化日志 | log.Printf("Printf: %s", "print") |
Println | 打印日志并换行 | log.Println("Println") |
Panic | 打印日志后执行 panic(s)(s 为日志内容) | log.Panic("Panic") |
Panicf | 打印格式化日志后执行 panic(s) | log.Panicf("Panicf: %s", "panic") |
Panicln | 打印日志并换行后执行 panic(s) | log.Panicln("Panicln") |
Fatal | 打印日志后执行 os.Exit(1) | log.Fatal("Fatal") |
Fatalf | 打印格式化日志后执行 os.Exit(1) | log.Fatalf("Fatalf: %s", "fatal") |
Fatalln | 打印日志并换行后执行 os.Exit(1) | log.Panicln("Panicln") |
log.Fatal("Fatal")os.Exit(1)
log.Newlogger
log.Newio.Writer
使用示例:
示例输出:
[Debug] - main.go:10: custom logger
os.Stdout[Debug] - log.Lshortfile
日志属性可选项如下:
prefixLdate|Ltime
|log.Ldate|log.Ltime|log.Lshortfile
log.Newloggerlogger
方法 | 作用 |
---|---|
SetOutput | 设置日志输出位置 |
SetPrefix | 设置日志输出前缀 |
SetFlags | 设置日志属性 |
现在我们来看一个更加完整的使用示例:
demo.log
main.go:15: [Debug] - Print
main.go:16: [Debug] - Println
控制台输出内容如下:
[Info] - 2023/03/11 01:24:56 Print
[Info] - 2023/03/11 01:24:56 Println
demo.loglog.Lmsgprefix[Debug] -
logger.SetXXXlogger
以上,基本涵盖了 log 包的所有常用功能。接下来我们就通过走读源码的方式来更深入的了解 log 包了。
源码
注意:本文以 Go 1.19.4 源码为例,其他版本可能存在差异。
Go 标准库的 log 包代码量非常少,算上注释也才 400+ 行,非常适合初学者阅读学习。
在上面介绍的第一个示例中,我们使用 log 包提供的 9 个公开函数对日志进行输出,并通过表格的形式分别介绍了函数的作用和使用示例,那么现在我们就来看看这几个函数是如何定义的:
std.OutputPrintXFatalos.Exit(1)Panicpanic(s)
stdOutput
std
stdNewLoggerLstdFlags
logger := log.New(os.Stdout, "[Debug] - ", log.Lshortfile)log.Print("Print")Logger
Logger
flagisDiscard
flag
具体含义我就不再一一解释了,前文的表格已经写的很详细了。
1 << iotalog.NewLoggerlog.Ldate|log.Ltime|log.Lshortfile
stdLstdFlags
isDiscardPrintXif atomic.LoadInt32(&std.isDiscard) != 0return
ioio.Discardio.Discardio.Writerio.Copy(io.Discard, io.Reader)io.Discard
NewLoggerout == io.Discardl.isDiscard1PrintXisDiscardint32bool
std.Output
Outputruntime.Caller
bufformatHeaderbuf\nPrintPrintlnPrint
l.out.Writebuf
formatHeader
formatHeader
itoaitoaintASCIIitoa
bufiyearmonthwidASCIIiitoa(&b, 12, 3)012
log.Print("Print")
stdLoggerLoggerlogger := log.New(os.Stdout, "[Debug] - ", log.Lshortfile)loggger.Printlog.PrintLogger
Logger
l.Output(2, s)calldepthruntime.Caller(calldepth)runtime.Caller
runtime.Callerskipruntime.Caller
main.go -> log.Print -> std.Output -> runtime.Callerskip
std.Outputruntime.Callerlog.Printstd.Outputmain.golog.Print
这样当代码出现问题时,就能根据日志中记录的函数调用栈来找到报错的源码位置了。
LoggerSetOutputSetPrefixSetFlags
gettersetter
当然,log 包级别的函数,也少不了这几个功能:
至此,log 包的全部代码我们就一起走读完成了。
LoggerLoggerstdLogger
使用建议
关于 log 包的使用,我还有几条建议分享给你:
DebugInfoWarnLoggerLogger
原文链接:https://jianghushinian.cn/2023/03/11/dive-into-the-go-log-standard-library/