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.

262 lines
6.9 KiB

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