orString{text}
}
那我们怎么捕获到呢?
使用中间件进行捕获,写一个 recover
中间件。
package recover
import (
"fmt"
"ginDemo/common/alarm"
"github.com/gin-gonic/gin"
)
func Recover() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
alarm.Panic(fmt.Sprintf("%s", r))
}
}()
c.Next()
}
}
路由调用中间件:
r.Use(logger.LoggerToFile(), recover.Recover())
//Use 可以传递多个中间件。
验证下吧,咱们先抛出两个异常,看看能否捕获到?
还是修改 product.go 这个文件吧。
有意抛出 panic:
package v1
import (
"fmt"
"ginDemo/entity"
"github.com/gin-gonic/gin"
"net/http"
)
func AddProduct(c *gin.Context) {
// 获取 Get 参数
name := c.Query("name")
var res = entity.Result{}
str, err := hello(name)
if err != nil {
res.SetCode(entity.CODE_ERROR)
res.SetMessage(err.Error())
c.JSON(http.StatusOK, res)
c.Abort()
return
}
res.SetCode(entity.CODE_SUCCESS)
res.SetMessage(str)
c.JSON(http.StatusOK, res)
}
func hello(name string) (str string, err error) {
if name == "" {
// 有意抛出 panic
panic("i am panic")
return
}
str = fmt.Sprintf("hello: %s", name)
return
}
访问:http://localhost:8080/v1/product/add
界面是空白的。
抛出了异常,输出信息如下:
{"time":"2019-07-23 22:42:37","alarm":"PANIC","message":"i am panic","filename":"绝对路径/ginDemo/middleware/recover/recover.go","line":13,"funcname":"1"}
很显然,定位的文件名、方法名、行号不是我们想要的。
需要调整 runtime.Caller(2)
,这个代码在 alarm.go 的 alarm
方法中。
将 2 调整成 4 ,看下输出信息:
{"time":"2019-07-23 22:45:24","alarm":"PANIC","message":"i am panic","filename":"绝对路径/ginDemo/router/v1/product.go","line":33,"funcname":"hello"}
这就对了。
无意抛出 panic:
// 上面代码不变
func hello(name string) (str string, err error) {
if name == "" {
// 无意抛出 panic
var slice = [] int {1, 2, 3, 4, 5}
slice[6] = 6
return
}
str = fmt.Sprintf("hello: %s", name)
return
}
访问:http://localhost:8080/v1/product/add
界面是空白的。
抛出了异常,输出信息如下:
{"time":"2019-07-23 22:50:06","alarm":"PANIC","message":"runtime error: index out of range","filename":"绝对路径/runtime/panic.go","line":44,"funcname":"panicindex"}
很显然,定位的文件名、方法名、行号也不是我们想要的。
将 4 调整成 5 ,看下输出信息:
{"time":"2019-07-23 22:55:27","alarm":"PANIC","message":"runtime error: index out of range","filename":"绝对路径/ginDemo/router/v1/product.go","line":34,"funcname":"hello"}
这就对了。
奇怪了,这是为什么?
在这里,有必要说下 runtime.Caller(skip)
了。
skip 指的调用的深度。
为 0 时,打印当前调用文件及行数。
为 1 时,打印上级调用的文件及行数。
依次类推...
在这块,调用的时候需要注意下,我现在还没有好的解决方案。
我是将 skip(调用深度),当一个参数传递进去。
比如:
// 发微信
func WeChat (text string) error {
alarm("WX", text, 2)
return &errorString{text}
}
// Panic 异常
func Panic (text string) error {
alarm("PANIC", text, 5)
return &errorString{text}
}
具体的代码就不贴了。
但是,有意抛出 Panic 和 无意抛出 Panic 的调用深度又不同,怎么办?
1、尽量将有意抛出的 Panic 改成抛出错误的方式。
2、想其他