日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Go標準庫日志打印及同時輸出到控制臺與文件_Golang

作者:Go和分布式IM ? 更新時間: 2022-12-27 編程語言

打印

在使用go寫一些小程序時,我們沒必要引入額外的包,直接使用fmt標準包打印即可:

import "fmt"

func main() {
   fmt.Println("line1")
   fmt.Print("line2")
   fmt.Printf("line%d \n", 3)

   str1 := fmt.Sprintln("hello", 3)
   str2 := fmt.Sprint("hello ", 1, " 2")
   str3 := fmt.Sprintf("hello %d", 1)
   fmt.Print(str1, str2, str3)
}

line1
line2line3?
hello 3
hello 1 2hello 1

那么,有些場景下,我們希望能同時打印到日志文件中要怎么辦呢?

log包

標準庫提供了log組件,用法和fmt一致,有3種方式:

import “log"

func main() {
   log.Println("line1")
   log.Print("line2")
   log.Printf("line%d \n", 3)
}

和fmt的區別就是多了時間:

2021/08/25 17:23:47 line1
2021/08/25 17:23:47 line2
2021/08/25 17:23:47 line3?

我們通過SetFlag函數,可以設置打印的格式:

// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
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
)

比如,我們只需要時間和文件名:

import “log"

func main() {
   log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

   log.Println("line1")
   log.Print("line2")
   log.Printf("line%d \n", 3)
}

此時,再次運行,則會打印文件和行號:

2021/08/25 17:27:56 mod_unread_redis.go:32: line1
2021/08/25 17:27:56 mod_unread_redis.go:33: line2
2021/08/25 17:27:56 mod_unread_redis.go:34: line3

如何輸出日志到文件?

log包使用非常簡單,默認情況下,只會輸出到控制臺。

我們可以使用SetOutput改變輸出流,比如輸出到文件。

先來看一下函數原型,其接收一個io.Writer接口:

// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
    // ...
}

那么,我們就可以創建一個文件流設置一下就行了。

// 創建、追加、讀寫,777,所有權限
f, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
   return
}
defer func() {
   f.Close()
}()

log.SetOutput(f)

此時,在運行,我們發現日志會輸出到文件,但是控制臺沒有任何東西輸出了。

如何同時輸出到控制臺和文件?

標準庫io包中,有一個MultiWriter,可以把文件流和控制臺標準輸出流整合到一個io.Writer上,其實現上就是一個數組,在執行寫操作時,遍歷數組:

// MultiWriter creates a writer that duplicates its writes to all the
// provided writers, similar to the Unix tee(1) command.
//
// Each write is written to each listed writer, one at a time.
// If a listed writer returns an error, that overall write operation
// stops and returns the error; it does not continue down the list.
func MultiWriter(writers ...Writer) Writer {
   allWriters := make([]Writer, 0, len(writers))
   for _, w := range writers {
      if mw, ok := w.(*multiWriter); ok {
         allWriters = append(allWriters, mw.writers...)
      } else {
         allWriters = append(allWriters, w)
      }
   }
   return &multiWriter{allWriters}
}

// 重寫io.Writer的Write函數函數,本質上就是遍歷數組,比較巧妙
func (t *multiWriter) Write(p []byte) (n int, err error) {
   for _, w := range t.writers {
      n, err = w.Write(p)
      if err != nil {
         return
      }
      if n != len(p) {
         err = ErrShortWrite
         return
      }
   }
   return len(p), nil
}

使用方式如下:

func main() {
   f, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
   if err != nil {
      return
   }
   defer func() {
      f.Close()
   }()

   // 組合一下即可,os.Stdout代表標準輸出流
   multiWriter := io.MultiWriter(os.Stdout, f)
   log.SetOutput(multiWriter)

   log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

   log.Println("line1")
   log.Print("line2")
   log.Printf("line%d \n", 3)
}

此時,再運行,則會同時輸出到控制臺和文件中。

2021/08/25 17:38:02 mod_unread_redis.go:42: line1
2021/08/25 17:38:02 mod_unread_redis.go:43: line2
2021/08/25 17:38:02 mod_unread_redis.go:44: line3?

附:日志切割(按文件大小切割、按日期切割)

其實就是每次記錄文件的大小,超過了就重新寫一個文件。

通過Stat()函數拿到文件的一些信息

    open, _:= os.Open("文件名")
    stat, _, := open.Stat()
    stat.Size()//拿到文件大小

日期切割:

拿到文件的名稱或者檢查下有沒有當天的日志文件,沒有就創建新增。

總結

原文鏈接:https://blog.csdn.net/xmcy001122/article/details/119916227

欄目分類
最近更新