[Series] - go-gin-api routing Middleware - catch exceptions

Posted by mcubedma on Wed, 11 Sep 2019 07:04:23 +0200

Summary

Firstly, the project overview is synchronized:

Last article shared, routing Middleware - logging, this article we share: routing Middleware - catch exceptions. When the system is abnormal, prompt "system abnormal, please contact the administrator!" At the same time, panic alert mail is sent.

What is an exception?

The exception in Go is panic, which is thrown when the program runs. When panic is thrown, if no protection measures are added to the program, the console prints out the details of panic and terminates the operation.

We can divide panic into two kinds:

One is deliberately thrown, for example,

panic("Self defined panic information")

Output:

2019/09/10 20:25:27 http: panic serving [::1]:61547: Self defined panic information
goroutine 8 [running]:
...

One is thrown unintentionally, resulting from sloppy programming, such as,

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

slice[6] = 6

Output:

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

Imagine if there is panic in the online environment and command line output, because we can't capture it, we can't locate the problem. It's terrible to think about it. Then the problem comes. How can we catch the exception?

How to catch exceptions?

When the program panic occurs, recover y can be invoked inside defer (defer function) for capture.

Not to mention much, go directly to the code:

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

Run "unintentionally thrown panic" and output:

runtime error: index out of range

OK, the error is caught, and then we can make an article.

Everyone knows what to do.

  • Get the call stack at runtime (debug.Stack())
  • Get the Request data at that time
  • Assemble data and send email

So, how does Go send e-mail? Is there an open source package?

Certainly. Look down, please.

Encapsulation and email method

Use packages: gopkg.in/gomail.v2

Code directly:

func SendMail(mailTo string, subject string, body string) error {
    
    if config.ErrorNotifyOpen != 1 {
        return nil
    }

    m := gomail.NewMessage()

    //Setting up sender
    m.SetHeader("From", config.SystemEmailUser)

    //Settings sent to multiple users
    mailArrTo := strings.Split(mailTo, ",")
    m.SetHeader("To", mailArrTo...)

    //Setting Mail Theme
    m.SetHeader("Subject", subject)

    //Setting Mail Text
    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
}

I've added a switch here. You can turn it on and off at will.

Now you can send mail, and the whole mail template will be perfect.

Custom mail template

As shown in the picture:

This is the alarm mail template, not bad, you want to record what you can customize to modify.

Encapsulating a middleware

Finally, encapsulate it.

Code directly:

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("[Important mistakes]%s The project is wrong!", 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, "System exception, please contact the administrator!", nil)
            }
        }()
        c.Next()
    }
}

When a panic exception occurs, the output:

{
    "code": 500,
    "msg": "System exception, please contact the administrator!",
    "data": null
}

At the same time, a panic alert email will be received.

For screenshots, DebugStack deletes some information.

This is the end.

Remarks

  • The place where the mail is sent can be adjusted to asynchronous.
  • Only part of the code has been posted in the article. Please refer to github for the relevant code.
  • When testing email, be sure to configure mailbox information.

Source address

https://github.com/xinliangno...

go-gin-api series of articles

Topics: Go github Programming