Gin框架组合(Zap、lumberjack、ini)使用手册
Gin
Gin是一個golang的微框架,封裝比較優雅,API友好,源碼注釋比較明確,已經發布了1.0版本。具有快速靈活,容錯方便等特點。其實對于golang而言,web框架的依賴要遠比Python,Java之類的要小。自身的net/http足夠簡單,性能也非常不錯。框架更像是一些常用函數或者工具的集合。借助框架開發,不僅可以省去很多常用的封裝帶來的時間,也有助于團隊的編碼風格和形成規范。
下面就Gin的用法做一個簡單的介紹。
首先需要安裝,安裝比較簡單,使用go get即可:
go get gopkg.in/gin-gonic/gin.v1 import ("gopkg.in/gin-gonic/gin.v1""net/http" )func main(){router := gin.Default()router.GET("/", func(c *gin.Context) {c.String(http.StatusOK, "Hello World")})router.Run(":8000") }日常我們是使用Gin框架編寫接口等等,可能會使用到一下幾種組件。
Zap 日志記錄
lumberjack 日志切割
ini 配置文件讀取
ini配置讀取
下載
go get github.com/go-ini/ini因為項目中要是到配置文件,我們可以采用ini模塊來實現
首先,創建一個config.ini配置文件:
port = 9000 release = false[mysql] user = db1 password = db1 host = 10.10.10.6 port = 3306 db = db1[log] level = debug filename = ./logs/info.log maxsize = 1 max_age = 30 max_backups = 5 package settingimport ("gopkg.in/ini.v1" )var Conf = new(AppConfig)// AppConfig 應用程序配置 type AppConfig struct {Release bool `ini:"release"`Port int `ini:"port"`*MySQLConfig `ini:"mysql"`*LogConfig `ini:"log"` }// MySQLConfig 數據庫配置 type MySQLConfig struct {User string `ini:"user"`Password string `ini:"password"`DB string `ini:"db"`Host string `ini:"host"`Port int `ini:"port"` }type LogConfig struct {Level string `ini:"level"`Filename string `ini:"filename"`MaxSize int `ini:"maxsize"`MaxAge int `ini:"max_age"`MaxBackups int `ini:"max_backups"` }//把先關初始的參數加載到全局變量,然后方便調用 func Init(file string) error {return ini.MapTo(Conf, file) }看一下調用方式
// 傳入配置文件路徑,加載配置文件,if err := setting.Init("conf/config.ini"); err != nil {fmt.Printf("load config from file failed, err:%v\n", err)return}fmt.Println("config.ini配置加載成功", setting.Conf.Port)// 創建數據庫// sql: CREATE DATABASE bubble;// 連接數據庫err := dao.InitMySQL(setting.Conf.MySQLConfig)if err != nil {fmt.Printf("init mysql failed, err:%v\n", err)return}fmt.Println("數據庫配置初始化加載成功", setting.Conf.MySQLConfig)if err := log.InitLogger(setting.Conf.LogConfig); err != nil {fmt.Printf("init logger failed, err:%v\n", err)return}已經加載全局變量,可以正常使用
mysql調用參數,
func InitMySQL(cfg *setting.MySQLConfig) (err error) {dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)DB, err = gorm.Open("mysql", dsn)if err != nil {return}DB.Debug()DB.LogMode(true)DB.SetLogger(&GormLogger{})return DB.DB().Ping() }Zap和lumberjack
Zap 日志記錄
lumberjack 日志切割
go get -u go.uber.org/zap go get -u github.com/natefinch/lumberjack大家可以參考
golang開發:類庫篇(一) Zap高性能日志類庫的使用 - 飛翔碼農 - 博客園使用zap接收gin框架默認的日志并配置日志歸檔 - 蘭玉磊的個人博客golang開發:類庫篇(一) Zap高性能日志類庫的使用 - 飛翔碼農 - 博客園
在Go語言項目中使用Zap日志庫 - 知乎
日志格式
{"level":"INFO","time":"2021-12-28T15:34:50.934+0800","caller":"log/logger.go:65","msg":"/","status":200,"method":"GET","path":"/","query":"","ip":"127.0.0.1","user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36","errors":"","cost":0.0010676} {"level":"DEBUG","time":"2021-12-28T15:34:51.194+0800","caller":"dao/mysql.go:23","msg":"sql","module":"gorm","type":"sql","src":"D:/桌面/bubble/models/todo.go:24","duration":0.0504676,"sql":"SELECT * FROM `todos` ","values":null,"rows_returned":3}我們需要把它集成到Gin框架中。
我這邊的配置,也會讀取ini的文件(看上面的代碼)
[log] level = debug filename = ./logs/info.log maxsize = 1 max_age = 30 max_backups = 5type LogConfig struct {Level string `ini:"level"`Filename string `ini:"filename"`MaxSize int `ini:"maxsize"`MaxAge int `ini:"max_age"`MaxBackups int `ini:"max_backups"` } package logimport ("bubble/setting""github.com/gin-gonic/gin""github.com/natefinch/lumberjack""go.uber.org/zap""go.uber.org/zap/zapcore""net""net/http""net/http/httputil""os""runtime/debug""strings""time" )var Logger *zap.Logger// InitLogger 初始化Logger func InitLogger(cfg *setting.LogConfig) (err error) {writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge)//fmt.Println(cfg.Filename)encoder := getEncoder()var l = new(zapcore.Level)err = l.UnmarshalText([]byte(cfg.Level))if err != nil {return}core := zapcore.NewCore(encoder, writeSyncer, l)Logger = zap.New(core, zap.AddCaller())return }func getEncoder() zapcore.Encoder {encoderConfig := zap.NewProductionEncoderConfig()encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoderencoderConfig.TimeKey = "time"encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoderencoderConfig.EncodeDuration = zapcore.SecondsDurationEncoderencoderConfig.EncodeCaller = zapcore.ShortCallerEncoderreturn zapcore.NewJSONEncoder(encoderConfig) }func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {lumberJackLogger := &lumberjack.Logger{Filename: filename,MaxSize: maxSize,MaxBackups: maxBackup,MaxAge: maxAge,}return zapcore.AddSync(lumberJackLogger) }// GinLogger 接收gin框架默認的日志 func GinLogger(logger *zap.Logger) gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()path := c.Request.URL.Pathquery := c.Request.URL.RawQueryc.Next()cost := time.Since(start)logger.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),)} }// GinRecovery recover掉項目可能出現的panic,并使用zap記錄相關日志 func GinRecovery(logger *zap.Logger, 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 boolif 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 {logger.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: errcheckc.Abort()return}if stack {logger.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),zap.String("stack", string(debug.Stack())),)} else {logger.Error("[Recovery from panic]",zap.Any("error", err),zap.String("request", string(httpRequest)),)}c.AbortWithStatus(http.StatusInternalServerError)}}()c.Next()} }注冊中間件的操作在routes.SetupRouter()中:
func SetupRouter() *gin.Engine {if setting.Conf.Release {gin.SetMode(gin.ReleaseMode)}#開始調用,中間件使用r := gin.New()r.Use(log.GinLogger(log.Logger), log.GinRecovery(log.Logger, true))}?main中調用
log.Logger.Debug("大家好,日志展示")defer log.Logger.Sync() {"level":"DEBUG","time":"2021-12-28T15:34:42.647+0800","caller":"bubble/main.go:39","msg":"大家好,日志展示"}日志格式可以定義json格式,后期可以直接接入elk分析展示日志
Gorm的sql日志記錄到文本
Gorm 建立了對 Logger 的支持,默認模式只會在錯誤發生的時候打印日志。可以通過gorm SetLogger(log logger)方法 改變gorm 打日志的行為。
gorm 中 logger的接口:
type logger interface {Print(ctx context.Context, v ...interface{}) }v 的值為:1個參數: level,表示這個是個什么請求,可以是“sql” 2個參數:打印sql的代碼行號,如/Users/yejianfeng/Documents/gopath/src/gorm-log/main.go:50, 3個參數: 執行時間戳 4個參數: sql語句 5參數:如果有預處理,請求參數,第六個參數是這個sql影響的行數。zaplog集成示例
DB.Debug() DB.LogMode(true) DB.SetLogger(&GormLogger{})// GormLogger struct type GormLogger struct{}// Print - Log Formatter func (*GormLogger) Print(v ...interface{}) {switch v[0] {case "sql":log.Debug("sql",zap.String("module", "gorm"),zap.String("type", "sql"),zap.Any("src", v[1]),zap.Any("duration", v[2]),zap.Any("sql", v[3]),zap.Any("values", v[4]),zap.Any("rows_returned", v[5]),)case "log":log.Debug("log", zap.Any("gorm", v[2]))} }?日志格式,sql語句已經打印到日志中
{"level":"DEBUG","time":"2021-12-28T15:34:51.194+0800","caller":"dao/mysql.go:23","msg":"sql","module":"gorm","type":"sql","src":"D:/桌面/bubble/models/todo.go:24","duration":0.0504676,"sql":"SELECT * FROM `todos` ","values":null,"rows_returned":3}參考文檔?GORM自定義日志配置 - 蒼山落暮 - 博客園
總結
以上是生活随笔為你收集整理的Gin框架组合(Zap、lumberjack、ini)使用手册的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 考研英语 各种阅读/翻译/新题型/完形填
- 下一篇: 基恩士XG-XvisionEditor修