log.Logger 结构如下:
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
}
包内对外接口如下, 实际上也是log.Logger的实例,只是这个实例是在log包内置的私有变量std
var std = New(os.Stderr, "", LstdFlags) // 包内私有变量
func Default() *Logger { return std }
func SetOutput(w io.Writer) // 设置log.out
func SetPrefix(prefix string) // 设置log.prefix
func SetFlags(flag int) // 设置log.flag
// 以上三种调用了 l.Output(2, fmt.Sprintln(v...)
func Print(v ...interface{})
func Printf(format string, v ...interface{})
func Println(v ...interface{})
// 区别是调用了l.Output()后,会调用 os.Exit(1)
func Fatal(v ...interface{})
func Fatalf(format string, v ...interface{})
func Fatalln(v ...interface{})
// 区别是调用了l.Output()后,会调用 panic()
func Panic(v ...interface
func Panicf(format string, v ...interface{})
func Panicln(v ...interface{})
log实例的接口
// 以下三种调用了 l.Output(2, fmt.Sprintln(v...)
func (l *Logger) Printf(format string, v ...interface{})
func (l *Logger) Print(v ...interface{})
func (l *Logger) Println(v ...interface{})
// 区别是调用了l.Output()后,会调用 os.Exit(1)
func (l *Logger) Fatal(v ...interface{})
func (l *Logger) Fatalf(format string, v ...interface{})
func (l *Logger) Fatalln(v ...interface{})
// 区别是调用了l.Output()后,会调用 panic()
func (l *Logger) Panic(v ...interface{})
func (l *Logger) Panicf(format string, v ...interface{}
func (l *Logger) Panicln(v ...interface{})
func SetOutput(w io.Writer) // 设置log.out
func SetPrefix(prefix string) // 设置log.prefix
func SetFlags(flag int) // 设置log.flag
重点是l.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()
// 判断l.flag是否符合输出runtime.Caller()
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)
if !ok {
file = "???"
line = 0
}
l.mu.Lock()
}
l.buf = l.buf[:0] // 清空对应的l.buf
l.formatHeader(&l.buf, now, file, line) // 主要是为了添加日志对应的perfix以及时间
// 将message信息append到l.buf
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) // 将l.buf内容写到l.out
return err
}
为了添加日志对应的perfix以及时间
func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
if l.flag&Lmsgprefix == 0 {
*buf = append(*buf, l.prefix...)
}
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
if l.flag&LUTC != 0 {
t = t.UTC()
}
if l.flag&Ldate != 0 {
year, month, day := t.Date()
itoa(buf, year, 4)
*buf = append(*buf, '/')
itoa(buf, int(month), 2)
*buf = append(*buf, '/')
itoa(buf, day, 2)
*buf = append(*buf, ' ')
}
if l.flag&(Ltime|Lmicroseconds) != 0 {
hour, min, sec := t.Clock()
itoa(buf, hour, 2)
*buf = append(*buf, ':')
itoa(buf, min, 2)
*buf = append(*buf, ':')
itoa(buf, sec, 2)
if l.flag&Lmicroseconds != 0 {
*buf = append(*buf, '.')
itoa(buf, t.Nanosecond()/1e3, 6)
}
*buf = append(*buf, ' ')
}
}
// 对buf中写入文件、函数、行数调用信息
if l.flag&(Lshortfile|Llongfile) != 0 {
if l.flag&Lshortfile != 0 {
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
}
*buf = append(*buf, file...)
*buf = append(*buf, ':')
itoa(buf, line, -1)
*buf = append(*buf, ": "...)
}
if l.flag&Lmsgprefix != 0 {
*buf = append(*buf, l.prefix...)
}
}
测试代码
package main
import (
"log"
"os"
)
func main() {
logtest("logtest")
logtestWithStack("logtestWithStack")
}
func logtest(message string) {
logger := log.New(os.Stdout, "test:", 1) // 最后面这个参数保证不开启runtime.Call()
logger.Println(message)
}
func logtestWithStack(message string) {
logger := log.New(os.Stdout, "test:", 10) // 最后面这个参数保证开启runtime.Call()
logger.Println(message)
}
主要的区别就是打印函数的调用信息
好了,这就是官方log包的阅读笔记了