You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
7.1 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package common
  2. import (
  3. "fmt"
  4. "runtime"
  5. )
  6. //----------------------------------------
  7. // Convenience methods
  8. // ErrorWrap will just call .TraceFrom(), or create a new *cmnError.
  9. func ErrorWrap(cause interface{}, format string, args ...interface{}) Error {
  10. msg := Fmt(format, args...)
  11. if causeCmnError, ok := cause.(*cmnError); ok {
  12. return causeCmnError.TraceFrom(1, msg)
  13. }
  14. // NOTE: cause may be nil.
  15. // NOTE: do not use causeCmnError here, not the same as nil.
  16. return newError(msg, cause, cause).Stacktrace()
  17. }
  18. //----------------------------------------
  19. // Error & cmnError
  20. /*
  21. Usage:
  22. ```go
  23. // Error construction
  24. var someT = errors.New("Some err type")
  25. var err1 error = NewErrorWithT(someT, "my message")
  26. ...
  27. // Wrapping
  28. var err2 error = ErrorWrap(err1, "another message")
  29. if (err1 != err2) { panic("should be the same")
  30. ...
  31. // Error handling
  32. switch err2.T() {
  33. case someT: ...
  34. default: ...
  35. }
  36. ```
  37. */
  38. type Error interface {
  39. Error() string
  40. Message() string
  41. Stacktrace() Error
  42. Trace(format string, args ...interface{}) Error
  43. TraceFrom(offset int, format string, args ...interface{}) Error
  44. Cause() interface{}
  45. WithT(t interface{}) Error
  46. T() interface{}
  47. Format(s fmt.State, verb rune)
  48. }
  49. // New Error with no cause where the type is the format string of the message..
  50. func NewError(format string, args ...interface{}) Error {
  51. msg := Fmt(format, args...)
  52. return newError(msg, nil, format)
  53. }
  54. // New Error with specified type and message.
  55. func NewErrorWithT(t interface{}, format string, args ...interface{}) Error {
  56. msg := Fmt(format, args...)
  57. return newError(msg, nil, t)
  58. }
  59. // NOTE: The name of a function "NewErrorWithCause()" implies that you are
  60. // creating a new Error, yet, if the cause is an Error, creating a new Error to
  61. // hold a ref to the old Error is probably *not* what you want to do.
  62. // So, use ErrorWrap(cause, format, a...) instead, which returns the same error
  63. // if cause is an Error.
  64. // IF you must set an Error as the cause of an Error,
  65. // then you can use the WithCauser interface to do so manually.
  66. // e.g. (error).(tmlibs.WithCauser).WithCause(causeError)
  67. type WithCauser interface {
  68. WithCause(cause interface{}) Error
  69. }
  70. type cmnError struct {
  71. msg string // first msg which also appears in msg
  72. cause interface{} // underlying cause (or panic object)
  73. t interface{} // for switching on error
  74. msgtraces []msgtraceItem // all messages traced
  75. stacktrace []uintptr // first stack trace
  76. }
  77. var _ WithCauser = &cmnError{}
  78. var _ Error = &cmnError{}
  79. // NOTE: do not expose.
  80. func newError(msg string, cause interface{}, t interface{}) *cmnError {
  81. return &cmnError{
  82. msg: msg,
  83. cause: cause,
  84. t: t,
  85. msgtraces: nil,
  86. stacktrace: nil,
  87. }
  88. }
  89. func (err *cmnError) Message() string {
  90. return err.msg
  91. }
  92. func (err *cmnError) Error() string {
  93. return fmt.Sprintf("%v", err)
  94. }
  95. // Captures a stacktrace if one was not already captured.
  96. func (err *cmnError) Stacktrace() Error {
  97. if err.stacktrace == nil {
  98. var offset = 3
  99. var depth = 32
  100. err.stacktrace = captureStacktrace(offset, depth)
  101. }
  102. return err
  103. }
  104. // Add tracing information with msg.
  105. func (err *cmnError) Trace(format string, args ...interface{}) Error {
  106. msg := Fmt(format, args...)
  107. return err.doTrace(msg, 0)
  108. }
  109. // Same as Trace, but traces the line `offset` calls out.
  110. // If n == 0, the behavior is identical to Trace().
  111. func (err *cmnError) TraceFrom(offset int, format string, args ...interface{}) Error {
  112. msg := Fmt(format, args...)
  113. return err.doTrace(msg, offset)
  114. }
  115. // Return last known cause.
  116. // NOTE: The meaning of "cause" is left for the caller to define.
  117. // There exists no "canonical" definition of "cause".
  118. // Instead of blaming, try to handle it, or organize it.
  119. func (err *cmnError) Cause() interface{} {
  120. return err.cause
  121. }
  122. // Overwrites the Error's cause.
  123. func (err *cmnError) WithCause(cause interface{}) Error {
  124. err.cause = cause
  125. return err
  126. }
  127. // Overwrites the Error's type.
  128. func (err *cmnError) WithT(t interface{}) Error {
  129. err.t = t
  130. return err
  131. }
  132. // Return the "type" of this message, primarily for switching
  133. // to handle this Error.
  134. func (err *cmnError) T() interface{} {
  135. return err.t
  136. }
  137. func (err *cmnError) doTrace(msg string, n int) Error {
  138. pc, _, _, _ := runtime.Caller(n + 2) // +1 for doTrace(). +1 for the caller.
  139. // Include file & line number & msg.
  140. // Do not include the whole stack trace.
  141. err.msgtraces = append(err.msgtraces, msgtraceItem{
  142. pc: pc,
  143. msg: msg,
  144. })
  145. return err
  146. }
  147. func (err *cmnError) Format(s fmt.State, verb rune) {
  148. switch verb {
  149. case 'p':
  150. s.Write([]byte(fmt.Sprintf("%p", &err)))
  151. default:
  152. if s.Flag('#') {
  153. s.Write([]byte("--= Error =--\n"))
  154. // Write msg.
  155. s.Write([]byte(fmt.Sprintf("Message: %#s\n", err.msg)))
  156. // Write cause.
  157. s.Write([]byte(fmt.Sprintf("Cause: %#v\n", err.cause)))
  158. // Write type.
  159. s.Write([]byte(fmt.Sprintf("T: %#v\n", err.t)))
  160. // Write msg trace items.
  161. s.Write([]byte(fmt.Sprintf("Msg Traces:\n")))
  162. for i, msgtrace := range err.msgtraces {
  163. s.Write([]byte(fmt.Sprintf(" %4d %s\n", i, msgtrace.String())))
  164. }
  165. // Write stack trace.
  166. if err.stacktrace != nil {
  167. s.Write([]byte(fmt.Sprintf("Stack Trace:\n")))
  168. for i, pc := range err.stacktrace {
  169. fnc := runtime.FuncForPC(pc)
  170. file, line := fnc.FileLine(pc)
  171. s.Write([]byte(fmt.Sprintf(" %4d %s:%d\n", i, file, line)))
  172. }
  173. }
  174. s.Write([]byte("--= /Error =--\n"))
  175. } else {
  176. // Write msg.
  177. if err.cause != nil {
  178. s.Write([]byte(fmt.Sprintf("Error{`%s` (cause: %v)}", err.msg, err.cause))) // TODO tick-esc?
  179. } else {
  180. s.Write([]byte(fmt.Sprintf("Error{`%s`}", err.msg))) // TODO tick-esc?
  181. }
  182. }
  183. }
  184. }
  185. //----------------------------------------
  186. // stacktrace & msgtraceItem
  187. func captureStacktrace(offset int, depth int) []uintptr {
  188. var pcs = make([]uintptr, depth)
  189. n := runtime.Callers(offset, pcs)
  190. return pcs[0:n]
  191. }
  192. type msgtraceItem struct {
  193. pc uintptr
  194. msg string
  195. }
  196. func (mti msgtraceItem) String() string {
  197. fnc := runtime.FuncForPC(mti.pc)
  198. file, line := fnc.FileLine(mti.pc)
  199. return fmt.Sprintf("%s:%d - %s",
  200. file, line,
  201. mti.msg,
  202. )
  203. }
  204. //----------------------------------------
  205. // Panic wrappers
  206. // XXX DEPRECATED
  207. // A panic resulting from a sanity check means there is a programmer error
  208. // and some guarantee is not satisfied.
  209. // XXX DEPRECATED
  210. func PanicSanity(v interface{}) {
  211. panic(Fmt("Panicked on a Sanity Check: %v", v))
  212. }
  213. // A panic here means something has gone horribly wrong, in the form of data corruption or
  214. // failure of the operating system. In a correct/healthy system, these should never fire.
  215. // If they do, it's indicative of a much more serious problem.
  216. // XXX DEPRECATED
  217. func PanicCrisis(v interface{}) {
  218. panic(Fmt("Panicked on a Crisis: %v", v))
  219. }
  220. // Indicates a failure of consensus. Someone was malicious or something has
  221. // gone horribly wrong. These should really boot us into an "emergency-recover" mode
  222. // XXX DEPRECATED
  223. func PanicConsensus(v interface{}) {
  224. panic(Fmt("Panicked on a Consensus Failure: %v", v))
  225. }
  226. // For those times when we're not sure if we should panic
  227. // XXX DEPRECATED
  228. func PanicQ(v interface{}) {
  229. panic(Fmt("Panicked questionably: %v", v))
  230. }