1. Golang语言的日志记录与错误追踪
在Golang语言开发过程中,日志记录和错误追踪是非常重要的功能,通过这些功能,我们可以根据代码运行情况及时发现问题,从而快速解决。
1.1 日志记录
Golang提供了非常便捷的日志记录库,使用者只需要引入log包,就可以快速构建自己的日志记录系统。log包提供了三个默认的Logger,分别是:默认Logger、标准Logger和自定义Logger。
默认Logger:
默认Logger是最简单的一种Logger,输出的日志信息只包含日志内容和文件行号:
package main
import (
"log"
)
func main() {
log.Print("This is a log message")
}
标准Logger:
标准Logger将日志信息包括在日志前缀中,并可以对日志信息进行定制化。代码示例:
package main
import (
"log"
"os"
)
func main() {
logFile, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
log.Fatal("Failed to open log file", err)
}
defer logFile.Close()
logger := log.New(logFile, "Example", log.LstdFlags)
logger.Println("This is a log message")
logger.Printf("This is a formatted %s message", "log")
}
自定义Logger:
自定义Logger可以根据不同的需求对日志进行个性化处理,例如改变日志前缀、过滤日志内容等。自定义Logger通过向log.New()传递一些选项来创建。代码示例:
package main
import (
"log"
)
func main() {
logger := log.New(new(SyslogWriter), "Example", log.LstdFlags)
logger.Println("This is a log message")
logger.Printf("This is a formatted %s message", "log")
}
type SyslogWriter struct {
}
func (w *SyslogWriter) Write(bytes []byte) (int, error) {
// code goes here
return len(bytes), nil
}
1.2 错误追踪
在Golang中,错误是通过返回值来表示的。因此,在开发过程中,我们需要能够快速地捕获错误并进行处理。
使用Golang内置函数panic()可以抛出错误,使用recover()可以捕获到panic()抛出的错误。代码示例:
package main
import (
"fmt"
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("Something bad happened")
}
在实际开发中,我们需要在需要处理错误的地方主动调用panic(),然后在需要捕获错误的地方调用recover()。在错误处理时,我们可以选择直接输出错误信息或者将其记录到日志中。
2. 使用第三方库进行日志记录和错误追踪
除了Golang内置的日志记录和错误追踪功能,还有很多优秀的第三方库可供选择。下面介绍两个比较常用的库:logrus和zap。
2.1 logrus库
logrus库是一个广泛使用的日志库,它能够提供非常多样化的日志记录选项。logrus提供了非常完善的日志记录格式控制、日志级别控制、日志输出到文件以及hook功能,因此,广泛应用于各类Golang项目中。
日志格式控制:
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
// 输出日志JSON格式
log.SetFormatter(&log.JSONFormatter{})
// 输出日志文本格式
log.SetFormatter(&log.TextFormatter{})
}
日志级别控制:
package main
import (
log "github.com/sirupsen/logrus"
)
func main() {
// 输出所有级别日志
log.SetLevel(log.TraceLevel)
// 输出Info级别以上日志
log.SetLevel(log.InfoLevel)
// 输出Error级别以上日志
log.SetLevel(log.ErrorLevel)
}
日志输出到文件:
package main
import (
log "github.com/sirupsen/logrus"
"os"
)
func main() {
logFile, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
// 日志输出到文件
log.SetOutput(logFile)
}
hook功能:
package main
import (
log "github.com/sirupsen/logrus"
"net/smtp"
)
func main() {
emailHook, err := NewEmailHook(
"smtp.gmail.com",
587,
"youremail@gmail.com",
"yourpassword",
[]string{"to@example.com"},
"Application Error",
log.ErrorLevel,
nil,
)
if err != nil {
log.Fatal(err)
}
log.AddHook(emailHook)
log.WithFields(log.Fields{
"animal": "walrus",
"number": 1,
"size": 10,
}).Info("A walrus is here")
log.Error("Something went wrong")
}
type emailHook struct {
username string
password string
host string
port int
to []string
subject string
level log.Level
formatter log.Formatter
auth smtp.Auth
}
func NewEmailHook(host string, port int, username string, password string, to []string, subject string, level log.Level, formatter log.Formatter) (*emailHook, error) {
auth := smtp.PlainAuth("", username, password, host)
return &emailHook{username, password, host, port, to, subject, level, formatter, auth}, nil
}
func (hook *emailHook) Levels() []log.Level {
return log.AllLevels[:hook.level+1]
}
func (hook *emailHook) Fire(entry *log.Entry) error {
body, err := hook.formatter.Format(entry)
if err != nil {
return err
}
msg := []byte("To: " + strings.Join(hook.to, ",") + "\r\n" +
"Subject: " + hook.subject + "\r\n" +
"\r\n" +
string(body))
return smtp.SendMail(hook.host+":"+strconv.Itoa(hook.port), hook.auth, hook.username, hook.to, msg)
}
2.2 zap库
相比logrus,zap库更侧重于性能优化,其宣称的日志速度是标准库的8-10倍。zap库提供了详细的日志级别控制、详细的日志记录格式控制、高性能、可定制机制。
日志级别控制:
package main
import (
"go.uber.org/zap"
)
func main() {
// 输出所有级别日志
logger, _ := zap.NewProduction()
// 输出Debug级别以上日志
logger, _ := zap.NewDevelopment()
logger.Debug("debug msg")
}
日志输出格式控制:
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
config := zap.Config{
Encoding: "json",
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stderr"},
EncoderConfig: zapcore.EncoderConfig{
MessageKey: "msg",
LevelKey: "level",
TimeKey: "time",
CallerKey: "caller",
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
ConsoleSeparator: "\t",
},
}
logger, _ := config.Build()
logger.Info("Info message")
}
高性能:
在zap库中,日志的输出是通过buffer缓存实现的。同时,zap提供了针对性能的两种写入策略,即async和sync策略。当使用async策略时,日志被缓存到内存中,并在后台日志处理器上异步地回写到磁盘上,这可以显著提高性能。
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
func main() {
config := zap.Config{
Encoding: "json",
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stderr"},
EncoderConfig: zapcore.EncoderConfig {
MessageKey: "msg",
LevelKey: "level",
TimeKey: "time",
CallerKey: "caller",
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
ConsoleSeparator: "\t",
},
}
logger, _ := config.Build()
defer logger.Sync()
logger.Info("Info message")
logger.Debug("Debug message")
logger.Warn("Warn message")
logger.Error("Error message")
logger.Fatal("Fatal message")
}
3. 小结
本文介绍了Golang语言的日志记录和错误追踪的使用。我们首先介绍了Golang内置提供的日志记录和错误追踪的功能,然后介绍了logrus和zap两个流行的第三方库的使用。最后,我们总结了这些底层和第三方库提供的常用选项。