encoder_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  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. "strings"
  23. "testing"
  24. "time"
  25. "github.com/stretchr/testify/assert"
  26. "github.com/stretchr/testify/require"
  27. . "go.uber.org/zap/zapcore"
  28. )
  29. var (
  30. _epoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
  31. _testEntry = Entry{
  32. LoggerName: "main",
  33. Level: InfoLevel,
  34. Message: `hello`,
  35. Time: _epoch,
  36. Stack: "fake-stack",
  37. Caller: EntryCaller{Defined: true, File: "foo.go", Line: 42},
  38. }
  39. )
  40. func testEncoderConfig() EncoderConfig {
  41. return EncoderConfig{
  42. MessageKey: "msg",
  43. LevelKey: "level",
  44. NameKey: "name",
  45. TimeKey: "ts",
  46. CallerKey: "caller",
  47. StacktraceKey: "stacktrace",
  48. LineEnding: "\n",
  49. EncodeTime: EpochTimeEncoder,
  50. EncodeLevel: LowercaseLevelEncoder,
  51. EncodeDuration: SecondsDurationEncoder,
  52. EncodeCaller: ShortCallerEncoder,
  53. }
  54. }
  55. func humanEncoderConfig() EncoderConfig {
  56. cfg := testEncoderConfig()
  57. cfg.EncodeTime = ISO8601TimeEncoder
  58. cfg.EncodeLevel = CapitalLevelEncoder
  59. cfg.EncodeDuration = StringDurationEncoder
  60. return cfg
  61. }
  62. func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) {
  63. enc.AppendString(strings.ToUpper(loggerName))
  64. }
  65. func TestEncoderConfiguration(t *testing.T) {
  66. base := testEncoderConfig()
  67. tests := []struct {
  68. desc string
  69. cfg EncoderConfig
  70. amendEntry func(Entry) Entry
  71. extra func(Encoder)
  72. expectedJSON string
  73. expectedConsole string
  74. }{
  75. {
  76. desc: "messages to be escaped",
  77. cfg: base,
  78. amendEntry: func(ent Entry) Entry {
  79. ent.Message = `hello\`
  80. return ent
  81. },
  82. expectedJSON: `{"level":"info","ts":0,"name":"main","caller":"foo.go:42","msg":"hello\\","stacktrace":"fake-stack"}` + "\n",
  83. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\\\nfake-stack\n",
  84. },
  85. {
  86. desc: "use custom entry keys in JSON output and ignore them in console output",
  87. cfg: EncoderConfig{
  88. LevelKey: "L",
  89. TimeKey: "T",
  90. MessageKey: "M",
  91. NameKey: "N",
  92. CallerKey: "C",
  93. StacktraceKey: "S",
  94. LineEnding: base.LineEnding,
  95. EncodeTime: base.EncodeTime,
  96. EncodeDuration: base.EncodeDuration,
  97. EncodeLevel: base.EncodeLevel,
  98. EncodeCaller: base.EncodeCaller,
  99. },
  100. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  101. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\n",
  102. },
  103. {
  104. desc: "skip level if LevelKey is omitted",
  105. cfg: EncoderConfig{
  106. LevelKey: OmitKey,
  107. TimeKey: "T",
  108. MessageKey: "M",
  109. NameKey: "N",
  110. CallerKey: "C",
  111. StacktraceKey: "S",
  112. LineEnding: base.LineEnding,
  113. EncodeTime: base.EncodeTime,
  114. EncodeDuration: base.EncodeDuration,
  115. EncodeLevel: base.EncodeLevel,
  116. EncodeCaller: base.EncodeCaller,
  117. },
  118. expectedJSON: `{"T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  119. expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n",
  120. },
  121. {
  122. desc: "skip timestamp if TimeKey is omitted",
  123. cfg: EncoderConfig{
  124. LevelKey: "L",
  125. TimeKey: OmitKey,
  126. MessageKey: "M",
  127. NameKey: "N",
  128. CallerKey: "C",
  129. StacktraceKey: "S",
  130. LineEnding: base.LineEnding,
  131. EncodeTime: base.EncodeTime,
  132. EncodeDuration: base.EncodeDuration,
  133. EncodeLevel: base.EncodeLevel,
  134. EncodeCaller: base.EncodeCaller,
  135. },
  136. expectedJSON: `{"L":"info","N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  137. expectedConsole: "info\tmain\tfoo.go:42\thello\nfake-stack\n",
  138. },
  139. {
  140. desc: "skip message if MessageKey is omitted",
  141. cfg: EncoderConfig{
  142. LevelKey: "L",
  143. TimeKey: "T",
  144. MessageKey: OmitKey,
  145. NameKey: "N",
  146. CallerKey: "C",
  147. StacktraceKey: "S",
  148. LineEnding: base.LineEnding,
  149. EncodeTime: base.EncodeTime,
  150. EncodeDuration: base.EncodeDuration,
  151. EncodeLevel: base.EncodeLevel,
  152. EncodeCaller: base.EncodeCaller,
  153. },
  154. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","S":"fake-stack"}` + "\n",
  155. expectedConsole: "0\tinfo\tmain\tfoo.go:42\nfake-stack\n",
  156. },
  157. {
  158. desc: "skip name if NameKey is omitted",
  159. cfg: EncoderConfig{
  160. LevelKey: "L",
  161. TimeKey: "T",
  162. MessageKey: "M",
  163. NameKey: OmitKey,
  164. CallerKey: "C",
  165. StacktraceKey: "S",
  166. LineEnding: base.LineEnding,
  167. EncodeTime: base.EncodeTime,
  168. EncodeDuration: base.EncodeDuration,
  169. EncodeLevel: base.EncodeLevel,
  170. EncodeCaller: base.EncodeCaller,
  171. },
  172. expectedJSON: `{"L":"info","T":0,"C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  173. expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n",
  174. },
  175. {
  176. desc: "skip caller if CallerKey is omitted",
  177. cfg: EncoderConfig{
  178. LevelKey: "L",
  179. TimeKey: "T",
  180. MessageKey: "M",
  181. NameKey: "N",
  182. CallerKey: OmitKey,
  183. StacktraceKey: "S",
  184. LineEnding: base.LineEnding,
  185. EncodeTime: base.EncodeTime,
  186. EncodeDuration: base.EncodeDuration,
  187. EncodeLevel: base.EncodeLevel,
  188. EncodeCaller: base.EncodeCaller,
  189. },
  190. expectedJSON: `{"L":"info","T":0,"N":"main","M":"hello","S":"fake-stack"}` + "\n",
  191. expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n",
  192. },
  193. {
  194. desc: "skip stacktrace if StacktraceKey is omitted",
  195. cfg: EncoderConfig{
  196. LevelKey: "L",
  197. TimeKey: "T",
  198. MessageKey: "M",
  199. NameKey: "N",
  200. CallerKey: "C",
  201. StacktraceKey: OmitKey,
  202. LineEnding: base.LineEnding,
  203. EncodeTime: base.EncodeTime,
  204. EncodeDuration: base.EncodeDuration,
  205. EncodeLevel: base.EncodeLevel,
  206. EncodeCaller: base.EncodeCaller,
  207. },
  208. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello"}` + "\n",
  209. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\n",
  210. },
  211. {
  212. desc: "use the supplied EncodeTime, for both the entry and any times added",
  213. cfg: EncoderConfig{
  214. LevelKey: "L",
  215. TimeKey: "T",
  216. MessageKey: "M",
  217. NameKey: "N",
  218. CallerKey: "C",
  219. StacktraceKey: "S",
  220. LineEnding: base.LineEnding,
  221. EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) },
  222. EncodeDuration: base.EncodeDuration,
  223. EncodeLevel: base.EncodeLevel,
  224. EncodeCaller: base.EncodeCaller,
  225. },
  226. extra: func(enc Encoder) {
  227. enc.AddTime("extra", _epoch)
  228. enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
  229. enc.AppendTime(_epoch)
  230. return nil
  231. }))
  232. },
  233. expectedJSON: `{"L":"info","T":"1970-01-01 00:00:00 +0000 UTC","N":"main","C":"foo.go:42","M":"hello","extra":"1970-01-01 00:00:00 +0000 UTC","extras":["1970-01-01 00:00:00 +0000 UTC"],"S":"fake-stack"}` + "\n",
  234. expectedConsole: "1970-01-01 00:00:00 +0000 UTC\tinfo\tmain\tfoo.go:42\thello\t" + // plain-text preamble
  235. `{"extra": "1970-01-01 00:00:00 +0000 UTC", "extras": ["1970-01-01 00:00:00 +0000 UTC"]}` + // JSON context
  236. "\nfake-stack\n", // stacktrace after newline
  237. },
  238. {
  239. desc: "use the supplied EncodeDuration for any durations added",
  240. cfg: EncoderConfig{
  241. LevelKey: "L",
  242. TimeKey: "T",
  243. MessageKey: "M",
  244. NameKey: "N",
  245. CallerKey: "C",
  246. StacktraceKey: "S",
  247. LineEnding: base.LineEnding,
  248. EncodeTime: base.EncodeTime,
  249. EncodeDuration: StringDurationEncoder,
  250. EncodeLevel: base.EncodeLevel,
  251. EncodeCaller: base.EncodeCaller,
  252. },
  253. extra: func(enc Encoder) {
  254. enc.AddDuration("extra", time.Second)
  255. enc.AddArray("extras", ArrayMarshalerFunc(func(enc ArrayEncoder) error {
  256. enc.AppendDuration(time.Minute)
  257. return nil
  258. }))
  259. },
  260. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","extra":"1s","extras":["1m0s"],"S":"fake-stack"}` + "\n",
  261. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + // preamble
  262. `{"extra": "1s", "extras": ["1m0s"]}` + // context
  263. "\nfake-stack\n", // stacktrace
  264. },
  265. {
  266. desc: "use the supplied EncodeLevel",
  267. cfg: EncoderConfig{
  268. LevelKey: "L",
  269. TimeKey: "T",
  270. MessageKey: "M",
  271. NameKey: "N",
  272. CallerKey: "C",
  273. StacktraceKey: "S",
  274. LineEnding: base.LineEnding,
  275. EncodeTime: base.EncodeTime,
  276. EncodeDuration: base.EncodeDuration,
  277. EncodeLevel: CapitalLevelEncoder,
  278. EncodeCaller: base.EncodeCaller,
  279. },
  280. expectedJSON: `{"L":"INFO","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  281. expectedConsole: "0\tINFO\tmain\tfoo.go:42\thello\nfake-stack\n",
  282. },
  283. {
  284. desc: "use the supplied EncodeName",
  285. cfg: EncoderConfig{
  286. LevelKey: "L",
  287. TimeKey: "T",
  288. MessageKey: "M",
  289. NameKey: "N",
  290. CallerKey: "C",
  291. StacktraceKey: "S",
  292. LineEnding: base.LineEnding,
  293. EncodeTime: base.EncodeTime,
  294. EncodeDuration: base.EncodeDuration,
  295. EncodeLevel: base.EncodeLevel,
  296. EncodeCaller: base.EncodeCaller,
  297. EncodeName: capitalNameEncoder,
  298. },
  299. expectedJSON: `{"L":"info","T":0,"N":"MAIN","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  300. expectedConsole: "0\tinfo\tMAIN\tfoo.go:42\thello\nfake-stack\n",
  301. },
  302. {
  303. desc: "close all open namespaces",
  304. cfg: EncoderConfig{
  305. LevelKey: "L",
  306. TimeKey: "T",
  307. MessageKey: "M",
  308. NameKey: "N",
  309. CallerKey: "C",
  310. StacktraceKey: "S",
  311. LineEnding: base.LineEnding,
  312. EncodeTime: base.EncodeTime,
  313. EncodeDuration: base.EncodeDuration,
  314. EncodeLevel: base.EncodeLevel,
  315. EncodeCaller: base.EncodeCaller,
  316. },
  317. extra: func(enc Encoder) {
  318. enc.OpenNamespace("outer")
  319. enc.OpenNamespace("inner")
  320. enc.AddString("foo", "bar")
  321. enc.OpenNamespace("innermost")
  322. },
  323. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","outer":{"inner":{"foo":"bar","innermost":{}}},"S":"fake-stack"}` + "\n",
  324. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" +
  325. `{"outer": {"inner": {"foo": "bar", "innermost": {}}}}` +
  326. "\nfake-stack\n",
  327. },
  328. {
  329. desc: "handle no-op EncodeTime",
  330. cfg: EncoderConfig{
  331. LevelKey: "L",
  332. TimeKey: "T",
  333. MessageKey: "M",
  334. NameKey: "N",
  335. CallerKey: "C",
  336. StacktraceKey: "S",
  337. LineEnding: base.LineEnding,
  338. EncodeTime: func(time.Time, PrimitiveArrayEncoder) {},
  339. EncodeDuration: base.EncodeDuration,
  340. EncodeLevel: base.EncodeLevel,
  341. EncodeCaller: base.EncodeCaller,
  342. },
  343. extra: func(enc Encoder) { enc.AddTime("sometime", time.Unix(0, 100)) },
  344. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","sometime":100,"S":"fake-stack"}` + "\n",
  345. expectedConsole: "info\tmain\tfoo.go:42\thello\t" + `{"sometime": 100}` + "\nfake-stack\n",
  346. },
  347. {
  348. desc: "handle no-op EncodeDuration",
  349. cfg: EncoderConfig{
  350. LevelKey: "L",
  351. TimeKey: "T",
  352. MessageKey: "M",
  353. NameKey: "N",
  354. CallerKey: "C",
  355. StacktraceKey: "S",
  356. LineEnding: base.LineEnding,
  357. EncodeTime: base.EncodeTime,
  358. EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {},
  359. EncodeLevel: base.EncodeLevel,
  360. EncodeCaller: base.EncodeCaller,
  361. },
  362. extra: func(enc Encoder) { enc.AddDuration("someduration", time.Microsecond) },
  363. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n",
  364. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + `{"someduration": 1000}` + "\nfake-stack\n",
  365. },
  366. {
  367. desc: "handle no-op EncodeLevel",
  368. cfg: EncoderConfig{
  369. LevelKey: "L",
  370. TimeKey: "T",
  371. MessageKey: "M",
  372. NameKey: "N",
  373. CallerKey: "C",
  374. StacktraceKey: "S",
  375. LineEnding: base.LineEnding,
  376. EncodeTime: base.EncodeTime,
  377. EncodeDuration: base.EncodeDuration,
  378. EncodeLevel: func(Level, PrimitiveArrayEncoder) {},
  379. EncodeCaller: base.EncodeCaller,
  380. },
  381. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  382. expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n",
  383. },
  384. {
  385. desc: "handle no-op EncodeCaller",
  386. cfg: EncoderConfig{
  387. LevelKey: "L",
  388. TimeKey: "T",
  389. MessageKey: "M",
  390. NameKey: "N",
  391. CallerKey: "C",
  392. StacktraceKey: "S",
  393. LineEnding: base.LineEnding,
  394. EncodeTime: base.EncodeTime,
  395. EncodeDuration: base.EncodeDuration,
  396. EncodeLevel: base.EncodeLevel,
  397. EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {},
  398. },
  399. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  400. expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n",
  401. },
  402. {
  403. desc: "handle no-op EncodeName",
  404. cfg: EncoderConfig{
  405. LevelKey: "L",
  406. TimeKey: "T",
  407. MessageKey: "M",
  408. NameKey: "N",
  409. CallerKey: "C",
  410. StacktraceKey: "S",
  411. LineEnding: base.LineEnding,
  412. EncodeTime: base.EncodeTime,
  413. EncodeDuration: base.EncodeDuration,
  414. EncodeLevel: base.EncodeLevel,
  415. EncodeCaller: base.EncodeCaller,
  416. EncodeName: func(string, PrimitiveArrayEncoder) {},
  417. },
  418. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n",
  419. expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n",
  420. },
  421. {
  422. desc: "use custom line separator",
  423. cfg: EncoderConfig{
  424. LevelKey: "L",
  425. TimeKey: "T",
  426. MessageKey: "M",
  427. NameKey: "N",
  428. CallerKey: "C",
  429. StacktraceKey: "S",
  430. LineEnding: "\r\n",
  431. EncodeTime: base.EncodeTime,
  432. EncodeDuration: base.EncodeDuration,
  433. EncodeLevel: base.EncodeLevel,
  434. EncodeCaller: base.EncodeCaller,
  435. },
  436. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\r\n",
  437. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\r\n",
  438. },
  439. {
  440. desc: "omit line separator definition - fall back to default",
  441. cfg: EncoderConfig{
  442. LevelKey: "L",
  443. TimeKey: "T",
  444. MessageKey: "M",
  445. NameKey: "N",
  446. CallerKey: "C",
  447. StacktraceKey: "S",
  448. EncodeTime: base.EncodeTime,
  449. EncodeDuration: base.EncodeDuration,
  450. EncodeLevel: base.EncodeLevel,
  451. EncodeCaller: base.EncodeCaller,
  452. },
  453. expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + DefaultLineEnding,
  454. expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack" + DefaultLineEnding,
  455. },
  456. }
  457. for i, tt := range tests {
  458. json := NewJSONEncoder(tt.cfg)
  459. console := NewConsoleEncoder(tt.cfg)
  460. if tt.extra != nil {
  461. tt.extra(json)
  462. tt.extra(console)
  463. }
  464. entry := _testEntry
  465. if tt.amendEntry != nil {
  466. entry = tt.amendEntry(_testEntry)
  467. }
  468. jsonOut, jsonErr := json.EncodeEntry(entry, nil)
  469. if assert.NoError(t, jsonErr, "Unexpected error JSON-encoding entry in case #%d.", i) {
  470. assert.Equal(
  471. t,
  472. tt.expectedJSON,
  473. jsonOut.String(),
  474. "Unexpected JSON output: expected to %v.", tt.desc,
  475. )
  476. }
  477. consoleOut, consoleErr := console.EncodeEntry(entry, nil)
  478. if assert.NoError(t, consoleErr, "Unexpected error console-encoding entry in case #%d.", i) {
  479. assert.Equal(
  480. t,
  481. tt.expectedConsole,
  482. consoleOut.String(),
  483. "Unexpected console output: expected to %v.", tt.desc,
  484. )
  485. }
  486. }
  487. }
  488. func TestLevelEncoders(t *testing.T) {
  489. tests := []struct {
  490. name string
  491. expected interface{} // output of encoding InfoLevel
  492. }{
  493. {"capital", "INFO"},
  494. {"lower", "info"},
  495. {"", "info"},
  496. {"something-random", "info"},
  497. }
  498. for _, tt := range tests {
  499. var le LevelEncoder
  500. require.NoError(t, le.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
  501. assertAppended(
  502. t,
  503. tt.expected,
  504. func(arr ArrayEncoder) { le(InfoLevel, arr) },
  505. "Unexpected output serializing InfoLevel with %q.", tt.name,
  506. )
  507. }
  508. }
  509. func TestTimeEncoders(t *testing.T) {
  510. moment := time.Unix(100, 50005000).UTC()
  511. tests := []struct {
  512. name string
  513. expected interface{} // output of serializing moment
  514. }{
  515. {"iso8601", "1970-01-01T00:01:40.050Z"},
  516. {"ISO8601", "1970-01-01T00:01:40.050Z"},
  517. {"millis", 100050.005},
  518. {"nanos", int64(100050005000)},
  519. {"", 100.050005},
  520. {"something-random", 100.050005},
  521. {"rfc3339", "1970-01-01T00:01:40Z"},
  522. {"RFC3339", "1970-01-01T00:01:40Z"},
  523. {"rfc3339nano", "1970-01-01T00:01:40.050005Z"},
  524. {"RFC3339Nano", "1970-01-01T00:01:40.050005Z"},
  525. }
  526. for _, tt := range tests {
  527. var te TimeEncoder
  528. require.NoError(t, te.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
  529. assertAppended(
  530. t,
  531. tt.expected,
  532. func(arr ArrayEncoder) { te(moment, arr) },
  533. "Unexpected output serializing %v with %q.", moment, tt.name,
  534. )
  535. }
  536. }
  537. func TestDurationEncoders(t *testing.T) {
  538. elapsed := time.Second + 500*time.Nanosecond
  539. tests := []struct {
  540. name string
  541. expected interface{} // output of serializing elapsed
  542. }{
  543. {"string", "1.0000005s"},
  544. {"nanos", int64(1000000500)},
  545. {"ms", int64(1000)},
  546. {"", 1.0000005},
  547. {"something-random", 1.0000005},
  548. }
  549. for _, tt := range tests {
  550. var de DurationEncoder
  551. require.NoError(t, de.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
  552. assertAppended(
  553. t,
  554. tt.expected,
  555. func(arr ArrayEncoder) { de(elapsed, arr) },
  556. "Unexpected output serializing %v with %q.", elapsed, tt.name,
  557. )
  558. }
  559. }
  560. func TestCallerEncoders(t *testing.T) {
  561. caller := EntryCaller{Defined: true, File: "/home/jack/src/github.com/foo/foo.go", Line: 42}
  562. tests := []struct {
  563. name string
  564. expected interface{} // output of serializing caller
  565. }{
  566. {"", "foo/foo.go:42"},
  567. {"something-random", "foo/foo.go:42"},
  568. {"short", "foo/foo.go:42"},
  569. {"full", "/home/jack/src/github.com/foo/foo.go:42"},
  570. }
  571. for _, tt := range tests {
  572. var ce CallerEncoder
  573. require.NoError(t, ce.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
  574. assertAppended(
  575. t,
  576. tt.expected,
  577. func(arr ArrayEncoder) { ce(caller, arr) },
  578. "Unexpected output serializing file name as %v with %q.", tt.expected, tt.name,
  579. )
  580. }
  581. }
  582. func TestNameEncoders(t *testing.T) {
  583. tests := []struct {
  584. name string
  585. expected interface{} // output of encoding InfoLevel
  586. }{
  587. {"", "main"},
  588. {"full", "main"},
  589. {"something-random", "main"},
  590. }
  591. for _, tt := range tests {
  592. var ne NameEncoder
  593. require.NoError(t, ne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name)
  594. assertAppended(
  595. t,
  596. tt.expected,
  597. func(arr ArrayEncoder) { ne("main", arr) },
  598. "Unexpected output serializing logger name with %q.", tt.name,
  599. )
  600. }
  601. }
  602. func assertAppended(t testing.TB, expected interface{}, f func(ArrayEncoder), msgAndArgs ...interface{}) {
  603. mem := NewMapObjectEncoder()
  604. mem.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error {
  605. f(arr)
  606. return nil
  607. }))
  608. arr := mem.Fields["k"].([]interface{})
  609. require.Equal(t, 1, len(arr), "Expected to append exactly one element to array.")
  610. assert.Equal(t, expected, arr[0], msgAndArgs...)
  611. }