日志包提供了方便的携带多样化信息的输出,比如日期、调用者行号等,为我们找bug或者分析问题提供了强有力的工具
基本使用
日志的输入输出都基于log.Logger
对象,通过New
方法进行创建
func New(out io.Writer, prefix string, flag int) *Logger
// eg.
l := log.New(os.Stderr, "", Ldate | Ltime | Lshortfile)
out
指定输出位置(比如标准流、文件),prefix
指定每行输出日志的前缀字符串,为了区分业务日志和第三方库输出的日志,flag
通过将标志位进行位或操作来指定每行日志携带的信息,其中可选的位有
const (
Ldate = 1 << iota // 日期
Ltime // 时间
Lmicroseconds // 毫秒
Llongfile // 文件绝对路径和行号
Lshortfile // 文件名和行号
LUTC // 如果同时使用了Ldate或者Ltime,使用UTC
Lmsgprefix // prefix将紧跟在用户输出内容的头部,而不是日志的行首
LstdFlags = Ldate | Ltime // Go提供的标准抬头信息
)
以上三个参数也可以分别通过公有方法来单独进行设置
log包内已经定义了一个名为std
的Logger
,通过包名直接调用Logger
的方法时其实就是对std
对象进行操作
func Println(v ...any) {
if atomic.LoadInt32(&std.isDiscard) != 0 {
return
}
std.Output(2, fmt.Sprintln(v...)) // 对std进行调用
}
其中所有的日志输出方法如Println
、Fatalf
都是通过调用Output
方法来输出,因此最重要的是Output
方法
func (l *Logger) Output(calldepth int, s string) error {
now := time.Now() // get this early.
var file string
var line int
l.mu.Lock()
defer l.mu.Unlock()
if l.flag&(Lshortfile|Llongfile) != 0 {
// Release lock while getting caller info - it's expensive.
l.mu.Unlock()
var ok bool
_, file, line, ok = runtime.Caller(calldepth) // 获取Println、Fatalf等调用时的所在文件和行数
if !ok {
file = "???"
line = 0
}
l.mu.Lock()
}
l.buf = l.buf[:0]
l.formatHeader(&l.buf, now, file, line) // 格式化抬头信息
l.buf = append(l.buf, s...) // 将抬头与输出内容拼接
if len(s) == 0 || s[len(s)-1] != '\n' {
l.buf = append(l.buf, '\n')
}
_, err := l.out.Write(l.buf) // 输出到指定的output中
return err
}
其中获取调用者的runtime.Caller
中传入的calldepth
作用如下
0表示*Logger.Output中调用runtime.Caller的源代码文件和行号
1表示log.Println中调用*Logger.Output的源代码文件和行号
2表示main中调用log.Println的源代码文件和行号
因此Println
、Fatalf
等方法调用Output
传入的calldepth
总是2
定制Logger
在实际的业务中通常不会使用默认的std
来输出日志,而是通过New
创建新的Logger
实现自定义格式的日志输出
var (
Info *log.Logger
Warning *log.Logger
Error * log.Logger
)
func init(){
errFile,err:=os.OpenFile("errors.log",os.O_CREATE|os.O_WRONLY|os.O_APPEND,0666)
if err!=nil{
log.Fatalln("打开日志文件失败:",err)
}
Info = log.New(os.Stdout,"Info:",log.LstdFlags | log.Lshortfile)
Warning = log.New(os.Stdout,"Warning:",log.LstdFlags | log.Lshortfile)
Error = log.New(io.MultiWriter(os.Stderr,errFile), "Error:", log.LstdFlags | log.Lshortfile)
}
func main() {
Info.Println("普通信息")
Warning.Println("警告信息")
Error.Println("错误信息")
}
其中io.MultiWriter
封装多个Writer,很简单,进行Write
操作时只是循环遍历传入的Writer
并调用各自的Write
输出同样的数据
参考链接:https://www.flysnow.org/2017/05/06/go-in-action-go-log