normalize.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Copyright 2020 PingCAP, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package errors
  14. import (
  15. "fmt"
  16. "runtime"
  17. "strconv"
  18. "go.uber.org/atomic"
  19. )
  20. // RedactLogEnabled defines whether the arguments of Error need to be redacted.
  21. var RedactLogEnabled atomic.Bool
  22. // ErrCode represents a specific error type in a error class.
  23. // Same error code can be used in different error classes.
  24. type ErrCode int
  25. // ErrCodeText is a textual error code that represents a specific error type in a error class.
  26. type ErrCodeText string
  27. type ErrorID string
  28. type RFCErrorCode string
  29. // Error is the 'prototype' of a type of errors.
  30. // Use DefineError to make a *Error:
  31. // var ErrUnavailable = errors.Normalize("Region %d is unavailable", errors.RFCCodeText("Unavailable"))
  32. //
  33. // "throw" it at runtime:
  34. // func Somewhat() error {
  35. // ...
  36. // if err != nil {
  37. // // generate a stackful error use the message template at defining,
  38. // // also see FastGen(it's stackless), GenWithStack(it uses custom message template).
  39. // return ErrUnavailable.GenWithStackByArgs(region.ID)
  40. // }
  41. // }
  42. //
  43. // testing whether an error belongs to a prototype:
  44. // if ErrUnavailable.Equal(err) {
  45. // // handle this error.
  46. // }
  47. type Error struct {
  48. code ErrCode
  49. // codeText is the textual describe of the error code
  50. codeText ErrCodeText
  51. // message is a template of the description of this error.
  52. // printf-style formatting is enabled.
  53. message string
  54. // redactArgsPos defines the positions of arguments in message that need to be redacted.
  55. // And it is controlled by the global var RedactLogEnabled.
  56. // For example, an original error is `Duplicate entry 'PRIMARY' for key 'key'`,
  57. // when RedactLogEnabled is ON and redactArgsPos is [0, 1], the error is `Duplicate entry '?' for key '?'`.
  58. redactArgsPos []int
  59. // Cause is used to warp some third party error.
  60. cause error
  61. args []interface{}
  62. file string
  63. line int
  64. }
  65. // Code returns the numeric code of this error.
  66. // ID() will return textual error if there it is,
  67. // when you just want to get the purely numeric error
  68. // (e.g., for mysql protocol transmission.), this would be useful.
  69. func (e *Error) Code() ErrCode {
  70. return e.code
  71. }
  72. // Code returns ErrorCode, by the RFC:
  73. //
  74. // The error code is a 3-tuple of abbreviated component name, error class and error code,
  75. // joined by a colon like {Component}:{ErrorClass}:{InnerErrorCode}.
  76. func (e *Error) RFCCode() RFCErrorCode {
  77. return RFCErrorCode(e.ID())
  78. }
  79. // ID returns the ID of this error.
  80. func (e *Error) ID() ErrorID {
  81. if e.codeText != "" {
  82. return ErrorID(e.codeText)
  83. }
  84. return ErrorID(strconv.Itoa(int(e.code)))
  85. }
  86. // Location returns the location where the error is created,
  87. // implements juju/errors locationer interface.
  88. func (e *Error) Location() (file string, line int) {
  89. return e.file, e.line
  90. }
  91. // MessageTemplate returns the error message template of this error.
  92. func (e *Error) MessageTemplate() string {
  93. return e.message
  94. }
  95. // Error implements error interface.
  96. func (e *Error) Error() string {
  97. if e == nil {
  98. return "<nil>"
  99. }
  100. describe := e.codeText
  101. if len(describe) == 0 {
  102. describe = ErrCodeText(strconv.Itoa(int(e.code)))
  103. }
  104. return fmt.Sprintf("[%s]%s", e.RFCCode(), e.GetMsg())
  105. }
  106. func (e *Error) GetMsg() string {
  107. if len(e.args) > 0 {
  108. return fmt.Sprintf(e.message, e.args...)
  109. }
  110. return e.message
  111. }
  112. func (e *Error) fillLineAndFile(skip int) {
  113. // skip this
  114. _, file, line, ok := runtime.Caller(skip + 1)
  115. if !ok {
  116. e.file = "<unknown>"
  117. e.line = -1
  118. return
  119. }
  120. e.file = file
  121. e.line = line
  122. }
  123. // GenWithStack generates a new *Error with the same class and code, and a new formatted message.
  124. func (e *Error) GenWithStack(format string, args ...interface{}) error {
  125. // TODO: RedactErrorArg
  126. err := *e
  127. err.message = format
  128. err.args = args
  129. err.fillLineAndFile(1)
  130. return AddStack(&err)
  131. }
  132. // GenWithStackByArgs generates a new *Error with the same class and code, and new arguments.
  133. func (e *Error) GenWithStackByArgs(args ...interface{}) error {
  134. RedactErrorArg(args, e.redactArgsPos)
  135. err := *e
  136. err.args = args
  137. err.fillLineAndFile(1)
  138. return AddStack(&err)
  139. }
  140. // FastGen generates a new *Error with the same class and code, and a new formatted message.
  141. // This will not call runtime.Caller to get file and line.
  142. func (e *Error) FastGen(format string, args ...interface{}) error {
  143. // TODO: RedactErrorArg
  144. err := *e
  145. err.message = format
  146. err.args = args
  147. return SuspendStack(&err)
  148. }
  149. // FastGen generates a new *Error with the same class and code, and a new arguments.
  150. // This will not call runtime.Caller to get file and line.
  151. func (e *Error) FastGenByArgs(args ...interface{}) error {
  152. RedactErrorArg(args, e.redactArgsPos)
  153. err := *e
  154. err.args = args
  155. return SuspendStack(&err)
  156. }
  157. // Equal checks if err is equal to e.
  158. func (e *Error) Equal(err error) bool {
  159. originErr := Cause(err)
  160. if originErr == nil {
  161. return false
  162. }
  163. if error(e) == originErr {
  164. return true
  165. }
  166. inErr, ok := originErr.(*Error)
  167. if !ok {
  168. return false
  169. }
  170. idEquals := e.ID() == inErr.ID()
  171. return idEquals
  172. }
  173. // NotEqual checks if err is not equal to e.
  174. func (e *Error) NotEqual(err error) bool {
  175. return !e.Equal(err)
  176. }
  177. // RedactErrorArg redacts the args by position if RedactLogEnabled is enabled.
  178. func RedactErrorArg(args []interface{}, position []int) {
  179. if RedactLogEnabled.Load() {
  180. for _, pos := range position {
  181. if len(args) > pos {
  182. args[pos] = "?"
  183. }
  184. }
  185. }
  186. }
  187. // ErrorEqual returns a boolean indicating whether err1 is equal to err2.
  188. func ErrorEqual(err1, err2 error) bool {
  189. e1 := Cause(err1)
  190. e2 := Cause(err2)
  191. if e1 == e2 {
  192. return true
  193. }
  194. if e1 == nil || e2 == nil {
  195. return e1 == e2
  196. }
  197. te1, ok1 := e1.(*Error)
  198. te2, ok2 := e2.(*Error)
  199. if ok1 && ok2 {
  200. return te1.Equal(te2)
  201. }
  202. return e1.Error() == e2.Error()
  203. }
  204. // ErrorNotEqual returns a boolean indicating whether err1 isn't equal to err2.
  205. func ErrorNotEqual(err1, err2 error) bool {
  206. return !ErrorEqual(err1, err2)
  207. }
  208. type jsonError struct {
  209. // Deprecated field, please use `RFCCode` instead.
  210. Class int `json:"class"`
  211. Code int `json:"code"`
  212. Msg string `json:"message"`
  213. RFCCode string `json:"rfccode"`
  214. }
  215. func (e *Error) Wrap(err error) *Error {
  216. if err != nil {
  217. newErr := *e
  218. newErr.cause = err
  219. return &newErr
  220. }
  221. return nil
  222. }
  223. func (e *Error) Cause() error {
  224. root := Unwrap(e.cause)
  225. if root == nil {
  226. return e.cause
  227. }
  228. return root
  229. }
  230. func (e *Error) FastGenWithCause(args ...interface{}) error {
  231. err := *e
  232. if e.cause != nil {
  233. err.message = e.cause.Error()
  234. }
  235. err.args = args
  236. return SuspendStack(&err)
  237. }
  238. func (e *Error) GenWithStackByCause(args ...interface{}) error {
  239. err := *e
  240. if e.cause != nil {
  241. err.message = e.cause.Error()
  242. }
  243. err.args = args
  244. err.fillLineAndFile(1)
  245. return AddStack(&err)
  246. }
  247. type NormalizeOption func(*Error)
  248. func RedactArgs(pos []int) NormalizeOption {
  249. return func(e *Error) {
  250. e.redactArgsPos = pos
  251. }
  252. }
  253. // RFCCodeText returns a NormalizeOption to set RFC error code.
  254. func RFCCodeText(codeText string) NormalizeOption {
  255. return func(e *Error) {
  256. e.codeText = ErrCodeText(codeText)
  257. }
  258. }
  259. // MySQLErrorCode returns a NormalizeOption to set error code.
  260. func MySQLErrorCode(code int) NormalizeOption {
  261. return func(e *Error) {
  262. e.code = ErrCode(code)
  263. }
  264. }
  265. // Normalize creates a new Error object.
  266. func Normalize(message string, opts ...NormalizeOption) *Error {
  267. e := &Error{
  268. message: message,
  269. }
  270. for _, opt := range opts {
  271. opt(e)
  272. }
  273. return e
  274. }