errors.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package ierrors
  2. import (
  3. "fmt"
  4. "net/http"
  5. "runtime"
  6. "strings"
  7. )
  8. const (
  9. defaultCategory = "default"
  10. )
  11. type Option func(*Error)
  12. type Error struct {
  13. err error
  14. prefix string
  15. statusCode int
  16. publicMessage string
  17. shouldReport bool
  18. category string
  19. stack []uintptr
  20. }
  21. func (e *Error) Error() string {
  22. if len(e.prefix) > 0 {
  23. return fmt.Sprintf("%s: %s", e.prefix, e.err.Error())
  24. }
  25. return e.err.Error()
  26. }
  27. func (e *Error) Unwrap() error {
  28. return e.err
  29. }
  30. func (e *Error) Cause() error {
  31. return e.err
  32. }
  33. func (e *Error) StatusCode() int {
  34. if e.statusCode <= 0 {
  35. return http.StatusInternalServerError
  36. }
  37. return e.statusCode
  38. }
  39. func (e *Error) PublicMessage() string {
  40. if len(e.publicMessage) == 0 {
  41. return "Internal error"
  42. }
  43. return e.publicMessage
  44. }
  45. func (e *Error) ShouldReport() bool {
  46. return e.shouldReport
  47. }
  48. func (e *Error) StackTrace() []uintptr {
  49. return e.stack
  50. }
  51. func (e *Error) Callers() []uintptr {
  52. return e.stack
  53. }
  54. func (e *Error) Category() string {
  55. if e.category == "" {
  56. return defaultCategory
  57. }
  58. return e.category
  59. }
  60. func (e *Error) FormatStackLines() []string {
  61. lines := make([]string, len(e.stack))
  62. for i, pc := range e.stack {
  63. f := runtime.FuncForPC(pc)
  64. file, line := f.FileLine(pc)
  65. lines[i] = fmt.Sprintf("%s:%d %s", file, line, f.Name())
  66. }
  67. return lines
  68. }
  69. func (e *Error) FormatStack() string {
  70. return strings.Join(e.FormatStackLines(), "\n")
  71. }
  72. func Wrap(err error, stackSkip int, opts ...Option) *Error {
  73. if err == nil {
  74. return nil
  75. }
  76. var e *Error
  77. if ierr, ok := err.(*Error); ok {
  78. // if we have some options, we need to copy the error to not modify the original one
  79. if len(opts) > 0 {
  80. ecopy := *ierr
  81. e = &ecopy
  82. } else {
  83. return ierr
  84. }
  85. } else {
  86. e = &Error{
  87. err: err,
  88. shouldReport: true,
  89. }
  90. }
  91. for _, opt := range opts {
  92. opt(e)
  93. }
  94. if len(e.stack) == 0 {
  95. e.stack = callers(stackSkip + 1)
  96. }
  97. return e
  98. }
  99. func WithStatusCode(code int) Option {
  100. return func(e *Error) {
  101. e.statusCode = code
  102. }
  103. }
  104. func WithPublicMessage(msg string) Option {
  105. return func(e *Error) {
  106. e.publicMessage = msg
  107. }
  108. }
  109. func WithPrefix(prefix string) Option {
  110. return func(e *Error) {
  111. if len(e.prefix) > 0 {
  112. e.prefix = fmt.Sprintf("%s: %s", prefix, e.prefix)
  113. } else {
  114. e.prefix = prefix
  115. }
  116. }
  117. }
  118. func WithShouldReport(report bool) Option {
  119. return func(e *Error) {
  120. e.shouldReport = report
  121. }
  122. }
  123. func WithCategory(category string) Option {
  124. return func(e *Error) {
  125. e.category = category
  126. }
  127. }
  128. func callers(skip int) []uintptr {
  129. stack := make([]uintptr, 10)
  130. n := runtime.Callers(skip+2, stack)
  131. return stack[:n]
  132. }