欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

go-gin-api 路由中间件 - 捕获异常

程序员文章站 2022-05-04 12:28:24
概述首先同步下项目概况:上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。当系统发生异常时,提示 “系统异常,请联系管理员!”,并发送 panic 告警邮件。什么是异常?在 Go 中异常就是 panic,它是在程序运行的时候抛出的,当 panic 抛出之后,如果在 ......

概述

首先同步下项目概况:
go-gin-api 路由中间件 - 捕获异常

上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。

当系统发生异常时,提示 “系统异常,请联系管理员!”,并发送 panic 告警邮件。
go-gin-api 路由中间件 - 捕获异常
什么是异常?

在 go 中异常就是 panic,它是在程序运行的时候抛出的,当 panic 抛出之后,如果在程序里没有添加任何保护措施的话,控制台就会在打印出 panic 的详细情况,然后终止运行。

我们可以将 panic 分为两种:

一种是有意抛出的,比如,

panic("自定义的 panic 信息")

输出:

    2019/09/10 20:25:27 http: panic serving [::1]:61547: 自定义的 panic 信息    
    goroutine 8 [running]:    
    ...

一种是无意抛出的,写程序马虎造成,比如,

    var slice = [] int {1, 2, 3, 4, 5}    
    slice[6] = 6

输出:

    2019/09/10 15:27:05 http: panic serving [::1]:61616: runtime error: index out of range    
    goroutine 6 [running]:    
    ...

 



想象一下,如果在线上环境出现了 panic,命令行输出的,因为咱们无法捕获就无法定位问题呀,想想都可怕,那么问题来了,怎么捕获异常?

怎么捕获异常?

当程序发生 panic 后,在 defer(延迟函数) 内部可以调用 recover 进行捕获。

不多说,直接上代码:

    defer func() {    
        if err := recover(); err != nil {    
            fmt.println(err)    
        }    
    }()

 



在运行一下 “无意抛出的 panic ”,输出:

runtime error: index out of range

ok,错误捕获到了,这时我们可以进行做文章了。

做啥文章,大家应该都知道了吧:

    获取运行时的调用栈(debug.stack())

    获取当时的 request 数据

    组装数据,进行发邮件

那么,go 怎么发邮件呀,有没有开源包呀?

当然有,请往下看。

封装发邮件方法

使用包:gopkg.in/gomail.v2

直接上代码:

    func sendmail(mailto string, subject string, body string) error {    
        if config.errornotifyopen != 1 {    
            return nil    
        }    
        m := gomail.newmessage()    
        //设置发件人    
        m.setheader("from", config.systememailuser)    
        //设置发送给多个用户    
        mailarrto := strings.split(mailto, ",")    
        m.setheader("to", mailarrto...)    
        //设置邮件主题    
        m.setheader("subject", subject)    
        //设置邮件正文    
        m.setbody("text/html", body)    
        d := gomail.newdialer(config.systememailhost, config.systememailport, config.systememailuser, config.systememailpass)    
        err := d.dialandsend(m)    
        if err != nil {    
            fmt.println(err)    
        }    
        return err    
    }

 



在这块我加了一个开关,想开想关,您随意。

现在会发送邮件了,再整个邮件模板就完美了。

自定义邮件模板

如图:
go-gin-api 路由中间件 - 捕获异常
这就是告警邮件的模板,还不错吧,大家还想记录什么,可以自定义去修改。

封装一个中间件

最后,封装一下。

直接上代码:

 

  func setup() gin.handlerfunc {    
        return func(c *gin.context) {    
            defer func() {    
                if err := recover(); err != nil {    
                    debugstack := ""    
                    for _, v := range strings.split(string(debug.stack()), "\n") {    
                        debugstack += v + "<br>"    
                    }    
                    subject := fmt.sprintf("【重要错误】%s 项目出错了!", config.appname)    
                    body := strings.replaceall(mailtemplate, "{errormsg}", fmt.sprintf("%s", err))    
                    body  = strings.replaceall(body, "{requesttime}", util.getcurrentdate())    
                    body  = strings.replaceall(body, "{requesturl}", c.request.method + "  " + c.request.host + c.request.requesturi)    
                    body  = strings.replaceall(body, "{requestua}", c.request.useragent())    
                    body  = strings.replaceall(body, "{requestip}", c.clientip())    
                    body  = strings.replaceall(body, "{debugstack}", debugstack)    
                    _ = util.sendmail(config.errornotifyuser, subject, body)    
                    utilgin := util.gin{ctx: c}    
                    utilgin.response(500, "系统异常,请联系管理员!", nil)    
                }    
            }()    
            c.next()    
        }    
    }

 



当发生 panic 异常时,输出:

 

   {    
        "code": 500,    
        "msg": "系统异常,请联系管理员!",    
        "data": null    
    }

 


同时,还会收到一封 panic 告警邮件。

go-gin-api 路由中间件 - 捕获异常

便于截图,debugstack 删减了一些信息。

到这,就结束了。
go-gin-api 路由中间件 - 捕获异常
备注

    发邮件的地方,可以调整为异步发送。

    文章中仅贴了部分代码,相关代码请查阅 github。

    测试发邮件时,一定要配置邮箱信息。