field_test.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. // Copyright (c) 2016 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. "math"
  25. "net/url"
  26. "testing"
  27. "time"
  28. "github.com/stretchr/testify/assert"
  29. "github.com/stretchr/testify/require"
  30. "go.uber.org/zap"
  31. . "go.uber.org/zap/zapcore"
  32. )
  33. type users int
  34. func (u users) String() string {
  35. return fmt.Sprintf("%d users", int(u))
  36. }
  37. func (u users) MarshalLogObject(enc ObjectEncoder) error {
  38. if int(u) < 0 {
  39. return errors.New("too few users")
  40. }
  41. enc.AddInt("users", int(u))
  42. return nil
  43. }
  44. func (u users) MarshalLogArray(enc ArrayEncoder) error {
  45. if int(u) < 0 {
  46. return errors.New("too few users")
  47. }
  48. for i := 0; i < int(u); i++ {
  49. enc.AppendString("user")
  50. }
  51. return nil
  52. }
  53. type obj struct {
  54. kind int
  55. }
  56. func (o *obj) String() string {
  57. if o == nil {
  58. return "nil obj"
  59. }
  60. if o.kind == 1 {
  61. panic("panic with string")
  62. } else if o.kind == 2 {
  63. panic(errors.New("panic with error"))
  64. } else if o.kind == 3 {
  65. // panic with an arbitrary object that causes a panic itself
  66. // when being converted to a string
  67. panic((*url.URL)(nil))
  68. }
  69. return "obj"
  70. }
  71. func TestUnknownFieldType(t *testing.T) {
  72. unknown := Field{Key: "k", String: "foo"}
  73. assert.Equal(t, UnknownType, unknown.Type, "Expected zero value of FieldType to be UnknownType.")
  74. assert.Panics(t, func() {
  75. unknown.AddTo(NewMapObjectEncoder())
  76. }, "Expected using a field with unknown type to panic.")
  77. }
  78. func TestFieldAddingError(t *testing.T) {
  79. var empty interface{}
  80. tests := []struct {
  81. t FieldType
  82. iface interface{}
  83. want interface{}
  84. err string
  85. }{
  86. {t: ArrayMarshalerType, iface: users(-1), want: []interface{}{}, err: "too few users"},
  87. {t: ObjectMarshalerType, iface: users(-1), want: map[string]interface{}{}, err: "too few users"},
  88. {t: StringerType, iface: obj{}, want: empty, err: "PANIC=interface conversion: zapcore_test.obj is not fmt.Stringer: missing method String"},
  89. {t: StringerType, iface: &obj{1}, want: empty, err: "PANIC=panic with string"},
  90. {t: StringerType, iface: &obj{2}, want: empty, err: "PANIC=panic with error"},
  91. {t: StringerType, iface: &obj{3}, want: empty, err: "PANIC=<nil>"},
  92. {t: StringerType, iface: (*url.URL)(nil), want: empty, err: "PANIC=runtime error: invalid memory address or nil pointer dereference"},
  93. }
  94. for _, tt := range tests {
  95. f := Field{Key: "k", Interface: tt.iface, Type: tt.t}
  96. enc := NewMapObjectEncoder()
  97. assert.NotPanics(t, func() { f.AddTo(enc) }, "Unexpected panic when adding fields returns an error.")
  98. assert.Equal(t, tt.want, enc.Fields["k"], "On error, expected zero value in field.Key.")
  99. assert.Equal(t, tt.err, enc.Fields["kError"], "Expected error message in log context.")
  100. }
  101. }
  102. func TestFields(t *testing.T) {
  103. tests := []struct {
  104. t FieldType
  105. i int64
  106. s string
  107. iface interface{}
  108. want interface{}
  109. }{
  110. {t: ArrayMarshalerType, iface: users(2), want: []interface{}{"user", "user"}},
  111. {t: ObjectMarshalerType, iface: users(2), want: map[string]interface{}{"users": 2}},
  112. {t: BinaryType, iface: []byte("foo"), want: []byte("foo")},
  113. {t: BoolType, i: 0, want: false},
  114. {t: ByteStringType, iface: []byte("foo"), want: "foo"},
  115. {t: Complex128Type, iface: 1 + 2i, want: 1 + 2i},
  116. {t: Complex64Type, iface: complex64(1 + 2i), want: complex64(1 + 2i)},
  117. {t: DurationType, i: 1000, want: time.Microsecond},
  118. {t: Float64Type, i: int64(math.Float64bits(3.14)), want: 3.14},
  119. {t: Float32Type, i: int64(math.Float32bits(3.14)), want: float32(3.14)},
  120. {t: Int64Type, i: 42, want: int64(42)},
  121. {t: Int32Type, i: 42, want: int32(42)},
  122. {t: Int16Type, i: 42, want: int16(42)},
  123. {t: Int8Type, i: 42, want: int8(42)},
  124. {t: StringType, s: "foo", want: "foo"},
  125. {t: TimeType, i: 1000, iface: time.UTC, want: time.Unix(0, 1000).In(time.UTC)},
  126. {t: TimeType, i: 1000, want: time.Unix(0, 1000)},
  127. {t: Uint64Type, i: 42, want: uint64(42)},
  128. {t: Uint32Type, i: 42, want: uint32(42)},
  129. {t: Uint16Type, i: 42, want: uint16(42)},
  130. {t: Uint8Type, i: 42, want: uint8(42)},
  131. {t: UintptrType, i: 42, want: uintptr(42)},
  132. {t: ReflectType, iface: users(2), want: users(2)},
  133. {t: NamespaceType, want: map[string]interface{}{}},
  134. {t: StringerType, iface: users(2), want: "2 users"},
  135. {t: StringerType, iface: &obj{}, want: "obj"},
  136. {t: StringerType, iface: (*obj)(nil), want: "nil obj"},
  137. {t: SkipType, want: interface{}(nil)},
  138. }
  139. for _, tt := range tests {
  140. enc := NewMapObjectEncoder()
  141. f := Field{Key: "k", Type: tt.t, Integer: tt.i, Interface: tt.iface, String: tt.s}
  142. f.AddTo(enc)
  143. assert.Equal(t, tt.want, enc.Fields["k"], "Unexpected output from field %+v.", f)
  144. delete(enc.Fields, "k")
  145. assert.Equal(t, 0, len(enc.Fields), "Unexpected extra fields present.")
  146. assert.True(t, f.Equals(f), "Field does not equal itself")
  147. }
  148. }
  149. func TestEquals(t *testing.T) {
  150. // Values outside the UnixNano range were encoded incorrectly (#737, #803).
  151. timeOutOfRangeHigh := time.Unix(0, math.MaxInt64).Add(time.Nanosecond)
  152. timeOutOfRangeLow := time.Unix(0, math.MinInt64).Add(-time.Nanosecond)
  153. timeOutOfRangeHighNano := time.Unix(0, timeOutOfRangeHigh.UnixNano())
  154. timeOutOfRangeLowNano := time.Unix(0, timeOutOfRangeLow.UnixNano())
  155. require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is > UnixNano range")
  156. require.False(t, timeOutOfRangeHigh.Equal(timeOutOfRangeHighNano), "should be different as value is < UnixNano range")
  157. tests := []struct {
  158. a, b Field
  159. want bool
  160. }{
  161. {
  162. a: zap.Int16("a", 1),
  163. b: zap.Int32("a", 1),
  164. want: false,
  165. },
  166. {
  167. a: zap.String("k", "a"),
  168. b: zap.String("k", "a"),
  169. want: true,
  170. },
  171. {
  172. a: zap.String("k", "a"),
  173. b: zap.String("k2", "a"),
  174. want: false,
  175. },
  176. {
  177. a: zap.String("k", "a"),
  178. b: zap.String("k", "b"),
  179. want: false,
  180. },
  181. {
  182. a: zap.Time("k", time.Unix(1000, 1000)),
  183. b: zap.Time("k", time.Unix(1000, 1000)),
  184. want: true,
  185. },
  186. {
  187. a: zap.Time("k", time.Unix(1000, 1000).In(time.UTC)),
  188. b: zap.Time("k", time.Unix(1000, 1000).In(time.FixedZone("TEST", -8))),
  189. want: false,
  190. },
  191. {
  192. a: zap.Time("k", timeOutOfRangeLow),
  193. b: zap.Time("k", timeOutOfRangeLowNano),
  194. want: false,
  195. },
  196. {
  197. a: zap.Time("k", timeOutOfRangeHigh),
  198. b: zap.Time("k", timeOutOfRangeHighNano),
  199. want: false,
  200. },
  201. {
  202. a: zap.Time("k", time.Unix(1000, 1000)),
  203. b: zap.Time("k", time.Unix(1000, 2000)),
  204. want: false,
  205. },
  206. {
  207. a: zap.Binary("k", []byte{1, 2}),
  208. b: zap.Binary("k", []byte{1, 2}),
  209. want: true,
  210. },
  211. {
  212. a: zap.Binary("k", []byte{1, 2}),
  213. b: zap.Binary("k", []byte{1, 3}),
  214. want: false,
  215. },
  216. {
  217. a: zap.ByteString("k", []byte("abc")),
  218. b: zap.ByteString("k", []byte("abc")),
  219. want: true,
  220. },
  221. {
  222. a: zap.ByteString("k", []byte("abc")),
  223. b: zap.ByteString("k", []byte("abd")),
  224. want: false,
  225. },
  226. {
  227. a: zap.Ints("k", []int{1, 2}),
  228. b: zap.Ints("k", []int{1, 2}),
  229. want: true,
  230. },
  231. {
  232. a: zap.Ints("k", []int{1, 2}),
  233. b: zap.Ints("k", []int{1, 3}),
  234. want: false,
  235. },
  236. {
  237. a: zap.Object("k", users(10)),
  238. b: zap.Object("k", users(10)),
  239. want: true,
  240. },
  241. {
  242. a: zap.Object("k", users(10)),
  243. b: zap.Object("k", users(20)),
  244. want: false,
  245. },
  246. {
  247. a: zap.Any("k", map[string]string{"a": "b"}),
  248. b: zap.Any("k", map[string]string{"a": "b"}),
  249. want: true,
  250. },
  251. {
  252. a: zap.Any("k", map[string]string{"a": "b"}),
  253. b: zap.Any("k", map[string]string{"a": "d"}),
  254. want: false,
  255. },
  256. }
  257. for _, tt := range tests {
  258. assert.Equal(t, tt.want, tt.a.Equals(tt.b), "a.Equals(b) a: %#v b: %#v", tt.a, tt.b)
  259. assert.Equal(t, tt.want, tt.b.Equals(tt.a), "b.Equals(a) a: %#v b: %#v", tt.a, tt.b)
  260. }
  261. }