1.简介<br />
本文主要是简单介绍下zap库的使用样例,之前看了网上的一些入门资料后,发现有些样例比较简单,结合工程实践,提供一个简单的工程样例;zap是Uber开源的高性能日志库,对zap还不太了解的同学可以先搜下对应的网上资料、有个大概了解。<br />
2.实现<br />
本次实践要的实现目标是:<br />
1)支持不同级别的日志写入不同文件;<br />
2)支持不同级别的日志写入同一文件;<br />
3)支持根据环境配置,控制低级别的日志不打印到线上文件;<br />
4)支持一个logger对象控制多个日志文件,内部根据Enable()实现级别区分;<br />
在学习zap之前,先参考了网上资料,总结的来说,使用zap生成logger对象涉及到的三要素是 : 文件对象、输出格式以及日志级别,所以会先围绕上面的三要素生成logger对象;由于日志格式化方式各不相同,所以在调用具体输出函数之前,还要对日志内容进行组合;为了方便全局操作,对日志对象封装,抽象出全局日志对象。<br />
样例实现:
```
package main
import (
"bytes"
"context"
"fmt"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"net/http"
"os"
"os/signal"
"runtime"
"strconv"
"syscall"
"time"
)
var Mode = "Offline"
// 全局日志对象
var GLog *GlobalLogger
type GlobalLogger struct {
Logger *FileLogger // 服务内部普通日志记录
Request *FileLogger // 服务内部入库日志记录
}
// 考虑到要按照指定格式记录日志,所以没有直接对外暴露zap.Logger,而是对zap.Logger封装一下
type FileLogger struct {
logger* zap.Logger
}
func (fl *FileLogger)DEBUG(msg string, fields ...zap.Field) {
msg = GenerateLoggerPrefix("DEBUG", fields...) + msg
fl.logger.Debug(msg, fields...)
}
func (fl *FileLogger)INFO(msg string, fields ...zap.Field){
msg = GenerateLoggerPrefix("INFO", fields...) + msg
fl.logger.Info(msg, fields...)
}
func (fl *FileLogger)WARNING(msg string, fields ...zap.Field){
msg = GenerateLoggerPrefix("WARNING", fields...) + msg
fl.logger.Warn(msg, fields...)
}
func (fl *FileLogger)FATAL(msg string, fields ...zap.Field){
msg = GenerateLoggerPrefix("FATAL", fields...) + msg
fl.logger.Fatal(msg, fields...)
}
func (fl *FileLogger)SYNC() {
fl.logger.Sync()
}
var LoggerPrefix = ": TQY "
// GenerateLoggerPrefix 生成调试跟踪文件的行前缀,fields是引用传递
func GenerateLoggerPrefix(level string, fields ...zap.Field) string {
data := bytes.Buffer{}
data.WriteString(level)
data.WriteString(LoggerPrefix)
data.WriteString("[" + time.Now().Format("2006-01-02 15:04:05") + "] ")
a, _, c, _ := runtime.Caller(2)
data.WriteString("[" + runtime.FuncForPC(a).Name() + ":" + strconv.Itoa(c) + "] ")
qid := ""
for _, field := range fields {
if field.Key == "Qid" {
qid = field.String
}
}
if qid != "" {
data.WriteString("[" + qid + "] ")
}
return data.String()
}
func GenerateGlobalLogger(dir string) error{
// zap生成logger对象的三要素是 : 文件对象、输出格式化以及日志级别
// step 1 : 生成文件对象
debugFile, err := os.OpenFile(dir + "server.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
return err
}
warningFile, err := os.OpenFile(dir + "server.log.wf", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
return err
}
requestFile, err := os.OpenFile(dir + "request.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
if err != nil {
return err
}
// step 2 : 生成输出格式
outputEncoder := GenerateLogFormat()
// step 3 : 配置日志级别
debugLevel := zap.LevelEnablerFunc(func(lv zapcore.Level) bool {
// 模式判断,线上可以使用gin.Mode()判断
if Mode == "Online" {
return lv == zapcore.InfoLevel
}
return lv <= zapcore.InfoLevel
})
warningLevel := zap.LevelEnablerFunc(func(lv zapcore.Level) bool {
return lv == zapcore.WarnLevel
})
requestLevel := zap.LevelEnablerFunc(func(lv zapcore.Level) bool {
return lv == zapcore.InfoLevel
})
// step 4 : 创建配置集
coreServer := zapcore.NewTee(
zapcore.NewCore(outputEncoder, debugFile, debugLevel),
zapcore.NewCore(outputEncoder, warningFile, warningLevel),
)
coreRequest := zapcore.NewCore(outputEncoder, requestFile, requestLevel)
// step 5 : 创建logger对象
loggerServer := zap.New(coreServer)
loggerRequest := zap.New(coreRequest)
// step 6 : 初始化全局日志对象
GLog = &GlobalLogger{
Logger:&FileLogger{logger:loggerServer},
Request:&FileLogger{logger:loggerRequest},
}
return nil
}
// 生成日志输出格式
func GenerateLogFormat() zapcore.Encoder {
encoderConfig := zapcore.EncoderConfig{
NameKey: "N",
CallerKey: "C",
MessageKey: "M",
StacktraceKey: "S",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
}
return zapcore.NewConsoleEncoder(encoderConfig)
}
func main() {
if err := GenerateGlobalLogger("./"); err != nil {
fmt.Println(err)
return
}
go func() {
GLog.Logger.DEBUG("logger debug")
GLog.Logger.INFO("logger info")
GLog.Logger.WARNING("logger warning")
GLog.Request.INFO("request info")
}()
engine := gin.Default()
server := &http.Server{
Addr: ":16889",
Handler: engine,
}
go signalListen(server)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
GLog.Logger.FATAL(fmt.Sprintf("Listen And Serve err: %v", err.Error()))
return
}
}
// 信号捕捉,用于缓存flush
func signalListen(server *http.Server) {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
for {
<-c
GLog.Logger.SYNC()
GLog.Request.SYNC()
server.Shutdown(context.Background())
}
}
```
有疑问加站长微信联系(非本文作者)