乐趣区

关于后端:GO的日志怎么玩

GO 的日志怎么玩

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

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

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

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

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

如何简略应用 log 包

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

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

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

package main

import "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 小魔童 打日志,触发了 Fatal
exit 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 的单元测试和性能测试分享

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

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

退出移动版