logs_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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 TestMustNil(t *testing.T) {
  116. Must(nil)
  117. }
  118. func BenchmarkCopyByteSliceAppend(b *testing.B) {
  119. for i := 0; i < b.N; i++ {
  120. var buf []byte
  121. buf = append(buf, getTimestamp()...)
  122. buf = append(buf, ' ')
  123. buf = append(buf, s...)
  124. _ = buf
  125. }
  126. }
  127. func BenchmarkCopyByteSliceAllocExactly(b *testing.B) {
  128. for i := 0; i < b.N; i++ {
  129. now := []byte(getTimestamp())
  130. buf := make([]byte, len(now)+1+len(s))
  131. n := copy(buf, now)
  132. buf[n] = ' '
  133. copy(buf[n+1:], s)
  134. }
  135. }
  136. func BenchmarkCopyByteSlice(b *testing.B) {
  137. var buf []byte
  138. for i := 0; i < b.N; i++ {
  139. buf = make([]byte, len(s))
  140. copy(buf, s)
  141. }
  142. fmt.Fprint(ioutil.Discard, buf)
  143. }
  144. func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
  145. var buf []byte
  146. for i := 0; i < b.N; i++ {
  147. size := len(s)
  148. buf = s[:size:size]
  149. }
  150. fmt.Fprint(ioutil.Discard, buf)
  151. }
  152. func BenchmarkCacheByteSlice(b *testing.B) {
  153. for i := 0; i < b.N; i++ {
  154. dup := fetch()
  155. copy(dup, s)
  156. put(dup)
  157. }
  158. }
  159. func BenchmarkLogs(b *testing.B) {
  160. b.ReportAllocs()
  161. log.SetOutput(ioutil.Discard)
  162. for i := 0; i < b.N; i++ {
  163. Info(i)
  164. }
  165. }
  166. func fetch() []byte {
  167. select {
  168. case b := <-pool:
  169. return b
  170. default:
  171. }
  172. return make([]byte, 4096)
  173. }
  174. func getFileLine() (string, int) {
  175. _, file, line, _ := runtime.Caller(1)
  176. short := file
  177. for i := len(file) - 1; i > 0; i-- {
  178. if file[i] == '/' {
  179. short = file[i+1:]
  180. break
  181. }
  182. }
  183. return short, line
  184. }
  185. func put(b []byte) {
  186. select {
  187. case pool <- b:
  188. default:
  189. }
  190. }
  191. func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteCloser),
  192. write func(...interface{})) {
  193. const message = "hello there"
  194. writer := new(mockWriter)
  195. setup(writer)
  196. atomic.StoreUint32(&initialized, 1)
  197. write(message)
  198. var entry logEntry
  199. if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
  200. t.Error(err)
  201. }
  202. assert.Equal(t, level, entry.Level)
  203. assert.Equal(t, message, entry.Content)
  204. }
  205. func testSetLevelTwiceWithMode(t *testing.T, mode string) {
  206. SetUp(LogConf{
  207. Mode: mode,
  208. Level: "error",
  209. Path: "/dev/null",
  210. })
  211. SetUp(LogConf{
  212. Mode: mode,
  213. Level: "info",
  214. Path: "/dev/null",
  215. })
  216. const message = "hello there"
  217. writer := new(mockWriter)
  218. infoLog = writer
  219. atomic.StoreUint32(&initialized, 1)
  220. Info(message)
  221. assert.Equal(t, 0, writer.builder.Len())
  222. }