Go語言中配置實現(xiàn)Logger日志的功能詳解
為什么需要Logger
一般在開發(fā)項目的時候我們都是需要一個存儲日志的文件,因為在部署項目以后,我們只能通過去篩查日志進行檢索問題,這時候日志是否可以呈現(xiàn)清晰這個對于我們進行排查工作是十分重要的,所以Logger能否展示出我們最想要的錯誤展示方式是很有必要的!本章節(jié)的案例是基于gin框架和viper進行編寫一個Logger的日志文件,使用zap日志庫進行記錄日志,日志會根據(jù)yaml文件定義的 mode進行判斷是否是開發(fā)環(huán)境還是線上環(huán)境進行寫的。
實現(xiàn)Logger.go
首先看一下yaml文件的配置
在yaml文件中定義了一個app,app下面有一個mode,這個mode就是用來識別我們是開發(fā)環(huán)境還是線上環(huán)境的。
app: name: "web_app" mode: "dev" port: 8080 version: "v0.01" log: level: "debug" filename: "web_app.log" max_size: 200 max_age: 30 max_backups: 7
settings的定義
var Conf AppConfig
type AppConfig struct {
App App `mapstructure:"app" yaml:"app"`
Log LogConfig `mapstructure:"log"`
}
type App struct {
Name string `mapstructure:"name"`
Mode string `mapstructure:"mode"`
Version string `mapstructure:"version"`
Port int `mapstructure:"port" yaml:"port"`
}
type LogConfig struct {
Level string `mapstructure:"level"`
Filename string `mapstructure:"filename"`
MaxSize int `mapstructure:"max_size"`
MaxAge int `mapstructure:"max_age"`
MaxBackups int `mapstructure:"max_backups"`
}Logger.go
這里需要結(jié)合自己的實際進行修改,如果復(fù)制了import中的依賴,需要執(zhí)行一下:go mod tidy
package logger
import(
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
var logger *zap.Logger
// Init 的兩個參數(shù): cfg 和 mode
func Init(cfg *settings.LogConfig, mode string) (err error) {
writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)
encoder := getEncoder()
var l = new(zapcore.Level)
err = l.UnmarshalText([]byte(cfg.Level))
if err != nil {
return err
}
var core zapcore.Core
// 開發(fā)模式,日志輸出終端
if mode == "dev" {
consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
core = zapcore.NewTee(
zapcore.NewCore(encoder, writeSyncer, l), // 保存在日志中
// 錯誤信息展示在終端
zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),
)
} else {
core = zapcore.NewCore(encoder, writeSyncer, l)
}
logger = zap.New(core, zap.AddCaller())
lg := zap.New(core, zap.AddCaller())
// 替換zap庫中的全局
zap.ReplaceGlobals(lg)
return err
}
func getEncoder() zapcore.Encoder {
//return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
encodeConfig := zapcore.EncoderConfig{
LevelKey: "level",
TimeKey: "ts",
NameKey: "logger",
CallerKey: "caller",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
//return zapcore.NewConsoleEncoder(zap.NewProductionEncoderConfig())
return zapcore.NewConsoleEncoder(encodeConfig)
}
// GinLogger 接收gin框架默認(rèn)的日志
func GinLogger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
query := c.Request.URL.RawQuery
c.Next()
cost := time.Since(start)
zap.L().Info(path,
zap.Int("status", c.Writer.Status()),
zap.String("method", c.Request.Method),
zap.String("path", path),
zap.String("query", query),
zap.String("ip", c.ClientIP()),
zap.String("user-agent", c.Request.UserAgent()),
zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
zap.Duration("cost", cost),
)
}
}
func getLogWriter(string, int, int, int) zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: viper.GetString("log.filename"),
MaxSize: viper.GetInt("log.max_size"), // 單位是M
MaxBackups: viper.GetInt("log.max_backups"), // 備份數(shù)量
MaxAge: viper.GetInt("log.max_age"), // 最大備份天數(shù)
Compress: false, // 是否壓縮
}
return zapcore.AddSync(lumberJackLogger)
}
func GinRecovery(stack bool) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
brokenPipe = true
}
}
}
httpRequest, _ := httputil.DumpRequest(c.Request, false)
if brokenPipe {
zap.L().Error(c.Request.URL.Path,
zap.Any("error", err),
zap.String("request", string(httpRequest)),
)
// If the connection is dead, we can't write a status to it.
c.Error(err.(error)) // nolint: errcheck
c.Abort()
return
}
if stack {
zap.L().Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
zap.String("stack", string(debug.Stack())),
)
} else {
zap.L().Error("[Recovery from panic]",
zap.Any("error", err),
zap.String("request", string(httpRequest)),
)
}
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()
}
}最后在main.go中進行注冊方法
func main() {
// 1、加載配置文件
if err := settings.Init(); err != nil {
fmt.Println("init setting failed", err)
return
}
// 2、初始化日志
if err := logger.Init(&settings.Conf.Log, settings.Conf.App.Mode); err != nil {
fmt.Printf("init logger failed, err:%v\n", err)
}
defer zap.L().Sync()
zap.L().Debug("logger init success...")
}在開發(fā)環(huán)境(dev)中的測試結(jié)果
與自己想要的結(jié)果是一致的:

到此這篇關(guān)于Go語言中配置實現(xiàn)Logger日志的功能詳解的文章就介紹到這了,更多相關(guān)Go配置Logger日志內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang?內(nèi)存模型The?Go?Memory?Model
這篇文章主要為大家介紹了Golang?內(nèi)存模型The?Go?Memory?Model實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
Go語言利用ffmpeg轉(zhuǎn)hls實現(xiàn)簡單視頻直播
這篇文章主要為大家介紹了Go語言利用ffmpeg轉(zhuǎn)hls實現(xiàn)簡單視頻直播,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-04-04
Go語言實現(xiàn)類似c++中的多態(tài)功能實例
Go本身不具有多態(tài)的特性,不能夠像Java、C++那樣編寫多態(tài)類、多態(tài)方法。但是,使用Go可以編寫具有多態(tài)功能的類綁定的方法。下面來一起看看吧2016-09-09

