preface
Why logs are needed
- Debugging development
- Program run log
- User Behavior Log
Different purposes determine the format and frequency of log output. As a developer, the purpose of logging during debug development is to output as much information as possible (above and below, variable values…). , to aid in development testing, so the log format should be easy to read and print frequently. While the program is running, the log format tends to be structured (easy to analyze and search) and printed less frequently for performance and to focus on critical information such as errors.
Go library Log
use
We often use the following three sets of functions:
Print/Printf/Println
: Displays log informationPanic/Panicf/Panicln
: After the log information is printed, Panic is called with the assembled string as the parameterFatal/Fatalf/Fatalln
: After the log information is printed,os.Exit(1)
Exit the program
The suffix “f” is formatted output. The “ln” suffix adds a newline character. However, a newline character is automatically added in log printing scenarios. Panic differs from Fatal in that Panic can be caught.
The following is an example:
package main
import "log"
func main(a) {
log.Println("Log Message 1")
log.Print("Log Message 2")
log.Panicln("Log Message 3")
log.Fatalln("Log Message 4") // Cannot run
}
Copy the code
2021/11/09 15:41:34 Message 1 2021/11/09 15:41:34 Message 2 2021/11/09 15:41:34 Message 3 Panic: Message 3 Goroutine 1 [running]: log.Panicln({0xc0000bdf60, 0x0, 0x1015ba5}) /usr/local/opt/go/libexec/src/log/log.go:368 +0x65
main.main()
/Users/xiaohe/go/src/github.com/he2121/log_test/main.go:8 +0xa8
Copy the code
As you can see, the default log format is time + MSG. Why is that? Can it be changed?
Principle and customization
func Println(v ...interface{}) {
std.Output(2, fmt.Sprintln(v...))
}
Copy the code
You can see that the previous methods all call the Output method of the STD object.
var std = New(os.Stderr, "", LstdFlags)
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}
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
}
Copy the code
STD is constructed using log.new with three arguments
io.Writer
: Any implementationWriter
The structure of the interface, the log will be written to this,std
The output position of isos.Stderr
prefix
: custom prefix string, STD is passed an empty stringflag
: some flag bits, custom prefix,std
isLstdFlags
The time in the default log format comes from this flag bit
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
)
Copy the code
The log output position, custom prefix string, and flag can be customized
Example (modify the STdLogger directly here, in the actual project should be a New logger) :
func main(a) {
var buf bytes.Buffer
log.SetOutput(&buf)
log.SetPrefix("[info: ]")
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("Custom log format")
//fmt.Print(&buf)
}
Copy the code
The above example puts the log output location in bytes.buffer, which can also be changed to file, stdout, network…
Prefix with [info:] string
Flag added Lshortfile: the file name and location of the print log statement.
Execute the above statement and find no console output.
Because the log output position is no longer stderr. Uncomment the last line and print a buff.
[info:]2021/11/09 16:49:27 main.go:14: custom log formatCopy the code
advantages
- Go built-in, easy to use
disadvantages
- Lack of common log levels,
Info/debug/error...
- Lack of structured log output function
- performance
Go third-party log library
logrus
Logrus: Logrus is a structured logger for Go (Golang) that is fully API compatible with the standard library Log. (19.1 k stars)
Why logrus ?
- Compare all log levels
PanicLevel: logs, panic FatalLevel: logs, programsexitErrorLevel: ErrorLevel log WarnLevel: warning level log InfoLevel: critical information level log DebugLevel: debugging level TraceLevel: tracing levelCopy the code
-
Support for structured logging
-
Ease of use
- hook
WithField
- .
Simple to use
package main
import "github.com/sirupsen/logrus"
func main(a) {
logrus.SetLevel(logrus.TraceLevel) // Set low level in test environment,
// Logrus.setlevel (logrus.infolevel) // In the production environment, you need to consider performance, focus on key information, and set the level higher
// logrus.setreportCaller (true) // The name and location of the caller
// logrus.setFormatter (new(logrus.jsonformatter)) // Set the log format to JSON
logrus.Traceln("The trace log")
logrus.Debugln("The debug log")
logrus.Infoln("The Info log")
logrus.Warnln("Warn log")
logrus.Errorln("error msg")
logrus.Fatalf("Fatal log")
logrus.Panicln("Panic log")}Copy the code
For ease of use, libraries typically create an object with a default value, and the outermost methods of the package typically operate on the default object, as logrus and log do
Before structuring:
The INFO [0000] / Users/xiaohe/go/src/github.com/he2121/log_test/main.go:12 main. The main () the INFO logs WARN [0000] / Users/xiaohe/go/src/github.com/he2121/log_test/main.go:13 main. The main () WARN log ERRO[0000]/Users/xiaohe/go/src/github.com/he2121/log_test/main.go:14 main.main() error msg The FATA [0000] / Users/xiaohe/go/src/github.com/he2121/log_test/main.go:15 main. The main () fatal logCopy the code
After structuring:
{"file":"/Users/xiaohe/go/src/github.com/he2121/log_test/main.go:12"."func":"main.main"."level":"info"."msg":"The Info log"."time":"2021-11-09T17:37:27+08:00"}
{"file":"/Users/xioahe/go/src/github.com/he2121/log_test/main.go:13"."func":"main.main"."level":"warning"."msg":"Warn log"."time":"2021-11-09T17:37:27+08:00"}
{"file":"/Users/xiaohe/go/src/github.com/he2121/log_test/main.go:14"."func":"main.main"."level":"error"."msg":"error msg"."time":"2021-11-09T17:37:27+08:00"}
{"file":"/Users/xiaohe/go/src/github.com/he2121/log_test/main.go:15"."func":"main.main"."level":"fatal"."msg":"Fatal log"."time":"2021-11-09T17:37:27+08:00"}
Copy the code
zap
Zap: Fast, structured, hierarchical logger (14K stars).
why zap?
-
Compare all log levels
-
Support for structured logging
-
performance
Simple to use
Zap provides two types of loggers – the Sugared Logger and Logger
The Sugared Logger values both performance and ease of use, supporting structured and printf-style logging.
Logger is very performance-oriented and does not provide a Printf-style API (which reduces interface{} and reflection performance losses), as shown in the following example:
package main
import "go.uber.org/zap"
func main(a) {
// sugared
sugar := zap.NewExample().Sugar()
sugar.Infof("hello! name:%s,age:%d"."xiaomin".20) // Printf style, easy to use
// logger
logger := zap.NewExample()
logger.Info("hello!", zap.String("name"."xiaomin"), zap.Int("age".20)) // Emphasize performance
}
Copy the code
// output
{"level":"info"."msg":"hello! name:xiaomin,age:20"}
{"level":"info"."msg":"hello!"."name":"xiaomin"."age"20} :Copy the code
Zap has three default configurations to create a Logger: Example, development, production, example:
package main
import "go.uber.org/zap"
func main(a) {
// example
logger := zap.NewExample()
logger.Info("example")
// Development
logger,_ = zap.NewDevelopment()
logger.Info("Development")
// Production
logger,_ = zap.NewProduction()
logger.Info("Production")}Copy the code
// output
{"level":"info"."msg":"example"} 2021-11-09T21:02:07.181+0800 INFO log_test/main.go:11 Development {2021-11-09T21:02:07.181+0800 INFO log_test/main.go:11 Development {"level":"info"."ts": 1636462927.182271."caller":"log_test/main.go:14"."msg":"Production"}
Copy the code
You can see that the log level, log output format, and default fields are different.
You can also customize loggers, as in the following example
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
func main(a) {
encoder := getEncoder()
sync := getWriteSync()
core := zapcore.NewCore(encoder, sync, zapcore.InfoLevel)
logger := zap.New(core)
logger.Info("The info log",zap.Int("line".1))
logger.Error("The info log", zap.Int("line".2))}// Set the log format for encoding
func getEncoder(a) zapcore.Encoder {
return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}
// The location where the log is written
func getWriteSync(a) zapcore.WriteSyncer {
file, _ := os.OpenFile("./log.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
syncFile := zapcore.AddSync(file)
syncConsole := zapcore.AddSync(os.Stderr)
return zapcore.NewMultiWriteSyncer(syncConsole, syncFile)
}
// output
// create log.txt and append logs
// console Displays logs
/ / {" level ":" info ", "ts" : 1636471657.16419, "MSG" : "the info log", "line" : 1}
/ / {" level ":" error ", "ts" : 1636471657.1643898, "MSG" : "the info log", "line" : 2}
Copy the code
New(core zapcore. core, options… Option) *Logger, need to construct zapcore.core
-
With the NewCore(ENC Encoder, WS WriteSyncer, enab LevelEnabler) Core method, you need to pass in three more parameters
-
Encoder: set the encoding of the log format, you can set the JSON or text structure, you can also customize the JSON key value, time format…
-
Ws WriteSyncer: the location responsible for writing logs. In this example, write to both file and console, but also to the network.
-
LevelEnabler: Sets the log level
-
-
Options is an interface that implements the Apply (*Logger) method and extends many functions
- Override the configuration of core
- Increase the hook
- Add key value information
- Error logs are output separately
conclusion
First introduced the Go standard library log use and custom process, summarized the advantages and disadvantages of log, thus introduced the topic of third-party library Logrus and ZAP, two libraries are very good and powerful, very recommended use