偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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

開發(fā) 前端
Golang 語言的標準庫中提供了一個簡單的 log 日志包,它不僅提供了很多函數(shù),還定義了一個包含很多方法的類型 Logger。但是它也有缺點,比如不支持區(qū)分日志級別,不支持日志文件切割等。

01、介紹

Golang 語言的標準庫中提供了一個簡單的 log 日志包,它不僅提供了很多函數(shù),還定義了一個包含很多方法的類型 Logger。但是它也有缺點,比如不支持區(qū)分日志級別,不支持日志文件切割等。

[[379196]]

02、函數(shù)

Golang 的 log 包主要提供了以下幾個具備輸出功能的函數(shù):

 

  1. func Fatal(v ...interface{})  
  2. func Fatalf(format string, v ...interface{})  
  3. func Fatalln(v ...interface{})  
  4. func Panic(v ...interface{})  
  5. func Panicf(format string, v ...interface{})  
  6. func Panicln(v ...interface{})  
  7. func Print(v ...interface{})  
  8. func Printf(format string, v ...interface{})  
  9. func Println(v ...interface{}) 

這些函數(shù)的使用方法和 fmt 包完全相同,通過查看源碼可以發(fā)現(xiàn),F(xiàn)atal[ln|f] 和 Panic[ln|f] 實際上是調用的 Print[ln|f],而 Print[ln|f] 實際上是調用的 Output() 函數(shù)。

其中 Fatal[ln|f] 是調用 Print[ln|f] 之后,又調用了 os.Exit(1) 退出程序。

其中 Panic[ln|f] 是調用 Panic[ln|f] 之后,又調用了 panic() 函數(shù),拋出一個恐慌。

所以,我們很有必要閱讀一下 Output() 函數(shù)的源碼。

函數(shù) Output() 的源碼:

  1. func (l *Logger) Output(calldepth int, s string) error { 
  2.  now := time.Now() // get this early. 
  3.  var file string 
  4.  var line int 
  5.  l.mu.Lock() 
  6.  defer l.mu.Unlock() 
  7.  if l.flag&(Lshortfile|Llongfile) != 0 { 
  8.   // Release lock while getting caller info - it's expensive. 
  9.   l.mu.Unlock() 
  10.   var ok bool 
  11.   _, file, line, ok = runtime.Caller(calldepth) 
  12.   if !ok { 
  13.    file = "???" 
  14.    line = 0 
  15.   } 
  16.   l.mu.Lock() 
  17.  } 
  18.  l.buf = l.buf[:0] 
  19.  l.formatHeader(&l.buf, now, file, line) 
  20.  l.buf = append(l.buf, s...) 
  21.  if len(s) == 0 || s[len(s)-1] != '\n' { 
  22.   l.buf = append(l.buf, '\n'
  23.  } 
  24.  _, err := l.out.Write(l.buf) 
  25.  return err 

通過閱讀 Output() 函數(shù)的源碼,可以發(fā)現(xiàn)使用互斥鎖來保證多個 goroutine 寫日志的安全,并且在調用 runtime.Caller() 函數(shù)之前,先釋放互斥鎖,獲取到信息后再加上互斥鎖來保證安全。

使用 formatHeader() 函數(shù)來格式化日志的信息,然后保存到 buf 中,然后再把日志信息追加到 buf 的末尾,然后再通過判斷,查看日志是否為空或末尾不是 \n,如果是就再把 \n 追加到 buf 的末尾,最后將日志信息輸出。

函數(shù) Output() 的源碼也比較簡單,其中最值得注意的是 runtime.Caller() 函數(shù),源碼如下:

  1. func Caller(skip int) (pc uintptr, file string, line int, ok bool) { 
  2.  rpc := make([]uintptr, 1) 
  3.  n := callers(skip+1, rpc[:]) 
  4.  if n < 1 { 
  5.   return 
  6.  } 
  7.  frame, _ := CallersFrames(rpc).Next() 
  8.  return frame.PC, frame.File, frame.Line, frame.PC != 0 

通過閱讀 runtime.Caller() 函數(shù)的源碼,可以發(fā)現(xiàn)它接收一個 int 類型的參數(shù) skip,該參數(shù)表示跳過棧幀數(shù),log 包中的輸出功能的函數(shù),使用的默認值都是 2,原因是什么?

舉例說明,比如在 main 函數(shù)中調用 log.Print,方法調用棧為 main->log.Print->*Logger.Output->runtime.Caller,所以此時參數(shù) skip 的值為 2,表示 main 函數(shù)中調用 log.Print 的源文件和代碼行號;

參數(shù)值為 1,表示 log.Print 函數(shù)中調用 *Logger.Output 的源文件和代碼行號;參數(shù)值為 0,表示 *Logger.Output 函數(shù)中調用 runtime.Caller 的源文件和代碼行號。

至此,我們發(fā)現(xiàn) log 包的輸出功能的函數(shù),全部都是把信息輸出到控制臺,那么該怎么將信息輸出到文件中呢?

函數(shù) SetOutPut 就是用來設置輸出目標的,源碼如下:

  1. func SetOutput(w io.Writer) { 
  2.  std.mu.Lock() 
  3.  defer std.mu.Unlock() 
  4.  std.out = w 

我們可以通過函數(shù) os.OpenFile 來打開一個用于 I/O 的文件,返回值作為函數(shù) SetOutput 的參數(shù)。

除此之外,讀者應該還發(fā)現(xiàn)了一個問題,輸出信息都是以日期和時間開頭,我們該怎么記錄更加豐富的信息呢?比如源文件和行號。

這就用到了函數(shù) SetFlags,它可以設置輸出的格式,源碼如下:

  1. func SetFlags(flag int) { 
  2.  std.SetFlags(flag) 

參數(shù) flag 的值可以是以下任意常量:

  1. const ( 
  2.  Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23 
  3.  Ltime                         // the time in the local time zone: 01:23:23 
  4.  Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime. 
  5.  Llongfile                     // full file name and line number: /a/b/c/d.go:23 
  6.  Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile 
  7.  LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone 
  8.  Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message 
  9.  LstdFlags     = Ldate | Ltime // initial values for the standard logger 

其中 Ldate、Ltime 和 Lmicroseconds 分別表示日期、時間和微秒,需要注意的是,如果設置 Lmicroseconds,那么設置 Ltime,也不會生效。

其中 Llongfile 和 Lshortfile 分別代碼絕對路徑、源文件名、行號,和代碼相對路徑、源文件名、行號,需要注意的是,如果設置 Lshortfile,那么即使設置 Llongfile,也不會生效。

其中 LUTC 表示設置時區(qū)為 UTC 時區(qū)。

其中 LstdFlags 表示標準記錄器的初始值,包含日期和時間。

截止到現(xiàn)在,還缺少點東西,就是日志信息的前綴,比如我們需要區(qū)分日志信息為 DEBUG、INFO 和 ERROR。是的,我們還有一個函數(shù) SetPrefix 可以實現(xiàn)此功能,源碼如下:

  1. func SetPrefix(prefix string) { 
  2.  std.SetPrefix(prefix) 

函數(shù) SetPrefix 接收一個 string 類型的參數(shù),用來設置日志信息的前綴。

03、Logger

log 包定義了一個包含很多方法的類型 Logger。我們通過查看輸出功能的函數(shù),發(fā)現(xiàn)它們都是調用 std.Output,std 是什么?我們查看 log 包的源碼。

  1. type Logger struct { 
  2.  mu     sync.Mutex // ensures atomic writes; protects the following fields 
  3.  prefix string     // prefix on each line to identify the logger (but see Lmsgprefix) 
  4.  flag   int        // properties 
  5.  out    io.Writer  // destination for output 
  6.  buf    []byte     // for accumulating text to write 
  7.  
  8. func New(out io.Writer, prefix string, flag int) *Logger { 
  9.  return &Logger{outout, prefix: prefix, flag: flag} 
  10.  
  11. var std = New(os.Stderr, "", LstdFlags) 

通過閱讀源碼,我們發(fā)現(xiàn) std 實際上是 Logger 類型的一個實例,Output 是 Logger 的一個方法。

std 通過 New 函數(shù)創(chuàng)建,參數(shù)分別是 os.Stderr、空字符串和 LstdFlags,分別表示標準錯誤輸出、空字符串前綴和日期時間。

Logger 類型的字段,注釋已經說明了,這里就不再贅述了。

自定義 Logger:

  1. func main () { 
  2.  logFile, err := os.OpenFile("error1.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755) 
  3.  if err != nil { 
  4.   fmt.Println(err) 
  5.   return 
  6.  } 
  7.  defer logFile.Close() 
  8.  logs := DefinesLogger(logFile, "", log.LstdFlags|log.Lshortfile) 
  9.  logs.Debug("message"
  10.  logs.Debugf("%s""content"
  11.  
  12. // 自定義 logger 
  13. type Logger struct { 
  14.  definesLogger *log.Logger 
  15.  
  16. type Level int8 
  17.  
  18. const( 
  19.  LevelDebug Level = iota 
  20.  LevelInfo 
  21.  LevelError 
  22.  
  23. func (l Level) String() string { 
  24.  switch l { 
  25.  case LevelDebug: 
  26.   return " [debug] " 
  27.  case LevelInfo: 
  28.   return " [info] " 
  29.  case LevelError: 
  30.   return " [error] " 
  31.  } 
  32.  return "" 
  33.  
  34. func DefinesLogger(w io.Writer, prefix string, flag int) *Logger { 
  35.  l := log.New(w, prefix, flag) 
  36.  return &Logger{definesLogger: l} 
  37.  
  38. func (l *Logger) Debug(v ...interface{}) { 
  39.  l.definesLogger.Print(LevelDebug, fmt.Sprint(v...)) 
  40.  
  41. func (l *Logger) Debugf(format string, v ...interface{}) { 
  42.  l.definesLogger.Print(LevelDebug, fmt.Sprintf(format, v...)) 
  43.  
  44. func (l *Logger) Info(v ...interface{}) { 
  45.  l.definesLogger.Print(LevelInfo, fmt.Sprint(v...)) 
  46.  
  47. func (l *Logger) Infof(format string, v ...interface{}) { 
  48.  l.definesLogger.Print(LevelInfo, fmt.Sprintf(format, v...)) 
  49.  
  50. func (l *Logger) Error(v ...interface{}) { 
  51.  l.definesLogger.Print(LevelError, fmt.Sprint(v...)) 
  52.  
  53. func (l *Logger) Errorf(format string, v ...interface{}) { 
  54.  l.definesLogger.Print(LevelError, fmt.Sprintf(format, v...)) 

04、總結

本文主要介紹 Golang 語言的標準庫中的 log 包,包括 log 包的函數(shù)和自定義類型 logger 的使用方法和一些細節(jié)上的注意事項。開篇也提到了,log 包不支持日志文件的切割,我們需要自己編碼去實現(xiàn),或者使用三方庫,比如 lumberjack。在生產環(huán)境中,一般比較少用 log 包來記錄日志,通常會使用三方庫來記錄日志,比如 zap 和 logrus 等。

責任編輯:未麗燕 來源: Golang語言開發(fā)棧
相關推薦

2021-09-13 05:02:49

GogRPC語言

2021-06-07 23:19:44

Golang語言 Defer

2021-07-12 05:05:59

Golang語言字段

2021-06-09 23:36:46

Golang語言版本

2021-04-28 09:02:48

Golang語言Context

2021-10-10 23:02:49

Golang語言代碼

2024-06-19 10:31:48

2021-12-13 01:24:14

語言Golang panic

2021-06-29 23:40:19

Golang語言并發(fā)

2020-10-22 06:59:09

GolangRust語言

2023-04-02 23:13:07

Go語言bufio

2023-02-13 00:24:37

Go語言日志庫

2023-11-13 21:55:12

Go編程

2022-01-04 23:13:57

語言PanicGolang

2021-11-08 23:09:07

Go排序數(shù)據(jù)

2021-07-26 11:19:43

微服務開發(fā)技術

2021-11-14 23:05:28

GoCast語言

2009-12-15 17:53:18

Ruby標準庫

2021-11-28 23:06:30

語言編程接口

2021-10-31 23:01:50

語言拼接字符串
點贊
收藏

51CTO技術棧公眾號