栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

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

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

背景

最近调整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("==> %sn", 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的封装调用而已

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/907293.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号