文章目录[隐藏]
前言
什么是异常?
在 Go 中异常就是 panic,它是在程序运行的时候抛出的,当 panic 抛出之后,如果在程序里没有添加任何保护措施的话,控制台就会在打印出 panic 的详细情况,然后终止运行。
我们可以将 panic 分为两种:
一种是有意抛出的,比如,
1 |
panic("自定义的 panic 信息") |
一种是无意抛出的,写程序马虎造成,比如,
1 2 |
var slice = []int{1,2,3,4,5} slice[6] = 6 |
想象一下,如果在线上环境出现了 panic,命令行输出的,因为咱们无法捕获就无法定位问题呀,想想都可怕,那么问题来了,怎么捕获异常?
怎么捕获异常
当程序发生 panic 后,在 defer(延迟函数) 内部可以调用 recover 进行捕获。
不多说,直接上代码:
1 2 3 4 5 |
defer func() { if err := recover(); err != nil { fmt.Println(err) } }() |
在运行一下 “无意抛出的 panic ”,输出:
runtime error: index out of range
OK,错误捕获到了,这时我们可以进行做文章了。
做啥文章,大家应该都知道了吧:
- 获取运行时的调用栈(debug.Stack())
- 获取当时的 Request 数据
- 组装数据,进行发邮件
Gin捕获异常中间件 + 发邮件
定义邮件模板
中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
func PanicMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // 捕获异常 defer func() { if err := recover(); err != nil { // 收集DebugStack信息 DebugStack := "" for _,line := range strings.Split(string(debug.Stack()), "\n") { DebugStack += line + "<br>" } // 写邮件,替换模板的占位符 subject := fmt.Sprintf( "【重要错误】%s 项目出错了!", "你的项目名") body := strings.ReplaceAll( template.MailPanicTemplate, "{ErrorMsg}", fmt.Sprintf("%s",err)) body = strings.ReplaceAll( body, "{RequestTime}", time.Now().Format("20060102 15:04:05 ")) 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) // 发送邮件。用的"github.com/jordan-wright/email" // email.Email.SendHtml是我自己封装的 email.Email.SendHtml( []string{"123456@qq.com"}, "发送人名称", subject, body) // 记录日志。用的是zap logger log.Log.SugarLogger.Errorf( "错误信息: %s; 请求时间: %s; 请求地址:%s; 请求UA:%s; 请求IP:%s; DebugStack:%s;", err, time.Now().Format("20060102 15:04:05 "), c.Request.Method + " " + c.Request.Host + c.Request.RequestURI, c.Request.UserAgent(), c.ClientIP(), DebugStack) // 返回信息给客户端 response.Json. SetHttpStatus(http.StatusInternalServerError). SetCode(dist.MessageSystemError). Err(c) } }() c.Next() } } |