golangerrorerror
golang中的error
golangerrorError()struct
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}
Model
func SomeFunc(id int) (Result, error) {
result, err := Find(id)
if err != nil {
// 没有找到数据,向log里面写入一条消息,方便以后进行问题定位
log.Info("some err in Find..., err: %v", err)
// 然后将错误进行返回
return Result{}, err
}
return result, nil
}
ServiceSomeFun()Result
func ServiceFun(id int) bool {
result, err := SomeFunc(id)
if err != nil {
// SomeFun执行出错,向log里面写入一条消息,方便以后进行问题定位
log.Info("some err in SomeFun..., err: %v", err)
return false
}
// do sth to handle result
}
error优雅的处理error
ServiceModelerrorlog.Info("some err in Find..., err: %v", err)errgolangerrorSomeFunreturn Result{}, err
使用github.com/pkg/error来处理错误
使用这个库可以很方便的打印出程序的调用栈。先来看一段程序
func foo() error {
return errors.Wrap(sql.ErrNoRows, "foo failed")
}
func bar() error {
err := foo()
return errors.WithMessage(err, "bar failed")
}
func baz() error {
err := bar()
return errors.WithMessage(err, "baz failed")
}
fun main() {
err := baz()
fmt.Printf("data not found, %v\n", err) // 标记①
fmt.Printf("data not found, %+v\n", err) /// 标记②
}
来看一下输出结果
### 下面这一行是标记①打印的内容
data not found, baz failed: bar failed: foo failed: sql: no rows in result set
### 下面这些内容是标记②打印的内容
sql: no rows in result set
foo failed
main.foo
/path/to/main.go:24
main.bar
/path/to/main.go:30
main.baz
/path/to/main.go:35
main.main
/path/to/main.go:65
runtime.main
/usr/local/Cellar/go@1.15/1.15.11/libexec/src/runtime/proc.go:204
runtime.goexit
/usr/local/Cellar/go@1.15/1.15.11/libexec/src/runtime/asm_amd64.s:1374
bar failed
baz failed
%v%+vWrapWithMessage
// 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(),
}
}
// WithMessage annotates err with a new message.
// If err is nil, WithMessage returns nil.
func WithMessage(err error, message string) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: message,
}
}
type withMessage struct {
cause error
msg string
}
type withStack struct {
error
*stack
}
WrapWithMessagemessageWrapwithStackWithMessagewithMessagewithStack*stackcallers()
mainerr := baz()err
%v%+v
WraperrorWithMessageWrapWrap%+v
更进一步
虽然看到详细的调用堆栈对错误的定位很方便,但是过多的堆栈信息打印仍然会对日志系统带来很大的负担,有些时候对问题的定位其实往往不需要详细的调用栈,只需要一条调用链即可,比如
main.mian()@line: err ==> main.a()@line: err ==> b.b()@line: err ==> c.c()@line: err ... ==> fun()@line: some err
github.com/pkg/errorWrapWithMessage
package MyError
import (
"github.com/pkg/errors"
"runtime"
"strconv"
)
/**
* @Author: chapaofan
* @Date: 2021/5/15 2:08 下午
*/
func Wrap(err error, message string) error {
return errors.Wrap(err, "==> "+printCallerNameAndLine()+message)
}
func WithMessage(err error, message string) error {
return errors.WithMessage(err, "==> "+printCallerNameAndLine()+message)
}
func printCallerNameAndLine() string {
pc, _, line, _ := runtime.Caller(2)
return runtime.FuncForPC(pc).Name() + "()@" + strconv.Itoa(line) + ": "
}
main
data not found, ==> main.baz()@39: baz failed: ==> main.bar()@33: bar failed: ==> main.foo()@24: foo failed: sql: no rows in result set