go語言web開發系列之五:gin用zap+file-rotatelogs實現日誌記錄及按日期切分日誌
阿新 • • 發佈:2020-12-18
技術標籤:用go做web開發gogolangwebapilog4j2
一,安裝需要用到的庫:
1,安裝zap日誌庫:
[email protected]:/data/liuhongdi/zaplog$ go get -u go.uber.org/zap
2,安裝go-file-rotatelogs庫
[email protected]:/data/liuhongdi/zaplog2$ go get -u github.com/lestrrat/go-file-rotatelogs
說明:劉巨集締的go森林是一個專注golang的部落格,
地址:https://blog.csdn.net/weixin_43881017
說明:作者:劉巨集締 郵箱: [email protected]
二,演示專案的相關資訊
1,專案地址:
https://github.com/liuhongdi/digv05
2,專案的功能:整合zap和go-file-rotatelogs實現記錄日誌和日誌切分
3,專案結構:如圖:
三,go程式碼說明:
1,config/config.yaml
Database: DBType: mysql UserName: root Password: password Host: 127.0.0.1:3306 DBName: dig Charset: utf8 ParseTime: True MaxIdleConns: 10 MaxOpenConns: 30 Server: RunMode: debug HttpPort: 8000 ReadTimeout: 60 WriteTimeout: 60 Log: LogFilePath: /data/gologs/logs LogInfoFileName: info LogWarnFileName: warn LogFileExt: log
2,global/logger.go
package global import ( "github.com/liuhongdi/digv05/pkg/zaplog" "go.uber.org/zap" ) var ( Logger *zap.SugaredLogger ) //建立logger func SetupLogger() (error) { var err error filepath:= LogSetting.LogFilePath infofilename:= LogSetting.LogInfoFileName warnfilename:= LogSetting.LogWarnFileName fileext:= LogSetting.LogFileExt //infofilename,warnfilename,fileext string Logger,err = zaplog.GetInitLogger(filepath,infofilename,warnfilename,fileext) if err != nil { return err } defer Logger.Sync() return nil }
3,pkg/zaplog/zaplog.go
package zaplog
import (
rotatelogs "github.com/lestrrat/go-file-rotatelogs"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"io"
"time"
)
//get logger
func GetInitLogger(filepath,infofilename,warnfilename,fileext string) (*zap.SugaredLogger,error) {
encoder := getEncoder()
//兩個判斷日誌等級的interface
//warnlevel以下屬於info
infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl < zapcore.WarnLevel
})
//warnlevel及以上屬於warn
warnLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.WarnLevel
})
infoWriter,err := getLogWriter(filepath+"/"+infofilename,fileext)
if (err != nil) {
return nil,err
}
warnWriter,err2 := getLogWriter(filepath+"/"+warnfilename,fileext)
if (err2 != nil) {
return nil,err2
}
//建立具體的Logger
core := zapcore.NewTee(
zapcore.NewCore(encoder, infoWriter, infoLevel),
zapcore.NewCore(encoder, warnWriter, warnLevel),
)
loggerres := zap.New(core, zap.AddCaller())
return loggerres.Sugar(),nil
}
//Encoder
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
//encoderConfig := zap.NewDevelopmentEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
//LogWriter
func getLogWriter(filePath,fileext string) (zapcore.WriteSyncer,error) {
warnIoWriter,err := getWriter(filePath,fileext)
if (err != nil) {
return nil,err
}
return zapcore.AddSync(warnIoWriter),nil
}
//日誌檔案切割,按天
func getWriter(filename,fileext string) (io.Writer,error) {
// 儲存30天內的日誌,每24小時(整點)分割一次日誌
hook, err := rotatelogs.New(
filename+"_%Y%m%d."+fileext,
rotatelogs.WithLinkName(filename),
rotatelogs.WithMaxAge(time.Hour*24*30),
rotatelogs.WithRotationTime(time.Hour*24),
)
/*
//供測試用,儲存30天內的日誌,每1分鐘(整點)分割一次日誌
hook, err := rotatelogs.New(
filename+"_%Y%m%d%H%M.log",
rotatelogs.WithLinkName(filename),
rotatelogs.WithMaxAge(time.Hour*24*30),
rotatelogs.WithRotationTime(time.Minute*1),
)
*/
if err != nil {
//panic(err)
return nil,err
}
return hook,nil
}
4,router/router.go
package router
import (
"github.com/gin-gonic/gin"
"github.com/liuhongdi/digv05/controller"
"github.com/liuhongdi/digv05/global"
"github.com/liuhongdi/digv05/pkg/result"
"runtime/debug"
)
func Router() *gin.Engine {
router := gin.Default()
//處理異常
router.NoRoute(HandleNotFound)
router.NoMethod(HandleNotFound)
router.Use(Recover)
// 路徑對映
articlec:=controller.NewArticleController()
router.GET("/article/getone/:id", articlec.GetOne);
router.GET("/article/list", articlec.GetList);
return router
}
//404
func HandleNotFound(c *gin.Context) {
global.Logger.Errorf("handle not found: %v", c.Request.RequestURI)
//global.Logger.Errorf("stack: %v",string(debug.Stack()))
result.NewResult(c).Error(404,"資源未找到")
return
}
//500
func Recover(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
//列印錯誤堆疊資訊
//log.Printf("panic: %v\n", r)
global.Logger.Errorf("panic: %v", r)
//log stack
global.Logger.Errorf("stack: %v",string(debug.Stack()))
//print stack
debug.PrintStack()
//return
result.NewResult(c).Error(500,"伺服器內部錯誤")
}
}()
//繼續後續介面呼叫
c.Next()
}
5,global/setting.go
package global
import (
"fmt"
"github.com/liuhongdi/digv05/pkg/setting"
"time"
)
//伺服器配置
type ServerSettingS struct {
RunMode string
HttpPort string
ReadTimeout time.Duration
WriteTimeout time.Duration
}
//資料庫配置
type DatabaseSettingS struct {
DBType string
UserName string
Password string
Host string
DBName string
Charset string
ParseTime bool
MaxIdleConns int
MaxOpenConns int
}
//日誌配置
type LogSettingS struct {
LogFilePath string //儲存到的目錄
LogInfoFileName string //info級日誌檔案的名字
LogWarnFileName string //warn級日誌檔案的名字
LogFileExt string //檔案的副檔名
}
//定義全域性變數
var (
ServerSetting *ServerSettingS
DatabaseSetting *DatabaseSettingS
LogSetting *LogSettingS
)
//讀取配置到全域性變數
func SetupSetting() error {
s, err := setting.NewSetting()
if err != nil {
return err
}
err = s.ReadSection("Database", &DatabaseSetting)
if err != nil {
return err
}
err = s.ReadSection("Server", &ServerSetting)
if err != nil {
return err
}
err = s.ReadSection("Log", &LogSetting)
if err != nil {
return err
}
fmt.Println("setting:")
fmt.Println(ServerSetting)
fmt.Println(DatabaseSetting)
fmt.Println(LogSetting)
return nil
}
6,main.go
package main
import (
"github.com/gin-gonic/gin"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/liuhongdi/digv05/global"
"github.com/liuhongdi/digv05/router"
"log"
)
//init
func init() {
//setting
err := global.SetupSetting()
if err != nil {
log.Fatalf("init.setupSetting err: %v", err)
}
//logger
err = global.SetupLogger()
if err != nil {
log.Fatalf("init.SetupLogger err: %v", err)
}
//db
err = global.SetupDBLink()
if err != nil {
log.Fatalf("init.SetupLogger err: %v", err)
global.Logger.Fatalf("init.setupDBEngine err: %v", err)
}
global.Logger.Infof("------應用init結束")
}
func main() {
global.Logger.Infof("------應用main函式開始")
//設定執行模式
gin.SetMode(global.ServerSetting.RunMode)
//引入路由
r := router.Router()
//run
r.Run(":"+global.ServerSetting.HttpPort)
}
7,其他相關程式碼請參見github
四,測試效果
1,檢視info級日誌:
啟用應用後:
[email protected]:/data/gologs/logs# more info
2020-12-16T14:37:45.666+0800 INFO digv05/main.go:32 ------應用init結束
2020-12-16T14:37:45.667+0800 INFO digv05/main.go:36 ------應用main函式開始
注意:info檔案其實是一個符號連結,指向到當前目錄下的/data/gologs/logs/info_20201216.log
[email protected]:/data/gologs/logs# ll info
lrwxrwxrwx 1 liuhongdi liuhongdi 35 12月 16 14:37 info -> /data/gologs/logs/info_20201216.log
2,檢視錯誤日誌:
訪問:
http://127.0.0.1:8000/article/getone/100
引發一次內部錯誤:我們在程式碼中故意添加了一個除0操作用來引發異常
頁面返回:
檢視錯誤日誌:
[email protected]:/data/gologs/logs# more warn
2020-12-16T14:45:37.533+0800 ERROR router/router.go:40 panic: runtime error: integer divide by zero
2020-12-16T14:45:37.533+0800 ERROR router/router.go:42 stack: goroutine 8 [running]:
runtime/debug.Stack(0xc0000112e8, 0xaf3002, 0xba2225)
/usr/local/soft/go/src/runtime/debug/stack.go:24 +0x9f
github.com/liuhongdi/digv05/router.Recover.func1(0xc0002d4000)
/data/liuhongdi/digv05/router/router.go:42 +0xdb
panic(0xaf30a0, 0x100e790)
/usr/local/soft/go/src/runtime/panic.go:969 +0x1b9
github.com/liuhongdi/digv05/controller.(*ArticleController).GetOne(0x1053de8, 0xc0002d4000)
/data/liuhongdi/digv05/controller/articleController.go:30 +0x1ff
github.com/gin-gonic/gin.(*Context).Next(0xc0002d4000)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:161 +0x3b
github.com/liuhongdi/digv05/router.Recover(0xc0002d4000)
/data/liuhongdi/digv05/router/router.go:50 +0x4e
github.com/gin-gonic/gin.(*Context).Next(0xc0002d4000)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:161 +0x3b
github.com/gin-gonic/gin.RecoveryWithWriter.func1(0xc0002d4000)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/recovery.go:83 +0x65
github.com/gin-gonic/gin.(*Context).Next(0xc0002d4000)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:161 +0x3b
github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0002d4000)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/logger.go:241 +0xe5
github.com/gin-gonic/gin.(*Context).Next(0xc0002d4000)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:161 +0x3b
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0002b8000, 0xc0002d4000)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:409 +0x67a
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0002b8000, 0xc84a20, 0xc0002b61c0, 0xc000398900)
/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:367 +0x14d
net/http.serverHandler.ServeHTTP(0xc0002b60e0, 0xc84a20, 0xc0002b61c0, 0xc000398900)
/usr/local/soft/go/src/net/http/server.go:2843 +0xa3
net/http.(*conn).serve(0xc00035d720, 0xc86e20, 0xc000396040)
/usr/local/soft/go/src/net/http/server.go:1925 +0x8ad
created by net/http.(*Server).Serve
/usr/local/soft/go/src/net/http/server.go:2969 +0x36c
...
五,檢視各庫的版本:
module github.com/liuhongdi/digv05
go 1.15
require (
github.com/gin-gonic/gin v1.6.3
github.com/go-playground/universal-translator v0.17.0
github.com/go-playground/validator/v10 v10.2.0
github.com/jinzhu/gorm v1.9.16
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
github.com/magiconair/properties v1.8.4 // indirect
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/spf13/afero v1.4.1 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.7.1
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
golang.org/x/text v0.3.4 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
)