前言
这是我参与更文挑战的第3天,大家好,我是作曲家种太阳
上一篇讲了使用viper解析yaml文件,这一篇我们来做路由初始化和Zap日志管理
日志管理在服务端必不可少,运维排故,debug分析都得用日志管理
1. 介绍
一个非常快的功能强大日志管理模块
Zap官网
需求分析(了解需求更容易理清楚思路)
路由:
- 路由分组
- 分流到controll层
- 添加zap中间件
日志:
- 把日志按照每天和日志等级输出到指定位置
- 在控制台可以看到
- 获取到http请响应的存放在日志(GinLogger中间件)
- 服务器宕机时可以把记录异常信息(GinRecovery中间件)
2. 安装
go get -u go.uber.org/zap
3.初始化路由
(1). router/user中编写
package router
import (
"github.com/gin-gonic/gin"
)
func UserRouter(Router *gin.RouterGroup) {
UserRouter := Router.Group("user")
{
UserRouter.GET("list", func(context *gin.Context) {
context.JSON(200, gin.H{
"message": "pong",
})
})
}
}
(2). 在initialize/router中编写
package initialize
import (
"github.com/gin-gonic/gin"
"go_gin/middlewares"
"go_gin/router"
)
func Routers() *gin.Engine {
Router := gin.Default()
// 路由分组
ApiGroup := Router.Group("/v1/")
router.UserRouter(ApiGroup)
return Router
}
ps:路由分组可以固定请求的前缀
(3). 在main.go中添加
package main
import (
"fmt"
"github.com/fatih/color"
"go.uber.org/zap"
"go_gin/global"
"go_gin/initialize"
"go_gin/utils"
)
func main() {
//1.初始化yaml配置
initialize.InitConfig()
//2. 初始化routers
Router := initialize.Routers()
color.Cyan("go-gin服务开始了")
//启动gin,并配置端口,global.Settings.Port是yaml配置过的
err := Router.Run(fmt.Sprintf(":%d", global.Settings.Port))
if err != nil {
zap.L().Info("this is hello func", zap.String("error", "启动错误!"))
}
}
ps: main.go中初始化routers(Router := initialize.Routers())
验证下路由配置是否成功
当你重启服务的时候,没有报错,说明以上环节都没有问题,可以继续啦~
4.初始化logger
(1.)在 initialize/logger中编写
package initialize
import (
"fmt"
"github.com/fatih/color"
"go.uber.org/zap"
"go_gin/global"
"go_gin/utils"
)
// InitLogger 初始化Logger
func InitLogger() {
// 实例化zap 配置
cfg := zap.NewDevelopmentConfig()
// 注意global.Settings.LogsAddress是在settings-dev.yaml配置过的
// 配置日志的输出地址
cfg.OutputPaths = []string{
fmt.Sprintf("%slog_%s.log", global.Settings.LogsAddress, utils.GetNowFormatTodayTime()), //
"stdout",
}
// 创建logger实例
logg, _ := cfg.Build()
zap.ReplaceGlobals(logg) // 替换zap包中全局的logger实例,后续在其他包中只需使用zap.L()调用即可
global.Lg = logg // 注册到全局变量中
}
ps:GetNowFormatTodayTime这个函数是我在utils下获取当天年月日的函数
(2.)编写日期函数(GetNowFormatTodayTime)
package utils
import (
"fmt"
"time"
)
func GetNowFormatTodayTime() string {
now := time.Now()
dateStr := fmt.Sprintf("%02d-%02d-%02d", now.Year(), int(now.Month()),
now.Day())
return dateStr
}
(3).在全局变量中添加
在global/globalVar.go中加入
var (
Lg *zap.Logger)
)
5.简单了解下中间件
根据我们的需求分析,日志管理涉及http请求异常的存档,所以我们得考虑使用中间件
我们在中间件做一层拦截,吧resonse异常的记录下来
我还发现了一个好文章,感兴趣的可以顺着学下大概怎么使用
juejin.cn/post/684490…
6.使用中间件logger记录http异常
package middlewares
import (
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// GinLogger 接收gin框架默认的日志
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)
// 若response的状态码不是200为异常
if c.Writer.Status() != 200 {
// 记录异常信息
zap.L().Info(path,
zap.Int("statusxixixixi", 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),
)
}
}
}
6.使用中间件记录宕机异常信息
// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
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 {
global.Lg.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()
}
}
7. main.go初始化Zap和中间件添加
main.go中使用InitLogger初始化和GinLogger中间件 \
(1).在main.go中加入
// 3.初始化日志信息
initialize.InitLogger()
(2).在initialize/router中加入GinLogger,GinRecovery中间件
Router.Use(middlewares.GinLogger(), middlewares.GinRecovery(true))
ps:使用Use是gin添加中间件函数
最后-验证结果环节
(1).启动 main.go
(2).输入网址: 127.0.0.1:8021/v1/user/list 返回结果是: 这一步说明你的初始化路由是成功的,但是日志打印功能还没有测试
(3) 输入一个不存在url,看看./log/下面的文件有没有输出日志
(4).调用
zap.L().Info("this is hello func", zap.String("test", "test--------->"))
看日志是否打印 这一步说明你的初始化路由,日志打印功能是成功的,继续下一篇的征程吧~
如果这系列的文章对你有有用,请点赞和留言吧~