github.com/team-ide/go-dialect@v1.9.20/vitess/vterrors/vterrors.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package vterrors provides simple error handling primitives for Vitess
    18  //
    19  // In all Vitess code, errors should be propagated using vterrors.Wrapf()
    20  // and not fmt.Errorf(). This makes sure that stacktraces are kept and
    21  // propagated correctly.
    22  //
    23  // New errors should be created using vterrors.New or vterrors.Errorf
    24  //
    25  // Vitess uses canonical error codes for error reporting. This is based
    26  // on years of industry experience with error reporting. This idea is
    27  // that errors should be classified into a small set of errors (10 or so)
    28  // with very specific meaning. Each error has a code, and a message. When
    29  // errors are passed around (even through RPCs), the code is
    30  // propagated. To handle errors, only the code should be looked at (and
    31  // not string-matching on the error message).
    32  //
    33  // Error codes are defined in /proto/vtrpc.proto. Along with an
    34  // RPCError message that can be used to transmit errors through RPCs, in
    35  // the message payloads. These codes match the names and numbers defined
    36  // by gRPC.
    37  //
    38  // A standardized error implementation that allows you to build an error
    39  // with an associated canonical code is also defined.
    40  // While sending an error through gRPC, these codes are transmitted
    41  // using gRPC's error propagation mechanism and decoded back to
    42  // the original code on the other end.
    43  //
    44  // Retrieving the cause of an error
    45  //
    46  // Using vterrors.Wrap constructs a stack of errors, adding context to the
    47  // preceding error, instead of simply building up a string.
    48  // Depending on the nature of the error it may be necessary to reverse the
    49  // operation of errors.Wrap to retrieve the original error for inspection.
    50  // Any error value which implements this interface
    51  //
    52  //     type causer interface {
    53  //             Cause() error
    54  //     }
    55  //
    56  // can be inspected by vterrors.Cause and vterrors.RootCause.
    57  //
    58  // * vterrors.Cause will find the immediate cause if one is available, or nil
    59  //   if the error is not a `causer` or if no cause is available.
    60  // * vterrors.RootCause will recursively retrieve
    61  //   the topmost error which does not implement causer, which is assumed to be
    62  //   the original cause. For example:
    63  //
    64  //     switch err := errors.RootCause(err).(type) {
    65  //     case *MyError:
    66  //             // handle specifically
    67  //     default:
    68  //             // unknown error
    69  //     }
    70  //
    71  // causer interface is not exported by this package, but is considered a part
    72  // of stable public API.
    73  //
    74  // Formatted printing of errors
    75  //
    76  // All error values returned from this package implement fmt.Formatter and can
    77  // be formatted by the fmt package. The following verbs are supported
    78  //
    79  //     %s    print the error. If the error has a Cause it will be
    80  //           printed recursively
    81  //     %v    extended format. Each Frame of the error's StackTrace will
    82  //           be printed in detail.
    83  //
    84  // Most but not all of the code in this file was originally copied from
    85  // https://github.com/pkg/errors/blob/v0.8.0/errors.go
    86  package vterrors
    87  
    88  import (
    89  	"fmt"
    90  	"io"
    91  
    92  	"context"
    93  
    94  	vtrpcpb "github.com/team-ide/go-dialect/vitess/vtrpc"
    95  )
    96  
    97  // LogErrStacks controls whether or not printing errors includes the
    98  // embedded stack trace in the output.
    99  var LogErrStacks bool
   100  
   101  func init() {
   102  	//flag.BoolVar(&LogErrStacks, "log_err_stacks", false, "log stack traces for errors")
   103  }
   104  
   105  // New returns an error with the supplied message.
   106  // New also records the stack trace at the point it was called.
   107  func New(code vtrpcpb.Code, message string) error {
   108  	return &fundamental{
   109  		msg:   message,
   110  		code:  code,
   111  		stack: callers(),
   112  	}
   113  }
   114  
   115  // Errorf formats according to a format specifier and returns the string
   116  // as a value that satisfies error.
   117  // Errorf also records the stack trace at the point it was called.
   118  func Errorf(code vtrpcpb.Code, format string, args ...interface{}) error {
   119  	return &fundamental{
   120  		msg:   fmt.Sprintf(format, args...),
   121  		code:  code,
   122  		stack: callers(),
   123  	}
   124  }
   125  
   126  // NewErrorf formats according to a format specifier and returns the string
   127  // as a value that satisfies error.
   128  // NewErrorf also records the stack trace at the point it was called.
   129  func NewErrorf(code vtrpcpb.Code, state State, format string, args ...interface{}) error {
   130  	return &fundamental{
   131  		msg:   fmt.Sprintf(format, args...),
   132  		code:  code,
   133  		state: state,
   134  		stack: callers(),
   135  	}
   136  }
   137  
   138  // fundamental is an error that has a message and a stack, but no caller.
   139  type fundamental struct {
   140  	msg   string
   141  	code  vtrpcpb.Code
   142  	state State
   143  	*stack
   144  }
   145  
   146  func (f *fundamental) Error() string { return f.msg }
   147  
   148  func (f *fundamental) Format(s fmt.State, verb rune) {
   149  	switch verb {
   150  	case 'v':
   151  		panicIfError(io.WriteString(s, "Code: "+f.code.String()+"\n"))
   152  		panicIfError(io.WriteString(s, f.msg+"\n"))
   153  		if LogErrStacks {
   154  			f.stack.Format(s, verb)
   155  		}
   156  		return
   157  	case 's':
   158  		panicIfError(io.WriteString(s, f.msg))
   159  	case 'q':
   160  		panicIfError(fmt.Fprintf(s, "%q", f.msg))
   161  	}
   162  }
   163  
   164  // Code returns the error code if it's a vtError.
   165  // If err is nil, it returns ok.
   166  func Code(err error) vtrpcpb.Code {
   167  	if err == nil {
   168  		return vtrpcpb.Code_OK
   169  	}
   170  	if err, ok := err.(*fundamental); ok {
   171  		return err.code
   172  	}
   173  
   174  	cause := Cause(err)
   175  	if cause != err && cause != nil {
   176  		// If we did not find an error code at the outer level, let's find the cause and check it's code
   177  		return Code(cause)
   178  	}
   179  
   180  	// Handle some special cases.
   181  	switch err {
   182  	case context.Canceled:
   183  		return vtrpcpb.Code_CANCELED
   184  	case context.DeadlineExceeded:
   185  		return vtrpcpb.Code_DEADLINE_EXCEEDED
   186  	}
   187  	return vtrpcpb.Code_UNKNOWN
   188  }
   189  
   190  // ErrState returns the error state if it's a vtError.
   191  // If err is nil, it returns Undefined.
   192  func ErrState(err error) State {
   193  	if err == nil {
   194  		return Undefined
   195  	}
   196  	if err, ok := err.(*fundamental); ok {
   197  		return err.state
   198  	}
   199  
   200  	cause := Cause(err)
   201  	if cause != err && cause != nil {
   202  		// If we did not find an error state at the outer level, let's find the cause and check it's state
   203  		return ErrState(cause)
   204  	}
   205  	return Undefined
   206  }
   207  
   208  // Wrap returns an error annotating err with a stack trace
   209  // at the point Wrap is called, and the supplied message.
   210  // If err is nil, Wrap returns nil.
   211  func Wrap(err error, message string) error {
   212  	if err == nil {
   213  		return nil
   214  	}
   215  	return &wrapping{
   216  		cause: err,
   217  		msg:   message,
   218  		stack: callers(),
   219  	}
   220  }
   221  
   222  // Wrapf returns an error annotating err with a stack trace
   223  // at the point Wrapf is call, and the format specifier.
   224  // If err is nil, Wrapf returns nil.
   225  func Wrapf(err error, format string, args ...interface{}) error {
   226  	if err == nil {
   227  		return nil
   228  	}
   229  	return &wrapping{
   230  		cause: err,
   231  		msg:   fmt.Sprintf(format, args...),
   232  		stack: callers(),
   233  	}
   234  }
   235  
   236  type wrapping struct {
   237  	cause error
   238  	msg   string
   239  	stack *stack
   240  }
   241  
   242  func (w *wrapping) Error() string { return w.msg + ": " + w.cause.Error() }
   243  func (w *wrapping) Cause() error  { return w.cause }
   244  
   245  func (w *wrapping) Format(s fmt.State, verb rune) {
   246  	if rune('v') == verb {
   247  		panicIfError(fmt.Fprintf(s, "%v\n", w.Cause()))
   248  		panicIfError(io.WriteString(s, w.msg))
   249  		if LogErrStacks {
   250  			w.stack.Format(s, verb)
   251  		}
   252  		return
   253  	}
   254  
   255  	if rune('s') == verb || rune('q') == verb {
   256  		panicIfError(io.WriteString(s, w.Error()))
   257  	}
   258  }
   259  
   260  // since we can't return an error, let's panic if something goes wrong here
   261  func panicIfError(_ int, err error) {
   262  	if err != nil {
   263  		panic(err)
   264  	}
   265  }
   266  
   267  // RootCause returns the underlying cause of the error, if possible.
   268  // An error value has a cause if it implements the following
   269  // interface:
   270  //
   271  //     type causer interface {
   272  //            Cause() error
   273  //     }
   274  //
   275  // If the error does not implement Cause, the original error will
   276  // be returned. If the error is nil, nil will be returned without further
   277  // investigation.
   278  func RootCause(err error) error {
   279  	for {
   280  		cause := Cause(err)
   281  		if cause == nil {
   282  			return err
   283  		}
   284  		err = cause
   285  	}
   286  }
   287  
   288  //
   289  // Cause will return the immediate cause, if possible.
   290  // An error value has a cause if it implements the following
   291  // interface:
   292  //
   293  //     type causer interface {
   294  //            Cause() error
   295  //     }
   296  // If the error does not implement Cause, nil will be returned
   297  func Cause(err error) error {
   298  	type causer interface {
   299  		Cause() error
   300  	}
   301  
   302  	causerObj, ok := err.(causer)
   303  	if !ok {
   304  		return nil
   305  	}
   306  
   307  	return causerObj.Cause()
   308  }
   309  
   310  // Equals returns true iff the error message and the code returned by Code()
   311  // are equal.
   312  func Equals(a, b error) bool {
   313  	if a == nil && b == nil {
   314  		// Both are nil.
   315  		return true
   316  	}
   317  
   318  	if a == nil || b == nil {
   319  		// One of the two is nil, since we know both are not nil.
   320  		return false
   321  	}
   322  
   323  	return a.Error() == b.Error() && Code(a) == Code(b)
   324  }
   325  
   326  // Print is meant to print the vtError object in test failures.
   327  // For comparing two vterrors, use Equals() instead.
   328  func Print(err error) string {
   329  	return fmt.Sprintf("%v: %v\n", Code(err), err.Error())
   330  }