头图

How to play GO log

Last time we shared GO’s timer timer and timed task cron , let’s review:

  • What is Timer
  • How to use Timer
  • What is Ticker
  • How to use Ticker
  • What is cron
  • How to use cron

If you want to know the answer to the above question, welcome to check the article GO Timer Timer and timing task cron

Today, let’s take a look at the log package log

Specific source code path: src/log/log.go

How to simply use log package

Let’s take a look at using the log package in the editor. What are the hints?

At first glance, log package. They are not complicated at all. The methods are as shown in the figure above.

Let's use a small case, and then look at the data structure

package main

import "log"

func main() {
   log.Println("小魔童打日志 ... ")
   test := "Hello wrold "
   // Printf 有格式控制符
   log.Printf("%s 小魔童打日志 ... \n", test)

   log.Fatalln("小魔童 打日志,触发了 Fatal")

   log.Panicln("小魔童 打日志,触发了 Panic")
}

Run the above code, the effect is as follows:

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

The date, time, and printed content can be printed by default

How to configure log and the corresponding principle

Use GO inside this log package, we use the default log it is certainly not enough, for example, the log of the small print of cases, you do not know specifically which line of code to print out, and print settings to log which log files Inside, wait

Let's take a look at how to configure log , starting from the creation of logger

Create a new logger

Let's add a prefix to the basic log

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

The implementation effect is as follows:

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

Let's take a look at the specific implementation of the log.New

Specific source code path: src/log/log.go

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

It can be seen that the func New(out io.Writer, prefix string, flag int) *Logger method actually calls the Logger data structure, let’s take a look

// 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 has the above members. It seems that each parameter is easy to understand. You can basically know the meaning according to the member name.

  • mu sync.Mutex

Lock to ensure atomic operation

  • prefix string

The prefix of each log line

  • out io.Writer

The output location can be a file or standard output

  • buf []byte

Buffer

  • flag int

Specific attributes, we can see from the source code, there are several options for specific attributes as follows

These parameters are used to control the details of the log output, such as time, number of lines of code, prefix, etc.

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
)

The comments written in the source code are still very clear. What exactly each field does, and what effect will be after using it, is clear at a glance according to this comment.

We can check the source code to know why in the above small case, the date, time, and specific content are output by default in the log, because the log package will default to New for our default use

Here var std = New(os.Stderr, "", LstdFlags) New needs to be filled with attributes, and LstdFlags

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

LstdFlags attributes, the default is to print the date and time

// 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 for specific output, calling (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 { function does the following things:

  • Concatenate log string data
  • Output to out , where out is standard output by default, or you can set the output to a file by yourself

Configure a logger

Let's use log to set the output log to the file

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("小魔童的重要日志...")
}

Run the above code, the effect is as follows:

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 actually called Logger corresponding func (l *Logger) SetOutput(w io.Writer) method

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

    log.SetFlags actually called Logger corresponding SetFlags method

SetPrefix is the same reason

// 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
}

Summarize

  • How to use the log package
  • log package principle and specific implementation
  • Custom log

Welcome to like, follow, favorite

Friends, writing is not easy

Your support and encouragement are my motivation to keep sharing and improve quality

Well, that’s it for this time, GO unit test and performance test sharing

Technology is open, and our mindset should be more open. Embrace the change, live toward the sun, and work hard to move forward.

I am Nezha , welcome to like and follow the collection, see you next time~


阿兵云原生
192 声望37 粉丝