go异常处理&错误堆栈获取

go异常处理&错误堆栈获取,第1张

背景

最近调整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排查

优化思路

和其他语言一样, 获取到错误的堆栈信息, 输出到日志

优化

于是, 找到以下两种方法

方法1
package 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的封装调用而已

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/995255.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存