error_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright (c) 2017 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package zapcore_test
  21. import (
  22. "errors"
  23. "fmt"
  24. "io"
  25. "testing"
  26. richErrors "github.com/pkg/errors"
  27. "github.com/stretchr/testify/assert"
  28. "go.uber.org/multierr"
  29. . "go.uber.org/zap/zapcore"
  30. )
  31. type errTooManyUsers int
  32. func (e errTooManyUsers) Error() string {
  33. return fmt.Sprintf("%d too many users", int(e))
  34. }
  35. func (e errTooManyUsers) Format(s fmt.State, verb rune) {
  36. // Implement fmt.Formatter, but don't add any information beyond the basic
  37. // Error method.
  38. if verb == 'v' && s.Flag('+') {
  39. io.WriteString(s, e.Error())
  40. }
  41. }
  42. type customMultierr struct{}
  43. func (e customMultierr) Error() string {
  44. return "great sadness"
  45. }
  46. func (e customMultierr) Errors() []error {
  47. return []error{
  48. errors.New("foo"),
  49. nil,
  50. multierr.Append(
  51. errors.New("bar"),
  52. errors.New("baz"),
  53. ),
  54. }
  55. }
  56. func TestErrorEncoding(t *testing.T) {
  57. tests := []struct {
  58. k string
  59. t FieldType // defaults to ErrorType
  60. iface interface{}
  61. want map[string]interface{}
  62. }{
  63. {
  64. k: "k",
  65. iface: errTooManyUsers(2),
  66. want: map[string]interface{}{
  67. "k": "2 too many users",
  68. },
  69. },
  70. {
  71. k: "err",
  72. iface: multierr.Combine(
  73. errors.New("foo"),
  74. errors.New("bar"),
  75. errors.New("baz"),
  76. ),
  77. want: map[string]interface{}{
  78. "err": "foo; bar; baz",
  79. "errCauses": []interface{}{
  80. map[string]interface{}{"error": "foo"},
  81. map[string]interface{}{"error": "bar"},
  82. map[string]interface{}{"error": "baz"},
  83. },
  84. },
  85. },
  86. {
  87. k: "e",
  88. iface: customMultierr{},
  89. want: map[string]interface{}{
  90. "e": "great sadness",
  91. "eCauses": []interface{}{
  92. map[string]interface{}{"error": "foo"},
  93. map[string]interface{}{
  94. "error": "bar; baz",
  95. "errorCauses": []interface{}{
  96. map[string]interface{}{"error": "bar"},
  97. map[string]interface{}{"error": "baz"},
  98. },
  99. },
  100. },
  101. },
  102. },
  103. {
  104. k: "k",
  105. iface: richErrors.WithMessage(errors.New("egad"), "failed"),
  106. want: map[string]interface{}{
  107. "k": "failed: egad",
  108. "kVerbose": "egad\nfailed",
  109. },
  110. },
  111. {
  112. k: "error",
  113. iface: multierr.Combine(
  114. richErrors.WithMessage(
  115. multierr.Combine(errors.New("foo"), errors.New("bar")),
  116. "hello",
  117. ),
  118. errors.New("baz"),
  119. richErrors.WithMessage(errors.New("qux"), "world"),
  120. ),
  121. want: map[string]interface{}{
  122. "error": "hello: foo; bar; baz; world: qux",
  123. "errorCauses": []interface{}{
  124. map[string]interface{}{
  125. "error": "hello: foo; bar",
  126. "errorVerbose": "the following errors occurred:\n" +
  127. " - foo\n" +
  128. " - bar\n" +
  129. "hello",
  130. },
  131. map[string]interface{}{"error": "baz"},
  132. map[string]interface{}{"error": "world: qux", "errorVerbose": "qux\nworld"},
  133. },
  134. },
  135. },
  136. }
  137. for _, tt := range tests {
  138. if tt.t == UnknownType {
  139. tt.t = ErrorType
  140. }
  141. enc := NewMapObjectEncoder()
  142. f := Field{Key: tt.k, Type: tt.t, Interface: tt.iface}
  143. f.AddTo(enc)
  144. assert.Equal(t, tt.want, enc.Fields, "Unexpected output from field %+v.", f)
  145. }
  146. }
  147. func TestRichErrorSupport(t *testing.T) {
  148. f := Field{
  149. Type: ErrorType,
  150. Interface: richErrors.WithMessage(richErrors.New("egad"), "failed"),
  151. Key: "k",
  152. }
  153. enc := NewMapObjectEncoder()
  154. f.AddTo(enc)
  155. assert.Equal(t, "failed: egad", enc.Fields["k"], "Unexpected basic error message.")
  156. serialized := enc.Fields["kVerbose"]
  157. // Don't assert the exact format used by a third-party package, but ensure
  158. // that some critical elements are present.
  159. assert.Regexp(t, `egad`, serialized, "Expected original error message to be present.")
  160. assert.Regexp(t, `failed`, serialized, "Expected error annotation to be present.")
  161. assert.Regexp(t, `TestRichErrorSupport`, serialized, "Expected calling function to be present in stacktrace.")
  162. }