1. 程式人生 > 其它 >go語言web開發系列之五:gin用zap+file-rotatelogs實現日誌記錄及按日期切分日誌

go語言web開發系列之五:gin用zap+file-rotatelogs實現日誌記錄及按日期切分日誌

技術標籤:用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
)