Go gin API routing Middleware - catch exceptions

Summary

First, synchronize the following project overview:


Last article shared, routing Middleware - logging, this article we share: routing Middleware - catch exceptions.

When there is an exception in the system, it will prompt "system exception, please contact the administrator!" And send the panic alarm email.

What is anomaly?

The exception in Go is panic, which is thrown when the program is running. After the panic is thrown, if no protection measures are added to the program, the console will print out the details of the panic and then terminate the operation.

We can divide panic into two types:

One is intentionally thrown, for example,

panic("customized panic information")

Output:

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

One is unintentionally thrown, which is caused by careless program writing, for example,

    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 that if there is panic in the online environment, and the command line output, because we can't capture it, we can't locate it. It's terrible to think about it. How can we capture the exception?

How to catch exceptions?

When a panic occurs in a program, recover can be called inside defer to capture it.

Not much to say, go straight 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. Now we can do the article.

You should know what to do:

Get runtime call stack (debug.Stack())

Get the Request data at that time

Assemble data and send emails

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

Sure, please look down.

Encapsulate email method

Use package: gopkg.in/gomail.v2

Direct code:

    func SendMail(mailTo string, subject string, body string) error {    
        if config.ErrorNotifyOpen != 1 {    
            return nil    
        }    
        m := gomail.NewMessage()    
        //Set sender    
        m.SetHeader("From", config.SystemEmailUser)    
        //Settings send to multiple users    
        mailArrTo := strings.Split(mailTo, ",")    
        m.SetHeader("To", mailArrTo...)    
        //Set message subject    
        m.SetHeader("Subject", subject)    
        //Set message body    
        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 or off at will.

Now you can send email, and the whole email template is perfect.

Custom mail template

As shown in the picture:

This is the template of alarm email. It's not bad. What else do you want to record? You can customize it.

Encapsulate a middleware

Finally, wrap it up.

Direct code:

 

  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 errors]%s Project error!", 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 administrator!", nil)    
                }    
            }()    
            c.Next()    
        }    
    }

 



When a panic exception occurs, the output is:

 

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

 


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



For screenshots, DebugStack prunes some information.

It's over here.

Remarks

The place where you send mail can be adjusted to asynchronous sending.

Only part of the code is pasted in the article. Please refer to github for relevant code.

Be sure to configure the mailbox information when testing outgoing mail.

Keywords: Go github

Added by djg_uk on Sat, 09 Nov 2019 16:43:57 +0200