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)
這裏就不細說了,主要就是用來完成數據的格式化操做的.