Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make tidb adaptor in errors. #2

Merged
merged 21 commits into from
Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}