errors.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Package errors implements functions for manipulating errors.
  2. //
  3. // The tranditional error handling idiom in Go is roughly akin to
  4. //
  5. // if err != nil {
  6. // return err
  7. // }
  8. //
  9. // which applied recursively up the call stack results in error reports
  10. // without context or debugging information. The errors package allows
  11. // programmers to add context to the failure path in their code in a way
  12. // that does not destroy the original value of the error.
  13. //
  14. // Adding context to an error
  15. //
  16. // The errors.Wrap function returns a new error that adds context to the
  17. // original error. For example
  18. //
  19. // _, err := ioutil.ReadAll(r)
  20. // if err != nil {
  21. // return errors.Wrap(err, "read failed")
  22. //
  23. // In addition, errors.Wrap records the file and line where it was called,
  24. // allowing the programmer to retrieve the path to the original error.
  25. //
  26. // Retrieving the cause of an error
  27. //
  28. // Using errors.Wrap constructs a stack of errors, adding context to the
  29. // preceeding error. Depending on the nature of the error it may be necessary
  30. // to recerse the operation of errors.Wrap to retrieve the original error
  31. // for inspection. Any error value which implements this interface
  32. //
  33. // type causer interface {
  34. // Cause() error
  35. // }
  36. //
  37. // Can be inspected by errors.Cause which will recursively retrieve the topmost
  38. // error which does nor implement causer, which is assumed to be the original
  39. // cause. For example:
  40. //
  41. // switch err := errors.Cause(err).(type) {
  42. // case *MyError:
  43. // // handle specifically
  44. // default:
  45. // // unknown error
  46. // }
  47. package errors
  48. import (
  49. "errors"
  50. "fmt"
  51. "io"
  52. "os"
  53. "runtime"
  54. "strings"
  55. )
  56. type loc uintptr
  57. func (l loc) Location() (string, int) {
  58. pc := uintptr(l)
  59. fn := runtime.FuncForPC(pc)
  60. if fn == nil {
  61. return "unknown", 0
  62. }
  63. _, prefix, _, _ := runtime.Caller(0)
  64. file, line := fn.FileLine(pc)
  65. if i := strings.LastIndex(prefix, "github.com/pkg/errors"); i > 0 {
  66. file = file[i:]
  67. }
  68. return file, line
  69. }
  70. // New returns an error that formats as the given text.
  71. func New(text string) error {
  72. pc, _, _, _ := runtime.Caller(1)
  73. return struct {
  74. error
  75. loc
  76. }{
  77. errors.New(text),
  78. loc(pc),
  79. }
  80. }
  81. type e struct {
  82. cause error
  83. message string
  84. loc
  85. }
  86. func (e *e) Error() string {
  87. return e.message + ": " + e.cause.Error()
  88. }
  89. func (e *e) Cause() error {
  90. return e.cause
  91. }
  92. // Wrap returns an error annotating the cause with message.
  93. // If cause is nil, Wrap returns nil.
  94. func Wrap(cause error, message string) error {
  95. if cause == nil {
  96. return nil
  97. }
  98. pc, _, _, _ := runtime.Caller(1)
  99. return &e{
  100. cause: cause,
  101. message: message,
  102. loc: loc(pc),
  103. }
  104. }
  105. type causer interface {
  106. Cause() error
  107. }
  108. // Cause returns the underlying cause of the error, if possible.
  109. // An error value has a cause if it implements the following
  110. // interface:
  111. //
  112. // type Causer interface {
  113. // Cause() error
  114. // }
  115. //
  116. // If the error does not implement Cause, the original error will
  117. // be returned. If the error is nil, nil will be returned without further
  118. // investigation.
  119. func Cause(err error) error {
  120. for err != nil {
  121. cause, ok := err.(causer)
  122. if !ok {
  123. break
  124. }
  125. err = cause.Cause()
  126. }
  127. return err
  128. }
  129. type locationer interface {
  130. Location() (string, int)
  131. }
  132. // Print prints the error to Stderr.
  133. // If the error implements the Causer interface described in Cause
  134. // Print will recurse into the error's cause.
  135. // If the error implements the inteface:
  136. //
  137. // type Location interface {
  138. // Location() (file string, line int)
  139. // }
  140. //
  141. // Print will also print the file and line of the error.
  142. func Print(err error) {
  143. Fprint(os.Stderr, err)
  144. }
  145. // Fprint prints the error to the supplied writer.
  146. // The format of the output is the same as Print.
  147. // If err is nil, nothing is printed.
  148. func Fprint(w io.Writer, err error) {
  149. for err != nil {
  150. location, ok := err.(locationer)
  151. if ok {
  152. file, line := location.Location()
  153. fmt.Fprintf(w, "%s:%d: ", file, line)
  154. }
  155. switch err := err.(type) {
  156. case *e:
  157. fmt.Fprintln(w, err.message)
  158. default:
  159. fmt.Fprintln(w, err.Error())
  160. }
  161. cause, ok := err.(causer)
  162. if !ok {
  163. break
  164. }
  165. err = cause.Cause()
  166. }
  167. }