最近调整gin项目框架的时候, 想起, 框架的异常处理还没完善, 目前只是把简单的error信息打印到日志里
优化 优化前package middleware
import (
"net/http"
"github.com/gin-gonic/gin"
)
func HandleException() gin.HandlerFunc {
return func(ctx *gin.Context) {
defer func() {
if err := recover(); err != nil {
// 这里用打印输出做示例
fmt.Println(err)
// 接口正常返回, 不把代码的错误信息抛给用户
ctx.JSON(http.StatusOK, gin.H{
"code": -1,
"message": "出错了"
})
return
}
}()
ctx.Next()
}
}
假设在服务内有这么一段代码
zero := 0
fmt.Println(1 / zero)
上述中间件捕获并输出的错误如下:
runtime error: integer divide by zero
并不利于问题快速定位和bug排查
优化思路和其他语言一样, 获取到错误的堆栈信息, 输出到日志
优化于是, 找到以下两种方法
方法1package main
import (
"fmt"
"runtime"
)
func main() {
defer HandleException()
num := 0
fmt.Println(1 / num)
}
func HandleException() {
errs := recover()
if errs == nil {
return
}
var stackBuf [1024]byte
stackBufLen := runtime.Stack(stackBuf[:], false)
fmt.Printf("==> %s\n", string(stackBuf[:stackBufLen]))
}
结果如下:
==> goroutine 1 [running]:
main.HandleException()
D:/go/exceptionHandler.go:20 +0x4e
panic({0xef8d60, 0xf9cf50})
D:/Programs/Go/src/runtime/panic.go:1038 +0x215
main.main()
D:/go/exceptionHandler.go:11 +0x3b
方法2
package main
import (
"fmt"
"runtime"
)
func main() {
defer HandleException()
num := 0
fmt.Println(1 / num)
}
func HandleException() {
errs := recover()
if errs == nil {
return
}
fmt.Println(string(debug.Stack()))
}
结果如下:
==> goroutine 1 [running]:
runtime/debug.Stack()
D:/Programs/Go/src/runtime/debug/stack.go:24 +0x65
main.HandleException()
D:/go/exceptionHandler.go:19 +0x4e
panic({0xef8d60, 0xf9cf50})
D:/Programs/Go/src/runtime/panic.go:1038 +0x215
main.main()
D:/go/exceptionHandler.go:11 +0x3b
总结
都能输出堆栈信息, 但没输出错误信息, 故还需把原来的error输出, 即:
fmt.Println(err)
源码分析
上述两种方法的核心代码如下:
//方法1
var stackBuf [1024]byte
stackBufLen := runtime.Stack(stackBuf[:], false)
stackStr := string(stackBuf[:stackBufLen])
//方法2
stackStr := string(debug.Stack())
查看debug.Stack源码, 如下:
// Stack returns a formatted stack trace of the goroutine that calls it.
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
func Stack() []byte {
buf := make([]byte, 1024)
for {
n := runtime.Stack(buf, false)
if n < len(buf) {
return buf[:n]
}
buf = make([]byte, 2*len(buf))
}
}
也就是, 方法2其实是对方法1的封装调用而已
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)