Skip to content

Instantly share code, notes, and snippets.

@alsritter
Created December 2, 2021 09:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alsritter/33524c0f41777c327efd97d79c8f0692 to your computer and use it in GitHub Desktop.
Save alsritter/33524c0f41777c327efd97d79c8f0692 to your computer and use it in GitHub Desktop.
Go 模拟 try-catch
// error/custom_error.go
package my_error
import (
"encoding/json"
"fmt"
"runtime"
"strings"
)
// StError 定义一个标准的错误接口
type StError interface {
Error() string
GetMsg() string
GetLevel() int
}
const (
LevelFatal = 1
LevelWarn = 2
skipLogCaller = 2
skipNewTradeErrCaller = 3
)
// levelMap 错误等级日志字符串map
//"level": levelMap[e.level]
var levelMap = map[int]string{
LevelFatal: "Fatal",
LevelWarn: "Warn",
}
func NewFatalErr(msg string, originErrList ...error) StError {
err := newStError(LevelFatal, msg, originErrList...)
err.genCallerInfo(skipNewTradeErrCaller)
return err
}
func NewWarnErr(msg string, originErrList ...error) StError {
err := newStError(LevelWarn, msg, originErrList...)
err.genCallerInfo(skipNewTradeErrCaller)
return err
}
func newStError(level int, msg string, originErrList ...error) *customError {
tradeErr := &customError{
level: level,
msg: msg,
}
tradeErr.initLogMsgAndOriginErr(originErrList...)
return tradeErr
}
// initLogMsgAndOriginErr 初始化 msg 和 OriginErr
func (e *customError) initLogMsgAndOriginErr(originErrList ...error) {
// 把传入的异常添加进 OriginErr 异常链
for _, originErr := range originErrList {
err, ok := originErr.(*customError)
if ok {
e.originErrS = append(e.originErrS, err)
} else {
e.msg += originErr.Error()
}
}
}
type customError struct {
level int
msg string
callerLogMsgS []string //[]callerLogMsg
callerStackS []string //[]funcName
callerStackMap map[string]string //map[funcName]logMsg
originErrS []*customError
}
func (e *customError) GetMsg() string {
return e.msg
}
func (e *customError) GetLevel() int {
return e.level
}
func (e *customError) Error() string {
// 取得当前方法都上一层方法名以及该方法的位置(log)
funName, logStr := getCallerFuncNameAndLogStr(skipLogCaller)
// 根据这个方法名生成调用链日志
e.genCallerLogStr(funName, logStr)
list := e.getLogStrMapList()
bytes, _ := json.Marshal(list)
return string(bytes)
}
// genCallerInfo 生成调用栈的相关信息属性填充 callerStackMap 和 callerStackS
func (e *customError) genCallerInfo(skip int) {
if len(e.originErrS) == 0 {
e.callerStackMap = make(map[string]string)
// 取出调用栈50个数据
pcSlice := make([]uintptr, 50)
count := runtime.Callers(skip, pcSlice)
pcSlice = pcSlice[:count]
frames := runtime.CallersFrames(pcSlice)
var frame runtime.Frame
more := count > 0
for more {
frame, more = frames.Next()
if frame.Function == "RecoverToError" || frame.Function == "PanicError" { // 跳过
continue
}
e.callerStackS = append(e.callerStackS, frame.Function)
e.callerStackMap[frame.Function] = formatCallerLogStr(frame.Function, frame.Line)
}
}
}
// getLogStrMapList 递归获取 logStrMap
func (e *customError) getLogStrMapList() []map[string]interface{} {
var logMapList []map[string]interface{}
logMapList = append(logMapList, e.getLogStrMap())
for _, originErr := range e.originErrS {
list := originErr.getLogStrMapList()
logMapList = append(logMapList, list...)
}
return logMapList
}
// getLogStrMap 取得当前层的调用信息
func (e *customError) getLogStrMap() map[string]interface{} {
caller := ""
for _, name := range e.callerLogMsgS {
if strings.Contains(name, "RecoverToError") {
continue
}
caller += name + " | "
}
return map[string]interface{}{
"level": levelMap[e.level],
"caller": caller,
"msg": e.msg,
}
}
// genCallerLogStr 递归把 callerStackS 里面的调用链添加到 callerLogMsgS 里面(因为 originErrS 是一个链)
func (e *customError) genCallerLogStr(endFuncName, endLogStr string) {
if endFuncName == "" || endLogStr == "" {
return
}
// 检查调用栈是否还存在信息
if len(e.callerStackS) != 0 && len(e.callerStackMap) != 0 {
var callerLogJoinSlice, endCallerFuncSlice []string
// 找到产生异常的那个方法的下标
for index, funcName := range e.callerStackS {
if funcName == endFuncName {
// 取得异常方法后面的调用方法
endCallerFuncSlice = e.callerStackS[:index]
break
}
}
// 把异常方法对应的日志添加进来
if len(endCallerFuncSlice) >= 0 {
callerLogJoinSlice = append(callerLogJoinSlice, endLogStr)
for i := len(endCallerFuncSlice) - 1; i >= 0; i-- {
callerLogJoinSlice = append(callerLogJoinSlice, e.callerStackMap[endCallerFuncSlice[i]])
}
// 设置 callerLogMsgS 为新的调用链
e.callerLogMsgS = callerLogJoinSlice
}
}
for _, err := range e.originErrS {
err.genCallerLogStr(endFuncName, endLogStr)
}
}
// getCallerFuncNameAndLogStr 获取调用者的方法名和格式化的日志字符串
func getCallerFuncNameAndLogStr(skip int) (funcName, logStr string) {
pc, _, line, ok := runtime.Caller(skip) // skip 是打印上一层调用的函数
if !ok {
return "", ""
}
funcName = runtime.FuncForPC(pc).Name()
return funcName, formatCallerLogStr(funcName, line)
}
// formatCallerLogStr 格式化调用链日志字符串
func formatCallerLogStr(funcName string, line int) string {
return fmt.Sprintf("%s: %d ", funcName, line)
}
// error/handler_error.go
package my_error
import "fmt"
// RecoverToError 捕获执行 f 方法的异常并转成 StError(类似 Java 中的 catch 的过程)
func RecoverToError(f func()) (retErr StError) { // 这里通过具名返回值的方式传递这个 retErr
defer func() {
panicErr := recover()
if panicErr != nil {
if err, ok := panicErr.(StError); ok {
retErr = err
} else { // 非 StError 类型的错误
retErr = NewFatalErr(fmt.Sprint(panicErr))
}
}
}()
f()
return nil
}
// PanicError 向上抛异常(类似 Java 中的 throw 的过程)
func PanicError(err StError) {
if err != nil {
panic(err)
}
}
package main
import (
"fmt"
my_error "sterror/error"
)
func Pass() {
if true { // 模拟因为什么原因要抛出异常
my_error.PanicError(my_error.NewFatalErr("系统异常")) // 向上抛异常
}
}
func Pass02() {
if err := my_error.RecoverToError(Pass); err != nil {
my_error.PanicError(my_error.NewFatalErr("包装第一层", err)) // 继续向上抛异常
}
}
func Pass03() {
if err := my_error.RecoverToError(Pass02); err != nil {
my_error.PanicError(my_error.NewFatalErr("包装第二层", err))
}
}
func Pass04() {
if err := my_error.RecoverToError(Pass03); err != nil {
my_error.PanicError(my_error.NewFatalErr("包装第三层", err))
}
}
func Pass05() {
if err := my_error.RecoverToError(Pass04); err != nil {
my_error.PanicError(err)
}
}
func Pass06() {
if err := my_error.RecoverToError(Pass05); err != nil {
my_error.PanicError(err)
}
}
func Pass07() {
if err := my_error.RecoverToError(Pass06); err != nil {
fmt.Println(err.Error()) // 最终打印错误(职责链的最顶层)
}
}
func main() {
Pass07()
}
@linkerlin
Copy link

看起来不错,是不是改成一个项目,更容易让更多的人用?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment