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://www.cnblogs.com/it-3327/p/11828151.html

时间: 2024-07-31 16:44:50

go-gin-api 路由中间件 - 捕获异常的相关文章

[系列] go-gin-api 路由中间件 - 捕获异常(四)

目录 概述 什么是异常? 怎么捕获异常? 封装发邮件方法 自定义邮件模板 封装一个中间件 备注 源码地址 go-gin-api 系列文章 概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常.当系统发生异常时,提示 "系统异常,请联系管理员!",同时并发送 panic 告警邮件. 什么是异常? 在 Go 中异常就是 panic,它是在程序运行的时候抛出的,当 panic 抛出之后,如果在程序里没有添加任何保护措施的话,控制台就

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

概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 捕获异常,这篇文章咱们分享:路由中间件 - Jaeger 链路追踪. 啥是链路追踪? 我理解链路追踪其实是为微服务架构提供服务的,当一个请求中,请求了多个服务单元,如果请求出现了错误或异常,很难去定位是哪个服务出了问题,这时就需要链路追踪. 咱们先看一张图: 这张图的调用链还比较清晰,咱们想象一下,随着服务的越来越多,服务与服务之间调用关系也越来越多,可能就会发展成下图的情况. 这调用关系真的是... 看到这,我的内心是崩溃的. 那么问

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

概览 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(实战篇),文章反响真是出乎意料, 「Go中国」 公众号也转发了,有很多朋友加我好友交流,直呼我大神,其实我哪是什么大神,只不过在本地实践了而已,对于 Go 语言的使用,我还是个新人,在这里感谢大家的厚爱!这篇文章咱们分享:路由中间件 - 签名验证. 为什么使用签名验证? 这个就不用多说了吧,主要是为了保证接口安全和识别调用方身份,基于这两点,咱们一起设计下签名. 调用方需要申请 App Key 和 App Sec

go-gin-api 路由中间件 - 日志记录

概述 首先同步下项目概况: 上篇文章分享了,规划项目目录和参数验证,其中参数验证使用的是 validator.v8 版本,现已更新到 validator.v9 版本,最新代码查看 github 即可. 这篇文章咱们分享:路由中间件 - 日志记录. 日志是特别重要的一个东西,方便我们对问题进行排查,这篇文章我们实现将日志记录到文本文件中. 这是我规划的,需要记录的参数: - request 请求数据 - request_time - request_method - request_uri - r

ASP.NET Web API 路由 (上)

1 什什么是ASP.NET Web API 路由 ASP.NET Web路由其实就是一个抽象的消息处理管道,ASP.NET Web API的路由机制不同于ASP.NET的路由机制,但是与ASP.NET的路由有着相似的一套设计. 2 ASP.NET Web API 请求和响应的相关对象 ASP.NET Web API的请求是通过HtppRequestMessage作为管道来处理请求的消息,通过HtppReponseMessage作为管道来处理响应的消息.也就是ASP.NET Web API处理用户

Asp.Net Web API 2第六课——Web API路由和动作选择

Asp.Net Web API 导航 Asp.Net Web API第一课——入门http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web API第二课——CRUD操作http://www.cnblogs.com/aehyok/p/3434578.html Asp.Net Web API第三课——.NET客户端调用Web API http://www.cnblogs.com/aehyok/p/3439698.html Asp.Net Web

Web API路由与动作(三)

本章包括三个小节  如果你输入了mvc的路由规则 这个可以粗略过一遍即可  内容说明有点繁琐 原文地址:http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api 3.1ASP.NET Web API中的路由 本节描述ASP.NET Web API如何将HTTP请求路由到控制器 如果你熟悉ASP.NET MVC,Web API路由与MVC路由十分类似.主要差别是Web API使

ASP.NET Web API 路由对象介绍

ASP.NET Web API 路由对象介绍 前言 在ASP.NET.ASP.NET MVC和ASP.NET Web API这些框架中都会发现有路由的身影,它们的原理都差不多,只不过在不同的环境下作了一些微小的修改,这也是根据每个框架的特性来制定的,今天我们就来看一看路由的结构,虽然我在MVC系列里写过路由的篇幅不过在这里是Web API 路由对象介绍. ASP.NET Web API路由.管道 ASP.NET Web API 开篇介绍示例 ASP.NET Web API 路由对象介绍 ASP.

ASP.NET Web API路由系统:路由系统的几个核心类型

虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分(定义在程序集System.Web.Http.dll中)已经移除了对System.Web.dll程序集的依赖,实现在ASP.NET Web API框架中的URL路由系统亦是如此.也就是说,ASP.NET Web API核心框架的URL路由系统与ASP.NET本身的路由系统是相对独立的.但是当我们采用基于Web Host的方式(定义在程序集System.Web.H