log包是go语言提供的一个简单的日志记录功能,其中定义了一个结构体类型 Logger
,是整个包的基础部分,包中的其余方法都是围绕这整个结构体建立的.缓存
Logger结构的定义以下:app
type Logger struct { mu sync.Mutex prefix string flag int out io.Writer buf []byte }
与此同时还提供了一个构造方法用于建立 Logger:函数
func New(out io.Writer, prefix string, flag int) *Logger { return &Logger{out: out, prefix: prefix, flag: flag} }
还有围绕Logger结构的几个参数定义的方法:指针
func (l *Logger) SetOutput(w io.Writer) // 用于设置日志输出目标 func (l *Logger) SetPrefix(prefix string) // 用于设置每一行日志的前缀 func (l *Logger) Prefix() string // 获取当前使用的前缀 func (l *Logger) SetFlags(flag int) // 用于设置使用的输出标志 func (l *Logger) Flags() int // 获取当前使用的标志
这些方法都很简单,只是给咱们提供了一个能够修改和获取当前日志器的设置的方式.日志
在 log 包中,定义了一系列的常亮用于表示 flag,以下:code
const ( Ldate = 1 << iota // 1 << 0 当地时区的日期: 2009/01/23 Ltime // 1 << 1 当地时区的时间: 01:23:23 Lmicroseconds // 1 << 2 显示精度到微秒: 01:23:23.123123 (应该和Ltime一块儿使用) Llongfile // 1 << 3 显示完整文件路径和行号: /a/b/c/d.go:23 Lshortfile // 1 << 4 显示当前文件名和行号: d.go:23 (若是与Llongfile一块儿出现,此项优先) LUTC // 1 << 5若是设置了Ldata或者Ltime, 最好使用 UTC 时间而不是当地时区 LstdFlags = Ldate | Ltime // 标准日志器的初始值 )
使用方法:orm
|
链接便可例如:对象
Ldate | Ltime // 2017/07/31 08:01:20 Ldate | Ltime | Lmicroseconds | Llongfile // 2017/07/31 08:01:20.123123 /a/b/c/d.go:23
在 log 包中,定义了下面几组方法:接口
func (l *Logger) Printf(format string, v ...interface{}) func (l *Logger) Print(v ...interface{}) func (l *Logger) Println(v ...interface{}) func (l *Logger) Fatal(v ...interface{}) func (l *Logger) Fatalf(format string, v ...interface{}) func (l *Logger) Fatalln(v ...interface{}) func (l *Logger) Panic(v ...interface{}) func (l *Logger) Panicf(format string, v ...interface{}) func (l *Logger) Panicln(v ...interface{})
即 Print*, Fatal*, Painc*, 这里方法结尾的 f 或者 ln 就跟 fmt.Print 的含义是相同的,所以上面这九个方法的使用方式其实与 fmt.Print/f/ln
是同样的.咱们直接以没有 f 或 ln 的方法为例来看看三组方法的代码:事件
func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) } func (l *Logger) Fatal(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) os.Exit(1) } func (l *Logger) Panic(v ...interface{}) { s := fmt.Sprint(v...) l.Output(2, s) panic(s) }
能够看到其实三个方法 都调用了接收者(也就是Logger类型的实例或指针)的 Output 方法,这个方法后面在说,其实就是字面的意思,即用来输出咱们传入进去的字符串(fmt.Sprint方法将咱们传入的参数转换为字符串后返回)
不一样的地方在于:
因此这三个方法的用处就显而易见了.
前面介绍了三组方法的内部都是调用了 Output 方法来实现的,也就是说实际的工做实在 Output 方法中执行的.
func (l *Logger) Output(calldepth int, s string) error { now := time.Now() var file string var line int l.mu.Lock() defer l.mu.Unlock() if l.flag&(Lshortfile|Llongfile) != 0 { 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.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) return err }
这里须要提早说一下 runtime.Caller 函数,这个函数用于获取调用Go程的栈上的函数调用所在的文件和行号信息。参数为 skip 表示咱们须要获取信息的调用层级,返回值为 程序计数器(pc), 文件名,行号以及获取成功与否的标志。
在 Output 方法中,咱们作了下面这些事情:
这里咱们注意到有一个 callpath 参数,这个参数是用于获取某个指定层级的信息,前面3组方法中,这里使用的都是2, 这是由于,咱们真正须要的文件名和行号是 调用 Print, Fatal, Panic 这些方法的地方,所以在调用 runtime.Caller
方法时,须要获取栈中当前位置的前两个位置处的信息.
log 包除了提供了上述一些须要先建立 Logger 实例才能使用的方法以外,还给咱们定义了一些快捷的方法,它的实现方式也很简单,其实就是在 log包内预先定义了一个 Logger 实例叫 std:
var std = New(os.Stderr, "", LstdFlags)
而后定义了一些能够直接使用包来调用的方法:
func Output(calldepth int, s string) error func Fatal(v ...interface{}) func Fatalf(format string, v ...interface{}) func Fatalln(v ...interface{}) func Panic(v ...interface{}) func Panicf(format string, v ...interface{}) func Panicln(v ...interface{}) func Print(v ...interface{}) func Printf(format string, v ...interface{}) func Println(v ...interface{}) func SetFlags(flag int) func Flags() int func SetOutput(w io.Writer) func SetPrefix(prefix string) func Prefix() string
这些方法的内部实际上大部分都是直接调用了 std 的对应的方法来实现的,不过 Print*, Panic*, Fatal* 这些方法的内部仍是调用了 std.Output 方法来实现的.
前面已经涵盖了 log 包中的全部方法,除了下面两个:
func itoa(buf *[]byte, i int, wid int)
func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int)
这里就不细说了,主要就是用来完成数据的格式化操做的.