GO的日志怎么玩

上次咱们分享了 GO的定时器timer和定时工作 cron,咱们来回顾一下:

  • Timer 是什么
  • Timer 如何应用
  • Ticker 是什么
  • Ticker 如何应用
  • cron 是什么
  • cron 如何应用

要是想理解如上问题的答案,欢送查看文章 GO的定时器Timer 和定时工作cron

明天咱们来看看 GO 的规范库外面的 日志包 log

具体源码门路:src/log/log.go

如何简略应用 log 包

咱们在编辑器中看看应用log包,会有什么提醒

一看,log包外面就波及这些办法和数据结构,一点都不简单,办法如上图

咱们来用一用小案例,再来看数据结构

package mainimport "log"func main() {   log.Println("小魔童打日志 ... ")   test := "Hello wrold "   // Printf 有格局控制符   log.Printf("%s 小魔童打日志 ... \n", test)   log.Fatalln("小魔童 打日志,触发了 Fatal")   log.Panicln("小魔童 打日志,触发了 Panic")}

运行上述代码,成果如下:

2021/06/xx xx:25:53 小魔童打日志 ...2021/06/xx xx:25:53 Hello wrold  小魔童打日志 ...2021/06/xx xx:25:53 小魔童 打日志,触发了 Fatalexit status 1

默认能够打印出日期、工夫、以及打印的内容

如何配置 log 以及相应的原理

应用 GO 外面的 这个log包,咱们应用默认的 log 那必定是不够用的,例如上述小案例打印的日志,你就不晓得具体是代码的哪一行打印进去的,以及设置日志打印到哪个日志文件外面,等等

咱们一起来看看如何配置 log,从创立logger开始看起

新建一个 logger

咱们在根本的日志上,加上一个前缀

func main() {  // 打印到规范输入上   myLog := log.New(os.Stdout, "<XMT>", log.Lshortfile|log.Ldate|log.Ltime)   myLog.Println("小魔童打印了带有前缀的日志 ... ")}

执行成果如下:

<XMT>2021/06/28 12:35:47 main.go:20: 小魔童打印了带有前缀的日志 ...

咱们看看 log.New 办法的具体实现

具体源码门路:src/log/log.go

func New(out io.Writer, prefix string, flag int) *Logger {   return &Logger{out: out, prefix: prefix, flag: flag}}

能够看出 func New(out io.Writer, prefix string, flag int) *Logger 办法实际上是调用了 Logger 数据结构,咱们瞅瞅

// A Logger represents an active logging object that generates lines of// output to an io.Writer. Each logging operation makes a single call to// the Writer's Write method. A Logger can be used simultaneously from// multiple goroutines; it guarantees to serialize access to the Writer.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}

type Logger struct有下面这几个成员,看上去每一个参数都比拟好了解,依据成员名字就可能根本晓得其含意

  • mu sync.Mutex

锁,确保原子操作

  • prefix string

每一行日志的前缀

  • out io.Writer

输入地位,能够是文件,能够是规范输入

  • buf []byte

缓冲区的buffer

  • flag int

具体属性,通过源码咱们能够看出,具体属性有如下几种抉择

这些参数,都是用于管制日志输入的细节,例如工夫,代码行数,前缀等等

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)

源码写的正文还是很清晰的,具体每一个字段是做什么的,用了之后是什么样的成果,依据这个正文,高深莫测

咱们查看源码就晓得,为什么上述的小案例,日志外面默认就输入了 日期、工夫、具体内容,因为 log包外面会默认 New 一个日志,供咱们默认应用

此处 var std = New(os.Stderr, "", LstdFlags) New 外面的第三个参数须要填属性,此处默认填了 LstdFlags

LstdFlags = Ldate | Ltime // initial values for the standard logger

LstdFlags 属性,默认是打印日期,和工夫

// Println calls l.Output to print to the logger.// Arguments are handled in the manner of fmt.Println.func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) }

(l *Logger) Println进行具体的输入,调用了(l *Logger) Output

// Output writes the output for a logging event. The string s contains// the text to print after the prefix specified by the flags of the// Logger. A newline is appended if the last character of s is not// already a newline. Calldepth is used to recover the PC and is// provided for generality, although at the moment on all pre-defined// paths it will be 2.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}

func (l *Logger) Output(calldepth int, s string) error {函数做了如下几个事件:

  • 拼接日志字符串数据
  • 输入到 out 中 , 此处的out 默认是规范输入,也能够本人设置输入到文件

配置一个 logger

咱们用一下 log 外面设置输入日志到文件中

func main() {   logFile, err := os.OpenFile("./XMT.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)   if err != nil {      fmt.Println("os.OpenFile error :", err)      return   }   // 设置输入地位 ,外面有锁进行管制   log.SetOutput(logFile)   // 设置日志属性   log.SetFlags(log.Llongfile | log.Ltime | log.Ldate)   // 打印日志   log.Println("小魔童的 新 日志 ... ")   // 手动设置前缀   log.SetPrefix("【重点】")      log.Println("小魔童的重要日志...")}

运行上述代码,成果如下:

2021/06/28 12:57:14 D:/mycode/my_new_first/my_log/main.go:36: 小魔童的 新 日志 ... 【重点】2021/06/28 12:57:14 D:/mycode/my_new_first/my_log/main.go:40: 小魔童的重要日志...
  • log.SetOutput

    log.SetOutput 实际上是调用了 Logger 对应的 func (l *Logger) SetOutput(w io.Writer) 办法

func (l *Logger) SetOutput(w io.Writer) {   l.mu.Lock()   defer l.mu.Unlock()   l.out = w}
  • log.SetFlags

    log.SetFlags 实际上是调用了 Logger 对应的 SetFlags 办法

SetPrefix 也是同样的情理

// SetFlags sets the output flags for the logger.// The flag bits are Ldate, Ltime, and so on.func (l *Logger) SetFlags(flag int) {   l.mu.Lock()   defer l.mu.Unlock()   l.flag = flag}

总结

  • 如何应用log
  • log 包原理和具体实现
  • 自定义日志

欢送点赞,关注,珍藏

敌人们,写作不易

你的反对和激励,是我保持分享,提高质量的能源

好了,本次就到这里,GO的单元测试和性能测试分享

技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。

我是小魔童哪吒,欢送点赞关注珍藏,下次见~