logs_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. package logx
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "runtime"
  10. "strings"
  11. "sync"
  12. "sync/atomic"
  13. "testing"
  14. "time"
  15. "github.com/stretchr/testify/assert"
  16. )
  17. var (
  18. s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection")
  19. pool = make(chan []byte, 1)
  20. _ Writer = (*mockWriter)(nil)
  21. )
  22. type mockWriter struct {
  23. lock sync.Mutex
  24. builder strings.Builder
  25. }
  26. func (mw *mockWriter) Alert(v interface{}) {
  27. mw.lock.Lock()
  28. defer mw.lock.Unlock()
  29. output(&mw.builder, levelAlert, v)
  30. }
  31. func (mw *mockWriter) Error(v interface{}, fields ...LogField) {
  32. mw.lock.Lock()
  33. defer mw.lock.Unlock()
  34. output(&mw.builder, levelError, v, fields...)
  35. }
  36. func (mw *mockWriter) Info(v interface{}, fields ...LogField) {
  37. mw.lock.Lock()
  38. defer mw.lock.Unlock()
  39. output(&mw.builder, levelInfo, v, fields...)
  40. }
  41. func (mw *mockWriter) Severe(v interface{}) {
  42. mw.lock.Lock()
  43. defer mw.lock.Unlock()
  44. output(&mw.builder, levelSevere, v)
  45. }
  46. func (mw *mockWriter) Slow(v interface{}, fields ...LogField) {
  47. mw.lock.Lock()
  48. defer mw.lock.Unlock()
  49. output(&mw.builder, levelSlow, v, fields...)
  50. }
  51. func (mw *mockWriter) Stack(v interface{}) {
  52. mw.lock.Lock()
  53. defer mw.lock.Unlock()
  54. output(&mw.builder, levelError, v)
  55. }
  56. func (mw *mockWriter) Stat(v interface{}, fields ...LogField) {
  57. mw.lock.Lock()
  58. defer mw.lock.Unlock()
  59. output(&mw.builder, levelStat, v, fields...)
  60. }
  61. func (mw *mockWriter) Close() error {
  62. return nil
  63. }
  64. func (mw *mockWriter) Contains(text string) bool {
  65. mw.lock.Lock()
  66. defer mw.lock.Unlock()
  67. return strings.Contains(mw.builder.String(), text)
  68. }
  69. func (mw *mockWriter) Reset() {
  70. mw.lock.Lock()
  71. defer mw.lock.Unlock()
  72. mw.builder.Reset()
  73. }
  74. func (mw *mockWriter) String() string {
  75. mw.lock.Lock()
  76. defer mw.lock.Unlock()
  77. return mw.builder.String()
  78. }
  79. func TestFileLineFileMode(t *testing.T) {
  80. w := new(mockWriter)
  81. old := writer.Swap(w)
  82. defer writer.Store(old)
  83. file, line := getFileLine()
  84. Error("anything")
  85. assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  86. file, line = getFileLine()
  87. Errorf("anything %s", "format")
  88. assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  89. }
  90. func TestFileLineConsoleMode(t *testing.T) {
  91. w := new(mockWriter)
  92. old := writer.Swap(w)
  93. defer writer.Store(old)
  94. file, line := getFileLine()
  95. Error("anything")
  96. assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  97. w.Reset()
  98. file, line = getFileLine()
  99. Errorf("anything %s", "format")
  100. assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  101. }
  102. func TestStructedLogAlert(t *testing.T) {
  103. w := new(mockWriter)
  104. old := writer.Swap(w)
  105. defer writer.Store(old)
  106. doTestStructedLog(t, levelAlert, w, func(v ...interface{}) {
  107. Alert(fmt.Sprint(v...))
  108. })
  109. }
  110. func TestStructedLogError(t *testing.T) {
  111. w := new(mockWriter)
  112. old := writer.Swap(w)
  113. defer writer.Store(old)
  114. doTestStructedLog(t, levelError, w, func(v ...interface{}) {
  115. Error(v...)
  116. })
  117. }
  118. func TestStructedLogErrorf(t *testing.T) {
  119. w := new(mockWriter)
  120. old := writer.Swap(w)
  121. defer writer.Store(old)
  122. doTestStructedLog(t, levelError, w, func(v ...interface{}) {
  123. Errorf("%s", fmt.Sprint(v...))
  124. })
  125. }
  126. func TestStructedLogErrorv(t *testing.T) {
  127. w := new(mockWriter)
  128. old := writer.Swap(w)
  129. defer writer.Store(old)
  130. doTestStructedLog(t, levelError, w, func(v ...interface{}) {
  131. Errorv(fmt.Sprint(v...))
  132. })
  133. }
  134. func TestStructedLogErrorw(t *testing.T) {
  135. w := new(mockWriter)
  136. old := writer.Swap(w)
  137. defer writer.Store(old)
  138. doTestStructedLog(t, levelError, w, func(v ...interface{}) {
  139. Errorw(fmt.Sprint(v...), Field("foo", "bar"))
  140. })
  141. }
  142. func TestStructedLogInfo(t *testing.T) {
  143. w := new(mockWriter)
  144. old := writer.Swap(w)
  145. defer writer.Store(old)
  146. doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
  147. Info(v...)
  148. })
  149. }
  150. func TestStructedLogInfof(t *testing.T) {
  151. w := new(mockWriter)
  152. old := writer.Swap(w)
  153. defer writer.Store(old)
  154. doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
  155. Infof("%s", fmt.Sprint(v...))
  156. })
  157. }
  158. func TestStructedLogInfov(t *testing.T) {
  159. w := new(mockWriter)
  160. old := writer.Swap(w)
  161. defer writer.Store(old)
  162. doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
  163. Infov(fmt.Sprint(v...))
  164. })
  165. }
  166. func TestStructedLogInfow(t *testing.T) {
  167. w := new(mockWriter)
  168. old := writer.Swap(w)
  169. defer writer.Store(old)
  170. doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
  171. Infow(fmt.Sprint(v...), Field("foo", "bar"))
  172. })
  173. }
  174. func TestStructedLogInfoConsoleAny(t *testing.T) {
  175. w := new(mockWriter)
  176. old := writer.Swap(w)
  177. defer writer.Store(old)
  178. doTestStructedLogConsole(t, w, func(v ...interface{}) {
  179. old := atomic.LoadUint32(&encoding)
  180. atomic.StoreUint32(&encoding, plainEncodingType)
  181. defer func() {
  182. atomic.StoreUint32(&encoding, old)
  183. }()
  184. Infov(v)
  185. })
  186. }
  187. func TestStructedLogInfoConsoleAnyString(t *testing.T) {
  188. w := new(mockWriter)
  189. old := writer.Swap(w)
  190. defer writer.Store(old)
  191. doTestStructedLogConsole(t, w, func(v ...interface{}) {
  192. old := atomic.LoadUint32(&encoding)
  193. atomic.StoreUint32(&encoding, plainEncodingType)
  194. defer func() {
  195. atomic.StoreUint32(&encoding, old)
  196. }()
  197. Infov(fmt.Sprint(v...))
  198. })
  199. }
  200. func TestStructedLogInfoConsoleAnyError(t *testing.T) {
  201. w := new(mockWriter)
  202. old := writer.Swap(w)
  203. defer writer.Store(old)
  204. doTestStructedLogConsole(t, w, func(v ...interface{}) {
  205. old := atomic.LoadUint32(&encoding)
  206. atomic.StoreUint32(&encoding, plainEncodingType)
  207. defer func() {
  208. atomic.StoreUint32(&encoding, old)
  209. }()
  210. Infov(errors.New(fmt.Sprint(v...)))
  211. })
  212. }
  213. func TestStructedLogInfoConsoleAnyStringer(t *testing.T) {
  214. w := new(mockWriter)
  215. old := writer.Swap(w)
  216. defer writer.Store(old)
  217. doTestStructedLogConsole(t, w, func(v ...interface{}) {
  218. old := atomic.LoadUint32(&encoding)
  219. atomic.StoreUint32(&encoding, plainEncodingType)
  220. defer func() {
  221. atomic.StoreUint32(&encoding, old)
  222. }()
  223. Infov(ValStringer{
  224. val: fmt.Sprint(v...),
  225. })
  226. })
  227. }
  228. func TestStructedLogInfoConsoleText(t *testing.T) {
  229. w := new(mockWriter)
  230. old := writer.Swap(w)
  231. defer writer.Store(old)
  232. doTestStructedLogConsole(t, w, func(v ...interface{}) {
  233. old := atomic.LoadUint32(&encoding)
  234. atomic.StoreUint32(&encoding, plainEncodingType)
  235. defer func() {
  236. atomic.StoreUint32(&encoding, old)
  237. }()
  238. Info(fmt.Sprint(v...))
  239. })
  240. }
  241. func TestStructedLogSlow(t *testing.T) {
  242. w := new(mockWriter)
  243. old := writer.Swap(w)
  244. defer writer.Store(old)
  245. doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
  246. Slow(v...)
  247. })
  248. }
  249. func TestStructedLogSlowf(t *testing.T) {
  250. w := new(mockWriter)
  251. old := writer.Swap(w)
  252. defer writer.Store(old)
  253. doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
  254. Slowf(fmt.Sprint(v...))
  255. })
  256. }
  257. func TestStructedLogSlowv(t *testing.T) {
  258. w := new(mockWriter)
  259. old := writer.Swap(w)
  260. defer writer.Store(old)
  261. doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
  262. Slowv(fmt.Sprint(v...))
  263. })
  264. }
  265. func TestStructedLogSloww(t *testing.T) {
  266. w := new(mockWriter)
  267. old := writer.Swap(w)
  268. defer writer.Store(old)
  269. doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
  270. Sloww(fmt.Sprint(v...), Field("foo", time.Second))
  271. })
  272. }
  273. func TestStructedLogStat(t *testing.T) {
  274. w := new(mockWriter)
  275. old := writer.Swap(w)
  276. defer writer.Store(old)
  277. doTestStructedLog(t, levelStat, w, func(v ...interface{}) {
  278. Stat(v...)
  279. })
  280. }
  281. func TestStructedLogStatf(t *testing.T) {
  282. w := new(mockWriter)
  283. old := writer.Swap(w)
  284. defer writer.Store(old)
  285. doTestStructedLog(t, levelStat, w, func(v ...interface{}) {
  286. Statf(fmt.Sprint(v...))
  287. })
  288. }
  289. func TestStructedLogSevere(t *testing.T) {
  290. w := new(mockWriter)
  291. old := writer.Swap(w)
  292. defer writer.Store(old)
  293. doTestStructedLog(t, levelSevere, w, func(v ...interface{}) {
  294. Severe(v...)
  295. })
  296. }
  297. func TestStructedLogSeveref(t *testing.T) {
  298. w := new(mockWriter)
  299. old := writer.Swap(w)
  300. defer writer.Store(old)
  301. doTestStructedLog(t, levelSevere, w, func(v ...interface{}) {
  302. Severef(fmt.Sprint(v...))
  303. })
  304. }
  305. func TestStructedLogWithDuration(t *testing.T) {
  306. const message = "hello there"
  307. w := new(mockWriter)
  308. old := writer.Swap(w)
  309. defer writer.Store(old)
  310. WithDuration(time.Second).Info(message)
  311. var entry logEntry
  312. if err := json.Unmarshal([]byte(w.builder.String()), &entry); err != nil {
  313. t.Error(err)
  314. }
  315. assert.Equal(t, levelInfo, entry.Level)
  316. assert.Equal(t, message, entry.Content)
  317. assert.Equal(t, "1000.0ms", entry.Duration)
  318. }
  319. func TestSetLevel(t *testing.T) {
  320. SetLevel(ErrorLevel)
  321. const message = "hello there"
  322. w := new(mockWriter)
  323. old := writer.Swap(w)
  324. defer writer.Store(old)
  325. Info(message)
  326. assert.Equal(t, 0, w.builder.Len())
  327. }
  328. func TestSetLevelTwiceWithMode(t *testing.T) {
  329. testModes := []string{
  330. "mode",
  331. "console",
  332. "volumn",
  333. }
  334. w := new(mockWriter)
  335. old := writer.Swap(w)
  336. defer writer.Store(old)
  337. for _, mode := range testModes {
  338. testSetLevelTwiceWithMode(t, mode, w)
  339. }
  340. }
  341. func TestSetLevelWithDuration(t *testing.T) {
  342. SetLevel(ErrorLevel)
  343. const message = "hello there"
  344. w := new(mockWriter)
  345. old := writer.Swap(w)
  346. defer writer.Store(old)
  347. WithDuration(time.Second).Info(message)
  348. assert.Equal(t, 0, w.builder.Len())
  349. }
  350. func TestErrorfWithWrappedError(t *testing.T) {
  351. SetLevel(ErrorLevel)
  352. const message = "there"
  353. w := new(mockWriter)
  354. old := writer.Swap(w)
  355. defer writer.Store(old)
  356. Errorf("hello %w", errors.New(message))
  357. assert.True(t, strings.Contains(w.builder.String(), "hello there"))
  358. }
  359. func TestMustNil(t *testing.T) {
  360. Must(nil)
  361. }
  362. func TestSetup(t *testing.T) {
  363. defer func() {
  364. SetLevel(InfoLevel)
  365. atomic.StoreUint32(&encoding, jsonEncodingType)
  366. }()
  367. MustSetup(LogConf{
  368. ServiceName: "any",
  369. Mode: "console",
  370. })
  371. MustSetup(LogConf{
  372. ServiceName: "any",
  373. Mode: "file",
  374. Path: os.TempDir(),
  375. })
  376. MustSetup(LogConf{
  377. ServiceName: "any",
  378. Mode: "volume",
  379. Path: os.TempDir(),
  380. })
  381. MustSetup(LogConf{
  382. ServiceName: "any",
  383. Mode: "console",
  384. TimeFormat: timeFormat,
  385. })
  386. MustSetup(LogConf{
  387. ServiceName: "any",
  388. Mode: "console",
  389. Encoding: plainEncoding,
  390. })
  391. assert.NotNil(t, setupWithVolume(LogConf{}))
  392. assert.NotNil(t, setupWithFiles(LogConf{}))
  393. assert.Nil(t, setupWithFiles(LogConf{
  394. ServiceName: "any",
  395. Path: os.TempDir(),
  396. Compress: true,
  397. KeepDays: 1,
  398. }))
  399. setupLogLevel(LogConf{
  400. Level: levelInfo,
  401. })
  402. setupLogLevel(LogConf{
  403. Level: levelError,
  404. })
  405. setupLogLevel(LogConf{
  406. Level: levelSevere,
  407. })
  408. _, err := createOutput("")
  409. assert.NotNil(t, err)
  410. Disable()
  411. SetLevel(InfoLevel)
  412. atomic.StoreUint32(&encoding, jsonEncodingType)
  413. }
  414. func TestDisable(t *testing.T) {
  415. Disable()
  416. var opt logOptions
  417. WithKeepDays(1)(&opt)
  418. WithGzip()(&opt)
  419. assert.Nil(t, Close())
  420. assert.Nil(t, Close())
  421. }
  422. func TestDisableStat(t *testing.T) {
  423. DisableStat()
  424. const message = "hello there"
  425. w := new(mockWriter)
  426. old := writer.Swap(w)
  427. defer writer.Store(old)
  428. Stat(message)
  429. assert.Equal(t, 0, w.builder.Len())
  430. }
  431. func TestSetWriter(t *testing.T) {
  432. Reset()
  433. SetWriter(nopWriter{})
  434. assert.NotNil(t, writer.Load())
  435. assert.True(t, writer.Load() == nopWriter{})
  436. SetWriter(new(mockWriter))
  437. assert.True(t, writer.Load() == nopWriter{})
  438. }
  439. func TestWithGzip(t *testing.T) {
  440. fn := WithGzip()
  441. var opt logOptions
  442. fn(&opt)
  443. assert.True(t, opt.gzipEnabled)
  444. }
  445. func TestWithKeepDays(t *testing.T) {
  446. fn := WithKeepDays(1)
  447. var opt logOptions
  448. fn(&opt)
  449. assert.Equal(t, 1, opt.keepDays)
  450. }
  451. func BenchmarkCopyByteSliceAppend(b *testing.B) {
  452. for i := 0; i < b.N; i++ {
  453. var buf []byte
  454. buf = append(buf, getTimestamp()...)
  455. buf = append(buf, ' ')
  456. buf = append(buf, s...)
  457. _ = buf
  458. }
  459. }
  460. func BenchmarkCopyByteSliceAllocExactly(b *testing.B) {
  461. for i := 0; i < b.N; i++ {
  462. now := []byte(getTimestamp())
  463. buf := make([]byte, len(now)+1+len(s))
  464. n := copy(buf, now)
  465. buf[n] = ' '
  466. copy(buf[n+1:], s)
  467. }
  468. }
  469. func BenchmarkCopyByteSlice(b *testing.B) {
  470. var buf []byte
  471. for i := 0; i < b.N; i++ {
  472. buf = make([]byte, len(s))
  473. copy(buf, s)
  474. }
  475. fmt.Fprint(ioutil.Discard, buf)
  476. }
  477. func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
  478. var buf []byte
  479. for i := 0; i < b.N; i++ {
  480. size := len(s)
  481. buf = s[:size:size]
  482. }
  483. fmt.Fprint(ioutil.Discard, buf)
  484. }
  485. func BenchmarkCacheByteSlice(b *testing.B) {
  486. for i := 0; i < b.N; i++ {
  487. dup := fetch()
  488. copy(dup, s)
  489. put(dup)
  490. }
  491. }
  492. func BenchmarkLogs(b *testing.B) {
  493. b.ReportAllocs()
  494. log.SetOutput(ioutil.Discard)
  495. for i := 0; i < b.N; i++ {
  496. Info(i)
  497. }
  498. }
  499. func fetch() []byte {
  500. select {
  501. case b := <-pool:
  502. return b
  503. default:
  504. }
  505. return make([]byte, 4096)
  506. }
  507. func getFileLine() (string, int) {
  508. _, file, line, _ := runtime.Caller(1)
  509. short := file
  510. for i := len(file) - 1; i > 0; i-- {
  511. if file[i] == '/' {
  512. short = file[i+1:]
  513. break
  514. }
  515. }
  516. return short, line
  517. }
  518. func put(b []byte) {
  519. select {
  520. case pool <- b:
  521. default:
  522. }
  523. }
  524. func doTestStructedLog(t *testing.T, level string, w *mockWriter, write func(...interface{})) {
  525. const message = "hello there"
  526. write(message)
  527. fmt.Println(w.String())
  528. var entry logEntry
  529. if err := json.Unmarshal([]byte(w.String()), &entry); err != nil {
  530. t.Error(err)
  531. }
  532. assert.Equal(t, level, entry.Level)
  533. val, ok := entry.Content.(string)
  534. assert.True(t, ok)
  535. assert.True(t, strings.Contains(val, message))
  536. }
  537. func doTestStructedLogConsole(t *testing.T, w *mockWriter, write func(...interface{})) {
  538. const message = "hello there"
  539. write(message)
  540. assert.True(t, strings.Contains(w.String(), message))
  541. }
  542. func testSetLevelTwiceWithMode(t *testing.T, mode string, w *mockWriter) {
  543. writer.Store(nil)
  544. SetUp(LogConf{
  545. Mode: mode,
  546. Level: "error",
  547. Path: "/dev/null",
  548. })
  549. SetUp(LogConf{
  550. Mode: mode,
  551. Level: "info",
  552. Path: "/dev/null",
  553. })
  554. const message = "hello there"
  555. Info(message)
  556. assert.Equal(t, 0, w.builder.Len())
  557. Infof(message)
  558. assert.Equal(t, 0, w.builder.Len())
  559. ErrorStack(message)
  560. assert.Equal(t, 0, w.builder.Len())
  561. ErrorStackf(message)
  562. assert.Equal(t, 0, w.builder.Len())
  563. }
  564. type ValStringer struct {
  565. val string
  566. }
  567. func (v ValStringer) String() string {
  568. return v.val
  569. }