概述

首先同步下项目概况:

上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。当系统发生异常时,提示 “系统异常,请联系管理员!”,同时并发送 panic 告警邮件。

什么是异常?

在 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
}

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

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

自定义邮件模板

如图:

这就是告警邮件的模板,还不错吧,大家还想记录什么,可以自定义去修改。

封装一个中间件

最后,封装一下。

直接上代码:

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 告警邮件。

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

到这,就结束了。

备注

  • 发邮件的地方,可以调整为异步发送。
  • 文章中仅贴了部分代码,相关代码请查阅 github。
  • 测试发邮件时,一定要配置邮箱信息。

源码地址

https://github.com/xinliangnote/go-gin-api

go-gin-api 系列文章

[系列] go-gin-api 路由中间件 - 捕获异常(四)的更多相关文章

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

    概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常. 当系统发生异常时,提示 “系统异常,请联系管理员!”,并发送 panic 告警邮件. ...

  2. [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(五)

    概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 捕获异常,这篇文章咱们分享:路由中间件 - Jaeger 链路追踪. 啥是链路追踪? 我理解链路追踪其实是为微服务架构提供服务的,当一个请求 ...

  3. [系列] go-gin-api 路由中间件 - 签名验证(七)

    目录 概览 MD5 组合 AES 对称加密 RSA 非对称加密 如何调用? 性能测试 PHP 与 Go 加密方法如何互通? 源码地址 go-gin-api 系列文章 概览 首先同步下项目概况: 上篇文 ...

  4. [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(六)

    [DOC] 概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(理论篇),这篇文章咱们接着分享:路由中间件 - Jaeger 链路追踪(实战篇). 这篇文章,确实让大家 ...

  5. [系列] - go-gin-api 路由中间件 - 日志记录(三)

    目录 概述 gin.Logger() 自定义 Logger() 源码地址 go-gin-api 系列文章 概述 首先同步下项目概况: 上篇文章分享了,规划项目目录和参数验证,其中参数验证使用的是 va ...

  6. 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)【有源码】

    原文:循序渐进学.Net Core Web Api开发系列[13]:中间件(Middleware) 系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:ht ...

  7. 【Gin-API系列】Gin中间件之鉴权访问(五)

    在完成中间件的介绍和日志中间件的代码后,我们的程序已经基本能正常跑通了,但如果要上生产,还少了一些必要的功能,例如鉴权.异常捕捉等.本章我们介绍如何编写鉴权中间件. 鉴权访问,说白了就是给用户的请求增 ...

  8. go-gin-api 路由中间件 - 签名验证(七)

    概览 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(实战篇),文章反响真是出乎意料, 「Go中国」 公众号也转发了,有很多朋友加我好友交流,直呼我大神,其实我哪是什么大 ...

  9. laravel 的路由中间件

    简介# Laravel 中间件提供了一种方便的机制来过滤进入应用的HTTP请求.例如,Laravel 内置了一个中间件来验证用户的身份认证 , 如果没有通过身份认证,中间件会将用户重定向到登陆界面,但 ...

随机推荐

  1. String关键字

    关于String和new String()见我写的前一篇博客 String和new String()的区别 1.String的"+"运算 a.String str = " ...

  2. selenium中的setUp,tearDown与setUpClass,tearDownClass的区别

    def setUpClass(cls): cls.driver = webdriver.Chrome() cls.driver.maximize_window() def setUp(self): s ...

  3. 100天搞定机器学习|Day21 Beautiful Soup

    前情回顾 机器学习100天|Day1数据预处理 100天搞定机器学习|Day2简单线性回归分析 100天搞定机器学习|Day3多元线性回归 100天搞定机器学习|Day4-6 逻辑回归 100天搞定机 ...

  4. 探究光线追踪技术及UE4的实现

    目录 一.光线追踪概述 1.1 光线追踪是什么 1.2 光线追踪的特点 1.3 光线追踪的历史 1.4 光线追踪的应用 二.光线追踪的原理 2.1 光线追踪的物理原理 2.2 光线追踪算法 2.3 R ...

  5. 3.php基础(控制语句,函数,数组遍历)

    if条件判断语句 结构一:只判断true,不管false 结构二:既判断true,也判断false(二选一) 结构三:多条件判断 switch多分支结构 Switch语法结构说明: l Switch的 ...

  6. web项目jsp中无法引入js问题

    https://blog.csdn.net/C1042135353/article/details/80274685#commentBox 这篇文章超赞的,几个小时的时间看了这篇文章豁然开朗,瞬间懂了 ...

  7. 0807 创建vue实例以及vue的基础指令

    lession1 1.Vue的了解   渐进式框架   作者:尤雨溪     mvvm 2.创建vue实例 引入<script src="vue.js"><scr ...

  8. Apache和Apache Tomcat

    Apache 和 Tomcat 都是web网络服务器,两者既有联系又有区别,在进行HTML.PHP.JSP.Perl等开发过程中,需要准确掌握其各自特点,选择最佳的服务器配置. Apache是web服 ...

  9. 2020届京东秋招正式批一面记录-Java开发-2019.08.31

    京东一面总结 总共时间持续时间约40分钟 1.你用过集合类里面哪些是线程安全的,哪些是线程不安全的?分别举两个例子? 线程安全:HashTable以及ConcurrentHashMap 非线程安全:A ...

  10. Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower)

    Leetcode之二分法专题-374. 猜数字大小(374. Guess Number Higher or Lower) 我们正在玩一个猜数字游戏. 游戏规则如下:我从 1 到 n 选择一个数字. 你 ...