共计 4111 个字符,预计需要花费 11 分钟才能阅读完成。
介绍不同语言的错误处理机制:
Error handling patterns
Musings about error handling mechanisms in programming languages
我的项目中 main 调 func1,func1 调取 func2…
这样就会呈现很多的 if err != nil {log.Printf()}
, 在 Kibana 上查看时会搜到多条日志, 须要逐级定位, 确定谬误抛出的点
心愿只有一条, 能清晰看到整个堆栈调用信息
应用 log.xxxx 办法:
package main | |
import ( | |
"fmt" | |
"log" | |
"strconv" | |
"time" | |
) | |
func init() {log.SetFlags(log.Lshortfile | log.LstdFlags) | |
} | |
func main() { | |
str := "123a" | |
rs, err := func1(str) | |
if err != nil {log.Printf("err is (%+v)\n", err) | |
return | |
} | |
fmt.Println("最终后果为:", rs) | |
} | |
func func1(str string) (int, error) {b, err := func2() | |
if err != nil {log.Printf("There is func11111, func2 err(%+v)\n", err) | |
} | |
if b == false {strInt, err := strconv.Atoi(str) | |
if err != nil {log.Printf("There is func11111, err(%+v)\n", err) | |
} | |
return strInt, err | |
} | |
return 0, nil | |
} | |
func func2() (bool, error) {now := time.Now().Unix() | |
endTimeStr := "2021-08-06 20:00:0000" | |
endtime, err := time.ParseInLocation("2006-01-02 15:04:05", endTimeStr, time.Local) | |
if err != nil {log.Printf("There is func22222, err(%+v)\n", err) | |
return false, err | |
} | |
if endtime.Unix() > now {return true, nil} | |
return false, nil | |
} |
执行后果:
2021/06/07 21:52:56 vs.go:56: There is func22222, err(parsing time "2021-08-06 20:00:0000": extra text: "00") | |
2021/06/07 21:52:56 vs.go:33: There is func11111, func2 err(parsing time "2021-08-06 20:00:0000": extra text: "00") | |
2021/06/07 21:52:56 vs.go:40: There is func11111, err(strconv.Atoi: parsing "123a": invalid syntax) | |
2021/06/07 21:52:56 vs.go:20: err is (strconv.Atoi: parsing "123a": invalid syntax) |
应用 errors.Wrapf 办法:
package main | |
import ( | |
"fmt" | |
"github.com/pkg/errors" | |
"strconv" | |
"time" | |
) | |
func main() { | |
str := "123a" | |
rs, err := func1(str) | |
if err != nil {fmt.Printf("err: %+v\n", err) | |
//fmt.Println("err:", lastErr) // 必须 %+ v 才会打印残缺堆栈信息, 否则只打印错误信息 | |
return | |
} | |
fmt.Println("最终后果为:", rs) | |
} | |
func func1(str string) (int, error) {b, err := func2() | |
if err != nil {err = errors.Wrapf(err, "There is func11111, func2 err, b is(%b) \n", b) | |
} | |
if b == false { | |
var strInt int | |
strInt, err = strconv.Atoi(str) | |
if err != nil {err = errors.Wrapf(err, "There is func11111,str is(%s)\n", str) | |
} | |
return strInt, err | |
} | |
return 0, nil | |
} | |
func func2() (bool, error) {now := time.Now().Unix() | |
endTimeStr := "2021-08-06 20:00:0000" | |
endtime, err := time.ParseInLocation("2006-01-02 15:04:05", endTimeStr, time.Local) | |
if err != nil {err = errors.Wrapf(err, "There is func22222,endTimeStr is(%s)\n", endTimeStr) | |
return false, err | |
} | |
if endtime.Unix() > now {return true, nil} | |
return false, nil | |
} |
执行:
err: strconv.Atoi: parsing "123a": invalid syntax | |
There is func11111,str is(123a) | |
main.func1 | |
/Users/fliter/go/src/shuang/llog/1.go:39 | |
main.main | |
/Users/fliter/go/src/shuang/llog/1.go:13 | |
runtime.main | |
/usr/local/Cellar/go/1.16.3/libexec/src/runtime/proc.go:225 | |
runtime.goexit | |
/usr/local/Cellar/go/1.16.3/libexec/src/runtime/asm_amd64.s:1371 |
留神赋值这步, 必不可少!
有一个问题, 即对于 f1 调 f2,f2 调 f3 这种, 如果 f3 产生 error, 能够逐级将 error 抛出.
但如果一个办法里有两个 error, 则第二条会笼罩掉第一条, 如上 err = errors.Wrapf(err, "There is func11111, func2 err, b is(%b) \n", b)
这一条就被笼罩了
// Wrapf returns an error annotating err with a stack trace | |
// at the point Wrapf is called, and the format specifier. | |
// If err is nil, Wrapf returns nil. | |
func Wrapf(err error, format string, args ...interface{}) error { | |
if err == nil {return nil} | |
err = &withMessage{ | |
cause: err, | |
msg: fmt.Sprintf(format, args...), | |
} | |
return &withStack{ | |
err, | |
callers(),} | |
} |
func callers() *stack { | |
const depth = 32 | |
var pcs [depth]uintptr | |
n := runtime.Callers(3, pcs[:]) | |
var st stack = pcs[0:n] | |
return &st | |
} |
// Callers fills the slice pc with the return program counters of function invocations | |
// on the calling goroutine's stack. The argument skip is the number of stack frames | |
// to skip before recording in pc, with 0 identifying the frame for Callers itself and | |
// 1 identifying the caller of Callers. | |
// It returns the number of entries written to pc. | |
// | |
// To translate these PCs into symbolic information such as function | |
// names and line numbers, use CallersFrames. CallersFrames accounts | |
// for inlined functions and adjusts the return program counters into | |
// call program counters. Iterating over the returned slice of PCs | |
// directly is discouraged, as is using FuncForPC on any of the | |
// returned PCs, since these cannot account for inlining or return | |
// program counter adjustment. | |
func Callers(skip int, pc []uintptr) int { | |
// runtime.callers uses pc.array==nil as a signal | |
// to print a stack trace. Pick off 0-length pc here | |
// so that we don't let a nil pc slice get to it. | |
if len(pc) == 0 {return 0} | |
return callers(skip, pc) | |
} |
func callers(skip int, pcbuf []uintptr) int {sp := getcallersp() | |
pc := getcallerpc() | |
gp := getg() | |
var n int | |
systemstack(func() {n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0) | |
}) | |
return n | |
} |
参考:
golang 打印 error 的堆栈信息
Go 错误处理最佳实际
本文由 mdnice 多平台公布
正文完