<menuitem id="vnnt5"><em id="vnnt5"><progress id="vnnt5"></progress></em></menuitem><meter id="vnnt5"></meter>

        <sub id="vnnt5"></sub> <progress id="vnnt5"><strike id="vnnt5"><noframes id="vnnt5">

        <p id="vnnt5"></p>

        <dfn id="vnnt5"></dfn>

          <meter id="vnnt5"></meter>

          當前位置:聯升科技 > 技術資訊 > 開發技術 >

          Golang 語言的標準庫 log 包怎么使用?

          2021-01-29    作者:frank    來源:Golang語言開發棧    閱讀:
          01、介紹
          Golang 語言的標準庫中提供了一個簡單的 log 日志包,它不僅提供了很多函數,還定義了一個包含很多方法的類型 Logger。但是它也有缺點,比如不支持區分日志級別,不支持日志文件切割等。

          02、函數
          Golang 的 log 包主要提供了以下幾個具備輸出功能的函數:
          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{}) 
          這些函數的使用方法和 fmt 包完全相同,通過查看源碼可以發現,Fatal[ln|f] 和 Panic[ln|f] 實際上是調用的 Print[ln|f],而 Print[ln|f] 實際上是調用的 Output() 函數。
          其中 Fatal[ln|f] 是調用 Print[ln|f] 之后,又調用了 os.Exit(1) 退出程序。
          其中 Panic[ln|f] 是調用 Panic[ln|f] 之后,又調用了 panic() 函數,拋出一個恐慌。
          所以,我們很有必要閱讀一下 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) 
            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 
          通過閱讀 Output() 函數的源碼,可以發現使用互斥鎖來保證多個 goroutine 寫日志的安全,并且在調用 runtime.Caller() 函數之前,先釋放互斥鎖,獲取到信息后再加上互斥鎖來保證安全。
          使用 formatHeader() 函數來格式化日志的信息,然后保存到 buf 中,然后再把日志信息追加到 buf 的末尾,然后再通過判斷,查看日志是否為空或末尾不是 \n,如果是就再把 \n 追加到 buf 的末尾,最后將日志信息輸出。
          函數 Output() 的源碼也比較簡單,其中最值得注意的是 runtime.Caller() 函數,源碼如下:
          func Caller(skip int) (pc uintptr, file string, line int, ok bool) { 
           rpc := make([]uintptr, 1) 
           n := callers(skip+1, rpc[:]) 
           if n < 1 { 
            return 
           } 
           frame, _ := CallersFrames(rpc).Next() 
           return frame.PC, frame.File, frame.Line, frame.PC != 0 
          通過閱讀 runtime.Caller() 函數的源碼,可以發現它接收一個 int 類型的參數 skip,該參數表示跳過棧幀數,log 包中的輸出功能的函數,使用的默認值都是 2,原因是什么?
          舉例說明,比如在 main 函數中調用 log.Print,方法調用棧為 main->log.Print->*Logger.Output->runtime.Caller,所以此時參數 skip 的值為 2,表示 main 函數中調用 log.Print 的源文件和代碼行號;
          參數值為 1,表示 log.Print 函數中調用 *Logger.Output 的源文件和代碼行號;參數值為 0,表示 *Logger.Output 函數中調用 runtime.Caller 的源文件和代碼行號。
          至此,我們發現 log 包的輸出功能的函數,全部都是把信息輸出到控制臺,那么該怎么將信息輸出到文件中呢?
          函數 SetOutPut 就是用來設置輸出目標的,源碼如下:
          func SetOutput(w io.Writer) { 
           std.mu.Lock() 
           defer std.mu.Unlock() 
           std.out = w 
          我們可以通過函數 os.OpenFile 來打開一個用于 I/O 的文件,返回值作為函數 SetOutput 的參數。
          除此之外,讀者應該還發現了一個問題,輸出信息都是以日期和時間開頭,我們該怎么記錄更加豐富的信息呢?比如源文件和行號。
          這就用到了函數 SetFlags,它可以設置輸出的格式,源碼如下:
          func SetFlags(flag int) { 
           std.SetFlags(flag) 
          參數 flag 的值可以是以下任意常量:
          const ( 
           Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23 
           Ltime                         // the time in the local time zone: 01:23:23 
           Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime. 
           Llongfile                     // full file name and line number: /a/b/c/d.go:23 
           Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile 
           LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone 
           Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message 
           LstdFlags     = Ldate | Ltime // initial values for the standard logger 
          其中 Ldate、Ltime 和 Lmicroseconds 分別表示日期、時間和微秒,需要注意的是,如果設置 Lmicroseconds,那么設置 Ltime,也不會生效。
          其中 Llongfile 和 Lshortfile 分別代碼絕對路徑、源文件名、行號,和代碼相對路徑、源文件名、行號,需要注意的是,如果設置 Lshortfile,那么即使設置 Llongfile,也不會生效。
          其中 LUTC 表示設置時區為 UTC 時區。
          其中 LstdFlags 表示標準記錄器的初始值,包含日期和時間。
          截止到現在,還缺少點東西,就是日志信息的前綴,比如我們需要區分日志信息為 DEBUG、INFO 和 ERROR。是的,我們還有一個函數 SetPrefix 可以實現此功能,源碼如下:
          func SetPrefix(prefix string) { 
           std.SetPrefix(prefix) 
          函數 SetPrefix 接收一個 string 類型的參數,用來設置日志信息的前綴。
          03、Logger
          log 包定義了一個包含很多方法的類型 Logger。我們通過查看輸出功能的函數,發現它們都是調用 std.Output,std 是什么?我們查看 log 包的源碼。
          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 
           
          func New(out io.Writer, prefix string, flag int) *Logger { 
           return &Logger{out: out, prefix: prefix, flag: flag} 
           
          var std = New(os.Stderr, "", LstdFlags) 
          通過閱讀源碼,我們發現 std 實際上是 Logger 類型的一個實例,Output 是 Logger 的一個方法。
          std 通過 New 函數創建,參數分別是 os.Stderr、空字符串和 LstdFlags,分別表示標準錯誤輸出、空字符串前綴和日期時間。
          Logger 類型的字段,注釋已經說明了,這里就不再贅述了。
          自定義 Logger:
          func main () { 
           logFile, err := os.OpenFile("error1.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755) 
           if err != nil { 
            fmt.Println(err) 
            return 
           } 
           defer logFile.Close() 
           logs := DefinesLogger(logFile, "", log.LstdFlags|log.Lshortfile) 
           logs.Debug("message") 
           logs.Debugf("%s", "content") 
           
          // 自定義 logger 
          type Logger struct { 
           definesLogger *log.Logger 
           
          type Level int8 
           
          const( 
           LevelDebug Level = iota 
           LevelInfo 
           LevelError 
           
          func (l Level) String() string { 
           switch l { 
           case LevelDebug: 
            return " [debug] " 
           case LevelInfo: 
            return " [info] " 
           case LevelError: 
            return " [error] " 
           } 
           return "" 
           
          func DefinesLogger(w io.Writer, prefix string, flag int) *Logger { 
           l := log.New(w, prefix, flag) 
           return &Logger{definesLogger: l} 
           
          func (l *Logger) Debug(v ...interface{}) { 
           l.definesLogger.Print(LevelDebug, fmt.Sprint(v...)) 
           
          func (l *Logger) Debugf(format string, v ...interface{}) { 
           l.definesLogger.Print(LevelDebug, fmt.Sprintf(format, v...)) 
           
          func (l *Logger) Info(v ...interface{}) { 
           l.definesLogger.Print(LevelInfo, fmt.Sprint(v...)) 
           
          func (l *Logger) Infof(format string, v ...interface{}) { 
           l.definesLogger.Print(LevelInfo, fmt.Sprintf(format, v...)) 
           
          func (l *Logger) Error(v ...interface{}) { 
           l.definesLogger.Print(LevelError, fmt.Sprint(v...)) 
           
          func (l *Logger) Errorf(format string, v ...interface{}) { 
           l.definesLogger.Print(LevelError, fmt.Sprintf(format, v...)) 
          04、總結
          本文主要介紹 Golang 語言的標準庫中的 log 包,包括 log 包的函數和自定義類型 logger 的使用方法和一些細節上的注意事項。開篇也提到了,log 包不支持日志文件的切割,我們需要自己編碼去實現,或者使用三方庫,比如 lumberjack。在生產環境中,一般比較少用 log 包來記錄日志,通常會使用三方庫來記錄日志,比如 zap 和 logrus 等。


          相關文章

          我們很樂意傾聽您的聲音!
          即刻與我們取得聯絡
          成為日后肩并肩合作的伙伴。

          行業資訊

          聯系我們

          13387904606

          地址:新余市仙女湖區仙女湖大道萬商紅A2棟

          手機:13755589003
          QQ:122322500
          微信號:13755589003

          江西新余網站設計_小程序制作_OA系統開發_企業ERP管理系統_app開發-新余聯升網絡科技有限公司 贛ICP備19013599號-1   贛公網安備 36050202000267號   

          微信二維碼
          午夜激情在线观看

                <menuitem id="vnnt5"><em id="vnnt5"><progress id="vnnt5"></progress></em></menuitem><meter id="vnnt5"></meter>

                <sub id="vnnt5"></sub> <progress id="vnnt5"><strike id="vnnt5"><noframes id="vnnt5">

                <p id="vnnt5"></p>

                <dfn id="vnnt5"></dfn>

                  <meter id="vnnt5"></meter>