[系列] go-gin-api 路由中间件 - 捕获异常(四)
概述
首先同步下项目概况:
上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。当系统发生异常时,提示 “系统异常,请联系管理员!”,同时并发送 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 路由中间件 - 捕获异常(四)的更多相关文章
- go-gin-api 路由中间件 - 捕获异常
概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常. 当系统发生异常时,提示 “系统异常,请联系管理员!”,并发送 panic 告警邮件. ...
- [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(五)
概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 捕获异常,这篇文章咱们分享:路由中间件 - Jaeger 链路追踪. 啥是链路追踪? 我理解链路追踪其实是为微服务架构提供服务的,当一个请求 ...
- [系列] go-gin-api 路由中间件 - 签名验证(七)
目录 概览 MD5 组合 AES 对称加密 RSA 非对称加密 如何调用? 性能测试 PHP 与 Go 加密方法如何互通? 源码地址 go-gin-api 系列文章 概览 首先同步下项目概况: 上篇文 ...
- [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(六)
[DOC] 概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(理论篇),这篇文章咱们接着分享:路由中间件 - Jaeger 链路追踪(实战篇). 这篇文章,确实让大家 ...
- [系列] - go-gin-api 路由中间件 - 日志记录(三)
目录 概述 gin.Logger() 自定义 Logger() 源码地址 go-gin-api 系列文章 概述 首先同步下项目概况: 上篇文章分享了,规划项目目录和参数验证,其中参数验证使用的是 va ...
- 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)【有源码】
原文:循序渐进学.Net Core Web Api开发系列[13]:中间件(Middleware) 系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:ht ...
- 【Gin-API系列】Gin中间件之鉴权访问(五)
在完成中间件的介绍和日志中间件的代码后,我们的程序已经基本能正常跑通了,但如果要上生产,还少了一些必要的功能,例如鉴权.异常捕捉等.本章我们介绍如何编写鉴权中间件. 鉴权访问,说白了就是给用户的请求增 ...
- go-gin-api 路由中间件 - 签名验证(七)
概览 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(实战篇),文章反响真是出乎意料, 「Go中国」 公众号也转发了,有很多朋友加我好友交流,直呼我大神,其实我哪是什么大 ...
- laravel 的路由中间件
简介# Laravel 中间件提供了一种方便的机制来过滤进入应用的HTTP请求.例如,Laravel 内置了一个中间件来验证用户的身份认证 , 如果没有通过身份认证,中间件会将用户重定向到登陆界面,但 ...
随机推荐
- cogs 1254. 最难的任务 Dijkstra + 重边处理
1254. 最难的任务 ★ 输入文件:hardest.in 输出文件:hardest.out 简单对比时间限制:1 s 内存限制:128 MB [题目描述] 这个真的很难.算出 123 ...
- 手写Struts,带你深入源码中心解析
个人剖析,不喜勿喷 扫码关注公众号,不定期更新干活 在此申明本博文并非原创,原文:http://blog.csdn.net/lenotang/article/details/3336623,本文章是在 ...
- JS扫雷小游戏
HTML代码 <title> 扫雷 </title> <!-- ondragstart:防拖拽生成新页面 oncontextmenu:屏蔽右键菜单--> <b ...
- Zookeeper_阅读源码第一步_在 IDE 里启动 zkServer(集群版)
上篇文章Zookeeper_阅读源码第一步_在 IDE 里启动 zkServer(单机版)讲了在 idea 里以单机的方式启动zookeeper,这篇介绍一下以集群的方式启动. 集群方式启动,才会真正 ...
- Spring Cloud与Dubbo的完美融合之手「Spring Cloud Alibaba」
很早以前,在刚开始搞Spring Cloud基础教程的时候,写过这样一篇文章:<微服务架构的基础框架选择:Spring Cloud还是Dubbo?>,可能不少读者也都看过.之后也就一直有关 ...
- 【JVM从小白学成大佬】2.Java虚拟机运行时数据区
目录 1.运行时数据区介绍 2.堆(Heap) 是否可能有两个对象共用一段内存的事故? 3.方法区(Method Area) 4.程序计数器(Program Counter Register) 5.虚 ...
- 阿里巴巴JAVA开发规范学习笔记
一.编程规约 (一)命名规约 1.类名驼峰.领域模型除外VO.BO.DTO.DO统称POJO 4.数组String[] args 8.枚举类 Enum ,其实就是特殊的常量类,构造方法强制私有 ( 二 ...
- 解放双手——相机与IMU外参的在线标定
本文作者 沈玥伶,公众号:计算机视觉life,编辑部成员 一.相机与IMU的融合 在SLAM的众多传感器解决方案中,相机与IMU的融合被认为具有很大的潜力实现低成本且高精度的定位与建图.这是因为这两个 ...
- Spring Boot 统一异常这样处理和剖析,安否?
话说异常 「欲渡黄河冰塞川,将登太行雪满天」,无论生活还是计算机世界难免发生异常,上一篇文章RESTful API 返回统一JSON数据格式 说明了统一返回的处理,这是请求一切正常的情形:这篇文章将说 ...
- Unity经典案例之:Fire Balls 多个圆环以及圆环的变速变向
版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...