Skip to content

Commit

Permalink
make tidb adaptor in errors. (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
lysu authored and coocood committed Sep 7, 2018
1 parent 816c908 commit f295249
Show file tree
Hide file tree
Showing 7 changed files with 407 additions and 82 deletions.
130 changes: 96 additions & 34 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,30 +64,22 @@
//
// Retrieving the stack trace of an error or wrapper
//
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface.
//
// type stackTracer interface {
// StackTrace() errors.StackTrace
// }
//
// Where errors.StackTrace is defined as
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are invoked.
// This information can be retrieved with the StackTracer interface that returns
// a StackTrace. Where errors.StackTrace is defined as
//
// type StackTrace []Frame
//
// The Frame type represents a call site in the stack trace. Frame supports
// the fmt.Formatter interface that can be used for printing information about
// the stack trace of this error. For example:
//
// if err, ok := err.(stackTracer); ok {
// for _, f := range err.StackTrace() {
// if stacked := errors.GetStackTracer(err); stacked != nil {
// for _, f := range stacked.StackTrace() {
// fmt.Printf("%+s:%d", f)
// }
// }
//
// stackTracer interface is not exported by this package, but is considered a part
// of stable public API.
//
// See the documentation for Frame.Format for more details.
package errors

Expand Down Expand Up @@ -115,6 +107,21 @@ func Errorf(format string, args ...interface{}) error {
}
}

// StackTraceAware is an optimization to avoid repetitive traversals of an error chain.
// HasStack checks for this marker first.
// Annotate/Wrap and Annotatef/Wrapf will produce this marker.
type StackTraceAware interface {
HasStack() bool
}

// HasStack tells whether a StackTracer exists in the error chain
func HasStack(err error) bool {
if errWithStack, ok := err.(StackTraceAware); ok {
return errWithStack.HasStack()
}
return GetStackTracer(err) != nil
}

// fundamental is an error that has a message and a stack, but no caller.
type fundamental struct {
msg string
Expand Down Expand Up @@ -145,12 +152,38 @@ func WithStack(err error) error {
if err == nil {
return nil
}

return &withStack{
err,
callers(),
}
}

// AddStack is similar to WithStack.
// However, it will first check with HasStack to see if a stack trace already exists in the causer chain before creating another one.
func AddStack(err error) error {
if HasStack(err) {
return err
}
return WithStack(err)
}

// GetStackTracer will return the first StackTracer in the causer chain.
// This function is used by AddStack to avoid creating redundant stack traces.
//
// You can also use the StackTracer interface on the returned error to get the stack trace.
func GetStackTracer(origErr error) StackTracer {
var stacked StackTracer
WalkDeep(origErr, func(err error) bool {
if stackTracer, ok := err.(StackTracer); ok {
stacked = stackTracer
return true
}
return false
})
return stacked
}

type withStack struct {
error
*stack
Expand All @@ -175,15 +208,19 @@ func (w *withStack) Format(s fmt.State, verb rune) {
}

// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
// at the point Annotate is called, and the supplied message.
// If err is nil, Annotate returns nil.
//
// Deprecated: use Annotate instead
func Wrap(err error, message string) error {
if err == nil {
return nil
}
hasStack := HasStack(err)
err = &withMessage{
cause: err,
msg: message,
cause: err,
msg: message,
causeHasStack: hasStack,
}
return &withStack{
err,
Expand All @@ -192,15 +229,19 @@ func Wrap(err error, message string) error {
}

// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is call, and the format specifier.
// If err is nil, Wrapf returns nil.
// at the point Annotatef is call, and the format specifier.
// If err is nil, Annotatef returns nil.
//
// Deprecated: use Annotatef instead
func Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
hasStack := HasStack(err)
err = &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
cause: err,
msg: fmt.Sprintf(format, args...),
causeHasStack: hasStack,
}
return &withStack{
err,
Expand All @@ -215,18 +256,21 @@ func WithMessage(err error, message string) error {
return nil
}
return &withMessage{
cause: err,
msg: message,
cause: err,
msg: message,
causeHasStack: HasStack(err),
}
}

type withMessage struct {
cause error
msg string
cause error
msg string
causeHasStack bool
}

func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *withMessage) Cause() error { return w.cause }
func (w *withMessage) HasStack() bool { return w.causeHasStack }

func (w *withMessage) Format(s fmt.State, verb rune) {
switch verb {
Expand Down Expand Up @@ -254,16 +298,34 @@ func (w *withMessage) Format(s fmt.State, verb rune) {
// be returned. If the error is nil, nil will be returned without further
// investigation.
func Cause(err error) error {
cause := Unwrap(err)
if cause == nil {
return err
}
return Cause(cause)
}

// Unwrap uses causer to return the next error in the chain or nil.
// This goes one-level deeper, whereas Cause goes as far as possible
func Unwrap(err error) error {
type causer interface {
Cause() error
}
if unErr, ok := err.(causer); ok {
return unErr.Cause()
}
return nil
}

for err != nil {
cause, ok := err.(causer)
if !ok {
break
// Find an error in the chain that matches a test function
func Find(origErr error, test func(error) bool) error {
var foundErr error
WalkDeep(origErr, func(err error) bool {
if test(err) {
foundErr = err
return true
}
err = cause.Cause()
}
return err
return false
})
return foundErr
}
Loading

0 comments on commit f295249

Please sign in to comment.