This is a creation in Article, where the information may have evolved or changed.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public or website http://www.flysnow.org/, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.
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.
1234 |
func Main () {log. Println ("Blizzard Ruthless Blog:", "http://www.flysnow.org") log. Printf ("Blizzard ruthless public Number:%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.
12 |
2017/04/29 13:18:44 Blizzard Ruthless blog: http://www.flysnow.org2017/04/29 13:18:44 Blizzard Ruthless public number: 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.
123 |
funcinit() {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.Lshortfile
that 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.
12 |
*/// Main. go:Blizzard Ruthless blog: http://www.flysnow.org//(main). go:One: Blizzard ruthless public number: 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.
123456789 |
const (Ldate = 1 << iota //date example: 2009/01/23 ltime //time example : 01:23:23 lmicroseconds //ms Example: 01:23:23.123123. Llongfile //absolute path and line number:/a/b/c/d.go:23 lshortfile //file and line number: D.go:23. LUTC //date time converted to 0 time zone lstdflags = Ldate | Ltime //go provides standard header information ) |
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.
LUTC
Very 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.
1 |
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:
12 |
2017/04/29 05:46:29 Blizzard Ruthless blog: http://www.flysnow.org2017/04/29 05:46:29 Blizzard Ruthless public number: 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.
1234 |
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.
12 |
"Usercenter" 2017/04/29 05:53:26 main.go:11: Blizzard Ruthless blog: http://www.flysnow.org "Usercenter" 2017/04/29 05:53:26 main.go : 12: Blizzard ruthless public number: flysnow_org |
log
Package 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)
.
Panic
the 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 Span class= "params" > (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.
12345 |
func New stringint) *Logger {return &logger{out:out, Prefix:prefix, Flag:flag}}var"" , 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.Stderr
The 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
.
12345 |
var (Stdin = NewFile (uintptr"/dev/stdin") Stdout = NewFile (uintptr"/dev/stdout" ) Stderr = NewFile (uintptr"/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.
1234567 |
type Logger struct {mu sync. Mutex //ensures atomic writes; protects the following fields prefix String //prefix to write at beginning of all line flag in T //properties out IO. Writer //destination for output buf []byte //for accumulating text to write } |
- The field
mu
is a mutex, mainly to ensure that this logger is Logger
also safe under multiple goroutine.
- Field
prefix
is the prefix for each row of logs
- Field
flag
is log header information
- The field
out
is the destination of the log output, which is by default os.Stderr
.
- The field
buf
is a log output text buffer, which will eventually be written out
in.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public or website http://www.flysnow.org/, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.
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.
12345678910111213141516171819202122232425262728293031 |
func (l *logger) Output(calldepth int, s string) Error {Now: = time. Now ()//Get this early.varFilestringvarLineint//locking to ensure safety under multiple GoroutineL.mu.lock ()deferL.mu.unlock ()//If get file and line number are configuredifl.flag& (lshortfile| Llongfile)! =0{//Because of runtime. Caller price is relatively large, first without lockingL.mu.unlock ()varOkBOOL_, file, line, OK = runtime. Caller (calldepth)if!ok {file ="???"line =0}//Get the line number and other information, and then lock to ensure securityL.mu.lock ()}//Stitching Our log information and settings log headerL.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 ')}//Output splicing good buffer buf log information to the destination_, Err: = L.out.write (L.BUF)returnErr |
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.
12345 |
func (l *logger) Setoutput (w io.) Writer) {l.mu.lock ()defer l.mu.unlock () l.out = w} |
log
The 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.
1 |
func Caller int) uintptrstringintbool) |
skip
the parameter represents the number of skipped stack frames, which means that 0
the caller is not skipped runtime.Caller
. 1
is 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:
0
Represents *Logger.Output
runtime.Caller
The source code file and line number that is called in
1
Represents log.Println
*Logger.Output
The source code file and line number that is called in
2
Represents 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
.
1234567891011121314151617181920212223 |
var(Info *log. Loggerwarning *log. Loggererror * log. Logger) func init() {Errfile,err:=os. OpenFile ("Errors.log", OS. O_create|os. O_wronly|os. O_append,0666)iferr!=Nil{log. Fatalln ("Failed to open log file:", 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 ("Blizzard Ruthless blog:","http://www.flysnow.org") warning.printf ("Blizzard ruthless public Number:%s\n","flysnow_org") Error.println ("Welcome to follow the message")} |
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.MultiWriter
The 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.MultiWriter
Implementation 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
.
12345678910111213 |
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 = Errshortwritereturn }}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.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public or website http://www.flysnow.org/, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.