...

Source file src/github.com/go-errors/errors/error.go

Documentation: github.com/go-errors/errors

     1  // Package errors provides errors that have stack-traces.
     2  //
     3  // This is particularly useful when you want to understand the
     4  // state of execution when an error was returned unexpectedly.
     5  //
     6  // It provides the type *Error which implements the standard
     7  // golang error interface, so you can use this library interchangably
     8  // with code that is expecting a normal error return.
     9  //
    10  // For example:
    11  //
    12  //  package crashy
    13  //
    14  //  import "github.com/go-errors/errors"
    15  //
    16  //  var Crashed = errors.Errorf("oh dear")
    17  //
    18  //  func Crash() error {
    19  //      return errors.New(Crashed)
    20  //  }
    21  //
    22  // This can be called as follows:
    23  //
    24  //  package main
    25  //
    26  //  import (
    27  //      "crashy"
    28  //      "fmt"
    29  //      "github.com/go-errors/errors"
    30  //  )
    31  //
    32  //  func main() {
    33  //      err := crashy.Crash()
    34  //      if err != nil {
    35  //          if errors.Is(err, crashy.Crashed) {
    36  //              fmt.Println(err.(*errors.Error).ErrorStack())
    37  //          } else {
    38  //              panic(err)
    39  //          }
    40  //      }
    41  //  }
    42  //
    43  // This package was original written to allow reporting to Bugsnag,
    44  // but after I found similar packages by Facebook and Dropbox, it
    45  // was moved to one canonical location so everyone can benefit.
    46  package errors
    47  
    48  import (
    49  	"bytes"
    50  	"fmt"
    51  	"reflect"
    52  	"runtime"
    53  )
    54  
    55  // The maximum number of stackframes on any error.
    56  var MaxStackDepth = 50
    57  
    58  // Error is an error with an attached stacktrace. It can be used
    59  // wherever the builtin error interface is expected.
    60  type Error struct {
    61  	Err    error
    62  	stack  []uintptr
    63  	frames []StackFrame
    64  	prefix string
    65  }
    66  
    67  // New makes an Error from the given value. If that value is already an
    68  // error then it will be used directly, if not, it will be passed to
    69  // fmt.Errorf("%v"). The stacktrace will point to the line of code that
    70  // called New.
    71  func New(e interface{}) *Error {
    72  	var err error
    73  
    74  	switch e := e.(type) {
    75  	case error:
    76  		err = e
    77  	default:
    78  		err = fmt.Errorf("%v", e)
    79  	}
    80  
    81  	stack := make([]uintptr, MaxStackDepth)
    82  	length := runtime.Callers(2, stack[:])
    83  	return &Error{
    84  		Err:   err,
    85  		stack: stack[:length],
    86  	}
    87  }
    88  
    89  // Wrap makes an Error from the given value. If that value is already an
    90  // error then it will be used directly, if not, it will be passed to
    91  // fmt.Errorf("%v"). The skip parameter indicates how far up the stack
    92  // to start the stacktrace. 0 is from the current call, 1 from its caller, etc.
    93  func Wrap(e interface{}, skip int) *Error {
    94  	if e == nil {
    95  		return nil
    96  	}
    97  
    98  	var err error
    99  
   100  	switch e := e.(type) {
   101  	case *Error:
   102  		return e
   103  	case error:
   104  		err = e
   105  	default:
   106  		err = fmt.Errorf("%v", e)
   107  	}
   108  
   109  	stack := make([]uintptr, MaxStackDepth)
   110  	length := runtime.Callers(2+skip, stack[:])
   111  	return &Error{
   112  		Err:   err,
   113  		stack: stack[:length],
   114  	}
   115  }
   116  
   117  // WrapPrefix makes an Error from the given value. If that value is already an
   118  // error then it will be used directly, if not, it will be passed to
   119  // fmt.Errorf("%v"). The prefix parameter is used to add a prefix to the
   120  // error message when calling Error(). The skip parameter indicates how far
   121  // up the stack to start the stacktrace. 0 is from the current call,
   122  // 1 from its caller, etc.
   123  func WrapPrefix(e interface{}, prefix string, skip int) *Error {
   124  	if e == nil {
   125  		return nil
   126  	}
   127  
   128  	err := Wrap(e, 1+skip)
   129  
   130  	if err.prefix != "" {
   131  		prefix = fmt.Sprintf("%s: %s", prefix, err.prefix)
   132  	}
   133  
   134  	return &Error{
   135  		Err:    err.Err,
   136  		stack:  err.stack,
   137  		prefix: prefix,
   138  	}
   139  
   140  }
   141  
   142  // Errorf creates a new error with the given message. You can use it
   143  // as a drop-in replacement for fmt.Errorf() to provide descriptive
   144  // errors in return values.
   145  func Errorf(format string, a ...interface{}) *Error {
   146  	return Wrap(fmt.Errorf(format, a...), 1)
   147  }
   148  
   149  // Error returns the underlying error's message.
   150  func (err *Error) Error() string {
   151  
   152  	msg := err.Err.Error()
   153  	if err.prefix != "" {
   154  		msg = fmt.Sprintf("%s: %s", err.prefix, msg)
   155  	}
   156  
   157  	return msg
   158  }
   159  
   160  // Stack returns the callstack formatted the same way that go does
   161  // in runtime/debug.Stack()
   162  func (err *Error) Stack() []byte {
   163  	buf := bytes.Buffer{}
   164  
   165  	for _, frame := range err.StackFrames() {
   166  		buf.WriteString(frame.String())
   167  	}
   168  
   169  	return buf.Bytes()
   170  }
   171  
   172  // Callers satisfies the bugsnag ErrorWithCallerS() interface
   173  // so that the stack can be read out.
   174  func (err *Error) Callers() []uintptr {
   175  	return err.stack
   176  }
   177  
   178  // ErrorStack returns a string that contains both the
   179  // error message and the callstack.
   180  func (err *Error) ErrorStack() string {
   181  	return err.TypeName() + " " + err.Error() + "\n" + string(err.Stack())
   182  }
   183  
   184  // StackFrames returns an array of frames containing information about the
   185  // stack.
   186  func (err *Error) StackFrames() []StackFrame {
   187  	if err.frames == nil {
   188  		err.frames = make([]StackFrame, len(err.stack))
   189  
   190  		for i, pc := range err.stack {
   191  			err.frames[i] = NewStackFrame(pc)
   192  		}
   193  	}
   194  
   195  	return err.frames
   196  }
   197  
   198  // TypeName returns the type this error. e.g. *errors.stringError.
   199  func (err *Error) TypeName() string {
   200  	if _, ok := err.Err.(uncaughtPanic); ok {
   201  		return "panic"
   202  	}
   203  	return reflect.TypeOf(err.Err).String()
   204  }
   205  
   206  // Return the wrapped error (implements api for As function).
   207  func (err *Error) Unwrap() error {
   208  	return err.Err
   209  }
   210  

View as plain text