functions.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. // Copyright 2014 Canonical Ltd.
  2. // Licensed under the LGPLv3, see LICENCE file for details.
  3. package errors
  4. import (
  5. "fmt"
  6. "strings"
  7. )
  8. // New is a drop in replacement for the standard library errors module that records
  9. // the location that the error is created.
  10. //
  11. // For example:
  12. // return errors.New("validation failed")
  13. //
  14. func New(message string) error {
  15. err := &Err{message: message}
  16. err.SetLocation(1)
  17. return err
  18. }
  19. // Errorf creates a new annotated error and records the location that the
  20. // error is created. This should be a drop in replacement for fmt.Errorf.
  21. //
  22. // For example:
  23. // return errors.Errorf("validation failed: %s", message)
  24. //
  25. func Errorf(format string, args ...interface{}) error {
  26. err := &Err{message: fmt.Sprintf(format, args...)}
  27. err.SetLocation(1)
  28. return err
  29. }
  30. // Trace adds the location of the Trace call to the stack. The Cause of the
  31. // resulting error is the same as the error parameter. If the other error is
  32. // nil, the result will be nil.
  33. //
  34. // For example:
  35. // if err := SomeFunc(); err != nil {
  36. // return errors.Trace(err)
  37. // }
  38. //
  39. func Trace(other error) error {
  40. if other == nil {
  41. return nil
  42. }
  43. err := &Err{previous: other, cause: Cause(other)}
  44. err.SetLocation(1)
  45. return err
  46. }
  47. // Annotate is used to add extra context to an existing error. The location of
  48. // the Annotate call is recorded with the annotations. The file, line and
  49. // function are also recorded.
  50. //
  51. // For example:
  52. // if err := SomeFunc(); err != nil {
  53. // return errors.Annotate(err, "failed to frombulate")
  54. // }
  55. //
  56. func Annotate(other error, message string) error {
  57. if other == nil {
  58. return nil
  59. }
  60. err := &Err{
  61. previous: other,
  62. cause: Cause(other),
  63. message: message,
  64. }
  65. err.SetLocation(1)
  66. return err
  67. }
  68. // Annotatef is used to add extra context to an existing error. The location of
  69. // the Annotate call is recorded with the annotations. The file, line and
  70. // function are also recorded.
  71. //
  72. // For example:
  73. // if err := SomeFunc(); err != nil {
  74. // return errors.Annotatef(err, "failed to frombulate the %s", arg)
  75. // }
  76. //
  77. func Annotatef(other error, format string, args ...interface{}) error {
  78. if other == nil {
  79. return nil
  80. }
  81. err := &Err{
  82. previous: other,
  83. cause: Cause(other),
  84. message: fmt.Sprintf(format, args...),
  85. }
  86. err.SetLocation(1)
  87. return err
  88. }
  89. // DeferredAnnotatef annotates the given error (when it is not nil) with the given
  90. // format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
  91. // does nothing. This method is used in a defer statement in order to annotate any
  92. // resulting error with the same message.
  93. //
  94. // For example:
  95. //
  96. // defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
  97. //
  98. func DeferredAnnotatef(err *error, format string, args ...interface{}) {
  99. if *err == nil {
  100. return
  101. }
  102. newErr := &Err{
  103. message: fmt.Sprintf(format, args...),
  104. cause: Cause(*err),
  105. previous: *err,
  106. }
  107. newErr.SetLocation(1)
  108. *err = newErr
  109. }
  110. // Wrap changes the Cause of the error. The location of the Wrap call is also
  111. // stored in the error stack.
  112. //
  113. // For example:
  114. // if err := SomeFunc(); err != nil {
  115. // newErr := &packageError{"more context", private_value}
  116. // return errors.Wrap(err, newErr)
  117. // }
  118. //
  119. func Wrap(other, newDescriptive error) error {
  120. err := &Err{
  121. previous: other,
  122. cause: newDescriptive,
  123. }
  124. err.SetLocation(1)
  125. return err
  126. }
  127. // Wrapf changes the Cause of the error, and adds an annotation. The location
  128. // of the Wrap call is also stored in the error stack.
  129. //
  130. // For example:
  131. // if err := SomeFunc(); err != nil {
  132. // return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
  133. // }
  134. //
  135. func Wrapf(other, newDescriptive error, format string, args ...interface{}) error {
  136. err := &Err{
  137. message: fmt.Sprintf(format, args...),
  138. previous: other,
  139. cause: newDescriptive,
  140. }
  141. err.SetLocation(1)
  142. return err
  143. }
  144. // Mask masks the given error with the given format string and arguments (like
  145. // fmt.Sprintf), returning a new error that maintains the error stack, but
  146. // hides the underlying error type. The error string still contains the full
  147. // annotations. If you want to hide the annotations, call Wrap.
  148. func Maskf(other error, format string, args ...interface{}) error {
  149. if other == nil {
  150. return nil
  151. }
  152. err := &Err{
  153. message: fmt.Sprintf(format, args...),
  154. previous: other,
  155. }
  156. err.SetLocation(1)
  157. return err
  158. }
  159. // Mask hides the underlying error type, and records the location of the masking.
  160. func Mask(other error) error {
  161. if other == nil {
  162. return nil
  163. }
  164. err := &Err{
  165. previous: other,
  166. }
  167. err.SetLocation(1)
  168. return err
  169. }
  170. // Cause returns the cause of the given error. This will be either the
  171. // original error, or the result of a Wrap or Mask call.
  172. //
  173. // Cause is the usual way to diagnose errors that may have been wrapped by
  174. // the other errors functions.
  175. func Cause(err error) error {
  176. var diag error
  177. if err, ok := err.(causer); ok {
  178. diag = err.Cause()
  179. }
  180. if diag != nil {
  181. return diag
  182. }
  183. return err
  184. }
  185. type causer interface {
  186. Cause() error
  187. }
  188. type wrapper interface {
  189. // Message returns the top level error message,
  190. // not including the message from the Previous
  191. // error.
  192. Message() string
  193. // Underlying returns the Previous error, or nil
  194. // if there is none.
  195. Underlying() error
  196. }
  197. type locationer interface {
  198. Location() (string, int)
  199. }
  200. var (
  201. _ wrapper = (*Err)(nil)
  202. _ locationer = (*Err)(nil)
  203. _ causer = (*Err)(nil)
  204. )
  205. // Details returns information about the stack of errors wrapped by err, in
  206. // the format:
  207. //
  208. // [{filename:99: error one} {otherfile:55: cause of error one}]
  209. //
  210. // This is a terse alternative to ErrorStack as it returns a single line.
  211. func Details(err error) string {
  212. if err == nil {
  213. return "[]"
  214. }
  215. var s []byte
  216. s = append(s, '[')
  217. for {
  218. s = append(s, '{')
  219. if err, ok := err.(locationer); ok {
  220. file, line := err.Location()
  221. if file != "" {
  222. s = append(s, fmt.Sprintf("%s:%d", file, line)...)
  223. s = append(s, ": "...)
  224. }
  225. }
  226. if cerr, ok := err.(wrapper); ok {
  227. s = append(s, cerr.Message()...)
  228. err = cerr.Underlying()
  229. } else {
  230. s = append(s, err.Error()...)
  231. err = nil
  232. }
  233. s = append(s, '}')
  234. if err == nil {
  235. break
  236. }
  237. s = append(s, ' ')
  238. }
  239. s = append(s, ']')
  240. return string(s)
  241. }
  242. // ErrorStack returns a string representation of the annotated error. If the
  243. // error passed as the parameter is not an annotated error, the result is
  244. // simply the result of the Error() method on that error.
  245. //
  246. // If the error is an annotated error, a multi-line string is returned where
  247. // each line represents one entry in the annotation stack. The full filename
  248. // from the call stack is used in the output.
  249. //
  250. // first error
  251. // github.com/juju/errors/annotation_test.go:193:
  252. // github.com/juju/errors/annotation_test.go:194: annotation
  253. // github.com/juju/errors/annotation_test.go:195:
  254. // github.com/juju/errors/annotation_test.go:196: more context
  255. // github.com/juju/errors/annotation_test.go:197:
  256. func ErrorStack(err error) string {
  257. return strings.Join(errorStack(err), "\n")
  258. }
  259. func errorStack(err error) []string {
  260. if err == nil {
  261. return nil
  262. }
  263. // We want the first error first
  264. var lines []string
  265. for {
  266. var buff []byte
  267. if err, ok := err.(locationer); ok {
  268. file, line := err.Location()
  269. // Strip off the leading GOPATH/src path elements.
  270. file = trimGoPath(file)
  271. if file != "" {
  272. buff = append(buff, fmt.Sprintf("%s:%d", file, line)...)
  273. buff = append(buff, ": "...)
  274. }
  275. }
  276. if cerr, ok := err.(wrapper); ok {
  277. message := cerr.Message()
  278. buff = append(buff, message...)
  279. // If there is a cause for this error, and it is different to the cause
  280. // of the underlying error, then output the error string in the stack trace.
  281. var cause error
  282. if err1, ok := err.(causer); ok {
  283. cause = err1.Cause()
  284. }
  285. err = cerr.Underlying()
  286. if cause != nil && !sameError(Cause(err), cause) {
  287. if message != "" {
  288. buff = append(buff, ": "...)
  289. }
  290. buff = append(buff, cause.Error()...)
  291. }
  292. } else {
  293. buff = append(buff, err.Error()...)
  294. err = nil
  295. }
  296. lines = append(lines, string(buff))
  297. if err == nil {
  298. break
  299. }
  300. }
  301. // reverse the lines to get the original error, which was at the end of
  302. // the list, back to the start.
  303. var result []string
  304. for i := len(lines); i > 0; i-- {
  305. result = append(result, lines[i-1])
  306. }
  307. return result
  308. }