go语言web开发系列之五:gin用zap+file-rotatelogs实现日志记录及按日期切分日志

一,安装需要用到的库:

1,安装zap日志库:

liuhongdi@ku:/data/liuhongdi/zaplog$ go get -u go.uber.org/zap

2,安装go-file-rotatelogs库

liuhongdi@ku:/data/liuhongdi/zaplog2$ go get -u github.com/lestrrat/go-file-rotatelogs

说明:刘宏缔的go森林是一个专注golang的博客,
 网站:https://blog.imgtouch.com
原文: go语言web开发系列之五:gin用zap+file-rotatelogs实现日志记录及按日期切分日志 – 架构森林

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,演示项目的相关信息

1,项目地址:

GitHub - liuhongdi/digv05: gin+zap+file-rotatelogs实现日志记录及按日期切分日志

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级日志:

启用应用后:

root@ku:/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

root@ku:/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操作用来引发异常

页面返回:

查看错误日志:

root@ku:/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/gin@v1.6.3/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/gin@v1.6.3/context.go:161 +0x3b
github.com/gin-gonic/gin.RecoveryWithWriter.func1(0xc0002d4000)
	/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/recovery.go:83 +0x65
github.com/gin-gonic/gin.(*Context).Next(0xc0002d4000)
	/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b
github.com/gin-gonic/gin.LoggerWithConfig.func1(0xc0002d4000)
	/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/logger.go:241 +0xe5
github.com/gin-gonic/gin.(*Context).Next(0xc0002d4000)
	/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/context.go:161 +0x3b
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0002b8000, 0xc0002d4000)
	/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/gin.go:409 +0x67a
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0002b8000, 0xc84a20, 0xc0002b61c0, 0xc000398900)
	/home/liuhongdi/go/pkg/mod/github.com/gin-gonic/gin@v1.6.3/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
)

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老刘你真牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值