logs_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. package logx
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "runtime"
  9. "strings"
  10. "sync/atomic"
  11. "testing"
  12. "time"
  13. "github.com/stretchr/testify/assert"
  14. )
  15. var (
  16. s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection")
  17. pool = make(chan []byte, 1)
  18. )
  19. type mockWriter struct {
  20. builder strings.Builder
  21. }
  22. func (mw *mockWriter) Write(data []byte) (int, error) {
  23. return mw.builder.Write(data)
  24. }
  25. func (mw *mockWriter) Close() error {
  26. return nil
  27. }
  28. func (mw *mockWriter) Reset() {
  29. mw.builder.Reset()
  30. }
  31. func (mw *mockWriter) Contains(text string) bool {
  32. return strings.Contains(mw.builder.String(), text)
  33. }
  34. func TestFileLineFileMode(t *testing.T) {
  35. writer := new(mockWriter)
  36. errorLog = writer
  37. atomic.StoreUint32(&initialized, 1)
  38. file, line := getFileLine()
  39. Error("anything")
  40. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  41. writer.Reset()
  42. file, line = getFileLine()
  43. Errorf("anything %s", "format")
  44. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  45. }
  46. func TestFileLineConsoleMode(t *testing.T) {
  47. writer := new(mockWriter)
  48. writeConsole = true
  49. errorLog = newLogWriter(log.New(writer, "[ERROR] ", flags))
  50. atomic.StoreUint32(&initialized, 1)
  51. file, line := getFileLine()
  52. Error("anything")
  53. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  54. writer.Reset()
  55. file, line = getFileLine()
  56. Errorf("anything %s", "format")
  57. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  58. }
  59. func TestStructedLogInfo(t *testing.T) {
  60. doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
  61. infoLog = writer
  62. }, func(v ...interface{}) {
  63. Info(v...)
  64. })
  65. }
  66. func TestStructedLogSlow(t *testing.T) {
  67. doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
  68. slowLog = writer
  69. }, func(v ...interface{}) {
  70. Slow(v...)
  71. })
  72. }
  73. func TestStructedLogWithDuration(t *testing.T) {
  74. const message = "hello there"
  75. writer := new(mockWriter)
  76. infoLog = writer
  77. atomic.StoreUint32(&initialized, 1)
  78. WithDuration(time.Second).Info(message)
  79. var entry logEntry
  80. if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
  81. t.Error(err)
  82. }
  83. assert.Equal(t, levelInfo, entry.Level)
  84. assert.Equal(t, message, entry.Content)
  85. assert.Equal(t, "1000.0ms", entry.Duration)
  86. }
  87. func TestSetLevel(t *testing.T) {
  88. SetLevel(ErrorLevel)
  89. const message = "hello there"
  90. writer := new(mockWriter)
  91. infoLog = writer
  92. atomic.StoreUint32(&initialized, 1)
  93. Info(message)
  94. assert.Equal(t, 0, writer.builder.Len())
  95. }
  96. func TestSetLevelTwiceWithMode(t *testing.T) {
  97. testModes := []string{
  98. "mode",
  99. "console",
  100. "volumn",
  101. }
  102. for _, mode := range testModes {
  103. testSetLevelTwiceWithMode(t, mode)
  104. }
  105. }
  106. func TestSetLevelWithDuration(t *testing.T) {
  107. SetLevel(ErrorLevel)
  108. const message = "hello there"
  109. writer := new(mockWriter)
  110. infoLog = writer
  111. atomic.StoreUint32(&initialized, 1)
  112. WithDuration(time.Second).Info(message)
  113. assert.Equal(t, 0, writer.builder.Len())
  114. }
  115. func BenchmarkCopyByteSliceAppend(b *testing.B) {
  116. for i := 0; i < b.N; i++ {
  117. var buf []byte
  118. buf = append(buf, getTimestamp()...)
  119. buf = append(buf, ' ')
  120. buf = append(buf, s...)
  121. _ = buf
  122. }
  123. }
  124. func BenchmarkCopyByteSliceAllocExactly(b *testing.B) {
  125. for i := 0; i < b.N; i++ {
  126. now := []byte(getTimestamp())
  127. buf := make([]byte, len(now)+1+len(s))
  128. n := copy(buf, now)
  129. buf[n] = ' '
  130. copy(buf[n+1:], s)
  131. }
  132. }
  133. func BenchmarkCopyByteSlice(b *testing.B) {
  134. var buf []byte
  135. for i := 0; i < b.N; i++ {
  136. buf = make([]byte, len(s))
  137. copy(buf, s)
  138. }
  139. fmt.Fprint(ioutil.Discard, buf)
  140. }
  141. func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
  142. var buf []byte
  143. for i := 0; i < b.N; i++ {
  144. size := len(s)
  145. buf = s[:size:size]
  146. }
  147. fmt.Fprint(ioutil.Discard, buf)
  148. }
  149. func BenchmarkCacheByteSlice(b *testing.B) {
  150. for i := 0; i < b.N; i++ {
  151. dup := fetch()
  152. copy(dup, s)
  153. put(dup)
  154. }
  155. }
  156. func BenchmarkLogs(b *testing.B) {
  157. b.ReportAllocs()
  158. log.SetOutput(ioutil.Discard)
  159. for i := 0; i < b.N; i++ {
  160. Info(i)
  161. }
  162. }
  163. func fetch() []byte {
  164. select {
  165. case b := <-pool:
  166. return b
  167. default:
  168. }
  169. return make([]byte, 4096)
  170. }
  171. func getFileLine() (string, int) {
  172. _, file, line, _ := runtime.Caller(1)
  173. short := file
  174. for i := len(file) - 1; i > 0; i-- {
  175. if file[i] == '/' {
  176. short = file[i+1:]
  177. break
  178. }
  179. }
  180. return short, line
  181. }
  182. func put(b []byte) {
  183. select {
  184. case pool <- b:
  185. default:
  186. }
  187. }
  188. func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteCloser),
  189. write func(...interface{})) {
  190. const message = "hello there"
  191. writer := new(mockWriter)
  192. setup(writer)
  193. atomic.StoreUint32(&initialized, 1)
  194. write(message)
  195. var entry logEntry
  196. if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
  197. t.Error(err)
  198. }
  199. assert.Equal(t, level, entry.Level)
  200. assert.Equal(t, message, entry.Content)
  201. }
  202. func testSetLevelTwiceWithMode(t *testing.T, mode string) {
  203. SetUp(LogConf{
  204. Mode: mode,
  205. Level: "error",
  206. Path: "/dev/null",
  207. })
  208. SetUp(LogConf{
  209. Mode: mode,
  210. Level: "info",
  211. Path: "/dev/null",
  212. })
  213. const message = "hello there"
  214. writer := new(mockWriter)
  215. infoLog = writer
  216. atomic.StoreUint32(&initialized, 1)
  217. Info(message)
  218. assert.Equal(t, 0, writer.builder.Len())
  219. }