Go log Log

Source: Internet
Author: User
Tags log log

After we develop the program, if there are some problems need to debug the program, the log is necessary, this is our analysis program problems commonly used means.

Log usage

Log analysis, is based on the output of the log information, analysis of the potential problems of mining, we use fmt.Println series functions can also achieve the purpose, because they can also be the information we need to output to the terminal or other files. But fmt.Println the series function output of the system is relatively simple, such as no time, there is no source code lines, etc., for us to troubleshoot problems, missing a lot of information.

In this respect, the go language provides us with a standard log package to track log records. Let's look at log the use of log packages.

func main() {    log.Println("飞雪无情的博客:","http://www.flysnow.org")    log.Printf("飞雪无情的公众号:%s\n","flysnow_org")}

It is very simple to use, and the function name and usage is fmt similar to the package, but its output is time stamped by default.

2017/04/29 13:18:44 飞雪无情的博客: http://www.flysnow.org2017/04/29 13:18:44 飞雪无情的公众号:flysnow_org

So we know very clearly, the time to log these logs, which is very useful for us to troubleshoot problems.

With time, we also want more information, the inevitable occurrence of source code line number, etc., for this log package log provides us with a customizable configuration, so that we can customize the log header information.

func init(){    log.SetFlags(log.Ldate|log.Lshortfile)}

We use the function init , this function main can be initialized before the function executes, can help us to do some configuration, here we custom log header information is time + file name + source code line number. log.Ldate|log.Lshortfilethat is, the middle is a bitwise operator | , which is then set by the function log.SetFlags . Now let's run a look at the output log.

2017/04/29 main.go:10: 飞雪无情的博客: http://www.flysnow.org2017/04/29 main.go:11: 飞雪无情的公众号:flysnow_org

More than the previous example, the source file and the line number, but the time is missing, this is the result of our custom. Now let's look at the log packages that provide us with the option constants that can be defined.

const (    Ldate         = 1 << iota     //日期示例: 2009/01/23    Ltime                         //时间示例: 01:23:23    Lmicroseconds                 //毫秒示例: 01:23:23.123123.    Llongfile                     //绝对路径和行号: /a/b/c/d.go:23    Lshortfile                    //文件和行号: d.go:23.    LUTC                          //日期时间转为0时区的    LstdFlags     = Ldate | Ltime //Go提供的标准抬头信息)

This is a log package to define some of the header information, there are date, time, millisecond time, absolute path and line number, file name and line number, and so on, there are comments on the above, it should be noted that: if set Lmicroseconds , then Ltime it does not take effect Lshortfile , set, and Llongfile will not take effect, We can test it for ourselves.

LUTCVery special, if we configure the time tag, then if set LUTC , it will be the output date time to 0 time zone of the date and time display.

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

So for the time of our East eight, we subtract 8 hours, we look at the output:

2017/04/29 05:46:29 飞雪无情的博客: http://www.flysnow.org2017/04/29 05:46:29 飞雪无情的公众号:flysnow_org

The last one that LstdFlags represents the standard log header is the default, including the date and time.

Most of us have a lot of business, every business need to log, then there is no way to differentiate these businesses? This makes it much easier for us to find the logs.

In this case, the Go language also helps us to consider, this is to set the log prefix, such as a User Center system log, we can set this.

func init(){    log.SetPrefix("【UserCenter】")    log.SetFlags(log.LstdFlags | log.Lshortfile |log.LUTC)}

By log.SetPrefix specifying the prefix of the output log, which we specify as 【UserCenter】 , we can then see that the printout of the log clearly marks the business of our logs.

【UserCenter】2017/04/29 05:53:26 main.go:11: 飞雪无情的博客: http://www.flysnow.org【UserCenter】2017/04/29 05:53:26 main.go:12: 飞雪无情的公众号:flysnow_org

logPackage In addition to a Print series of functions, Fatal as well as a Panic series of functions, which indicates that the Fatal program encountered a fatal error, need to exit, Fatal when the log is used, then the program exits, that is, the Fatal equivalent of calling Print the print log first, The exit program is then called os.Exit(1) .

Panicthe same is true of the same series of functions, which means that the Print log is used first, and then the calling panic() function throws a panic, and unless recover() the function is used, the program prints the error stack information and the program terminates.

The source code of these series of functions is put down here for better understanding.

func Println(v ...interface{}) {    std.Output(2, fmt.Sprintln(v...))}func Fatalln(v ...interface{}) {    std.Output(2, fmt.Sprintln(v...))    os.Exit(1)}func Panicln(v ...interface{}) {    s := fmt.Sprintln(v...)    std.Output(2, s)    panic(s)}

Implementation principle

Through the source code above, we found that the log packet log of these functions are similar, the key output log is the std.Output method.

func New(out io.Writer, prefix string, flag int) *Logger {    return &Logger{out: out, prefix: prefix, flag: flag}}var std = New(os.Stderr, "", LstdFlags)

From the above source code can be seen, the variable std is actually a *Logger , through the log.New function creation, the default output to the os.Stderr device, the prefix is empty, the log header information is the standard header LstdFlags .

os.StderrThe output device corresponding to the standard error warning information in UNIX is also used as the default log output destination. For the first time, there are standard output devices os.Stdout as well as standard input devices os.Stdin .

var (    Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")    Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")    Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr"))

The above is the standard three devices defined for UNIX, respectively, for input, output, and warning error messages. Understand os.Stderr , now we look at Logger this structure, the information and operation of the log, all through this Logger operation.

type Logger struct {    mu     sync.Mutex // ensures atomic writes; protects the following fields    prefix string     // prefix to write at beginning of each line    flag   int        // properties    out    io.Writer  // destination for output    buf    []byte     // for accumulating text to write}
    1. The field mu is a mutex, mainly to ensure that this logger is Logger also safe under multiple goroutine.
    2. Field prefix is the prefix for each row of logs
    3. Field flag is log header information
    4. The field out is the destination of the log output, which is by default os.Stderr .
    5. The field buf is a log output text buffer, which will eventually be written out in.

Understanding the structure Logger of the field, you can now look at its most important method Output , this method will output the formatted good log information.

func (l *Logger) Output(calldepth int, s string) error {    now := time.Now() // get this early.    var file string    var line int    //加锁,保证多goroutine下的安全    l.mu.Lock()    defer l.mu.Unlock()    //如果配置了获取文件和行号的话    if l.flag&(Lshortfile|Llongfile) != 0 {        //因为runtime.Caller代价比较大,先不加锁        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')    }    //输出拼接好的缓冲buf里的日志信息到目的地    _, err := l.out.Write(l.buf)    return err}

The entire code is relatively concise, in order to multi-goroutine security mutex is also used, but in the acquisition of the call stack information, the first time to unlock, because this process is heavier. After obtaining information such as file, line number and so on, continue to add the mutex to ensure security.

The following is relatively simple, the formatHeader Main method is to format the log header information, and then stored in buf the buffer, and finally put our own log information to the buf back of the buffer, and then for the log output to append a newline character, so that each log output is a row of one line.

With the final log information buf , and then write it to the output of the destination out , which is a type of implementation of the io.Writer interface, as long as the implementation of this interface, can be used as the output destination.

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

logThe function of the package SetOutput , you can set the output destination. This is a little bit easier to describe runtime.Caller , and it can get the invocation information of the runtime method.

func Caller(skip int) (pc uintptr, file string, line int, ok bool)

skipthe parameter represents the number of skipped stack frames, which means that 0 the caller is not skipped runtime.Caller . 1is to go up one level, indicating the caller's caller.

The log log is used in the package 2 , which means that we call in the source code log.Print , and the log.Fatal log.Panic callers of these functions.

In the main case of a function call log.Println , main->log.Println->*Logger.Output->runtime.Caller This is a method call stack, so at this point, the value of skip represents:

    1. 0Represents *Logger.Output runtime.Caller The source code file and line number that is called in
    2. 1Represents log.Println *Logger.Output The source code file and line number that is called in
    3. 2Represents main log.Println The source code file and line number that is called in

So that's log why the value of this bag skip is always 2 the reason.

Customize your own logs

Through the above source analysis, we know that the root of the log is a logger Logger , so we customize their own log, in fact, is to create a different Logger .

var (    Info *log.Logger    Warning *log.Logger    Error * log.Logger)func init(){    errFile,err:=os.OpenFile("errors.log",os.O_CREATE|os.O_WRONLY|os.O_APPEND,0666)    if err!=nil{        log.Fatalln("打开日志文件失败:",err)    }    Info = log.New(os.Stdout,"Info:",log.Ldate | log.Ltime | log.Lshortfile)    Warning = log.New(os.Stdout,"Warning:",log.Ldate | log.Ltime | log.Lshortfile)    Error = log.New(io.MultiWriter(os.Stderr,errFile),"Error:",log.Ldate | log.Ltime | log.Lshortfile)}func main() {    Info.Println("飞雪无情的博客:","http://www.flysnow.org")    Warning.Printf("飞雪无情的公众号:%s\n","flysnow_org")    Error.Println("欢迎关注留言")}

We defined three different logger based on the log level, respectively, for Info the Warning Error output of different levels of logging. These three types of loggers are log.New created using functions.

When the logger is created here, Info and Warning both are normal, Error there are multiple destination outputs, where you can simultaneously output the error log to the os.Stderr file we created errors.log .

io.MultiWriterThe function can wrap multiple io.Writer for one io.Writer , so that we can achieve the same io.Writer purpose for multiple output logs simultaneously.

io.MultiWriterImplementation is also simple, define a type implementation io.Writer , and then loop through the implemented method Write to invoke the multiple interfaces to be wrapped 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}

Here we define a number of logger to distinguish between different log levels, the use of more trouble, in this case, you can use the third-party log framework, you can also customize the wrapper definition, directly through different levels of methods to record different levels of the log, you can set the level of logging and so on.

Each Golang package requires only one of the best article series
Http://www.nextblockchain.top/books/golangpackage/summary

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.