logs_test.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. package logx
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "runtime"
  10. "strings"
  11. "sync/atomic"
  12. "testing"
  13. "time"
  14. "github.com/stretchr/testify/assert"
  15. )
  16. var (
  17. s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection")
  18. pool = make(chan []byte, 1)
  19. )
  20. type mockWriter struct {
  21. builder strings.Builder
  22. }
  23. func (mw *mockWriter) Write(data []byte) (int, error) {
  24. return mw.builder.Write(data)
  25. }
  26. func (mw *mockWriter) Close() error {
  27. return nil
  28. }
  29. func (mw *mockWriter) Reset() {
  30. mw.builder.Reset()
  31. }
  32. func (mw *mockWriter) Contains(text string) bool {
  33. return strings.Contains(mw.builder.String(), text)
  34. }
  35. func TestFileLineFileMode(t *testing.T) {
  36. writer := new(mockWriter)
  37. errorLog = writer
  38. atomic.StoreUint32(&initialized, 1)
  39. file, line := getFileLine()
  40. Error("anything")
  41. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  42. writer.Reset()
  43. file, line = getFileLine()
  44. Errorf("anything %s", "format")
  45. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  46. }
  47. func TestFileLineConsoleMode(t *testing.T) {
  48. writer := new(mockWriter)
  49. writeConsole = true
  50. errorLog = newLogWriter(log.New(writer, "[ERROR] ", flags))
  51. atomic.StoreUint32(&initialized, 1)
  52. file, line := getFileLine()
  53. Error("anything")
  54. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  55. writer.Reset()
  56. file, line = getFileLine()
  57. Errorf("anything %s", "format")
  58. assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  59. }
  60. func TestStructedLogInfo(t *testing.T) {
  61. doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
  62. infoLog = writer
  63. }, func(v ...interface{}) {
  64. Info(v...)
  65. })
  66. }
  67. func TestStructedLogSlow(t *testing.T) {
  68. doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
  69. slowLog = writer
  70. }, func(v ...interface{}) {
  71. Slow(v...)
  72. })
  73. }
  74. func TestStructedLogSlowf(t *testing.T) {
  75. doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
  76. slowLog = writer
  77. }, func(v ...interface{}) {
  78. Slowf(fmt.Sprint(v...))
  79. })
  80. }
  81. func TestStructedLogStat(t *testing.T) {
  82. doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
  83. statLog = writer
  84. }, func(v ...interface{}) {
  85. Stat(v...)
  86. })
  87. }
  88. func TestStructedLogStatf(t *testing.T) {
  89. doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
  90. statLog = writer
  91. }, func(v ...interface{}) {
  92. Statf(fmt.Sprint(v...))
  93. })
  94. }
  95. func TestStructedLogSevere(t *testing.T) {
  96. doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
  97. severeLog = writer
  98. }, func(v ...interface{}) {
  99. Severe(v...)
  100. })
  101. }
  102. func TestStructedLogSeveref(t *testing.T) {
  103. doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
  104. severeLog = writer
  105. }, func(v ...interface{}) {
  106. Severef(fmt.Sprint(v...))
  107. })
  108. }
  109. func TestStructedLogWithDuration(t *testing.T) {
  110. const message = "hello there"
  111. writer := new(mockWriter)
  112. infoLog = writer
  113. atomic.StoreUint32(&initialized, 1)
  114. WithDuration(time.Second).Info(message)
  115. var entry logEntry
  116. if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
  117. t.Error(err)
  118. }
  119. assert.Equal(t, levelInfo, entry.Level)
  120. assert.Equal(t, message, entry.Content)
  121. assert.Equal(t, "1000.0ms", entry.Duration)
  122. }
  123. func TestSetLevel(t *testing.T) {
  124. SetLevel(ErrorLevel)
  125. const message = "hello there"
  126. writer := new(mockWriter)
  127. infoLog = writer
  128. atomic.StoreUint32(&initialized, 1)
  129. Info(message)
  130. assert.Equal(t, 0, writer.builder.Len())
  131. }
  132. func TestSetLevelTwiceWithMode(t *testing.T) {
  133. testModes := []string{
  134. "mode",
  135. "console",
  136. "volumn",
  137. }
  138. for _, mode := range testModes {
  139. testSetLevelTwiceWithMode(t, mode)
  140. }
  141. }
  142. func TestSetLevelWithDuration(t *testing.T) {
  143. SetLevel(ErrorLevel)
  144. const message = "hello there"
  145. writer := new(mockWriter)
  146. infoLog = writer
  147. atomic.StoreUint32(&initialized, 1)
  148. WithDuration(time.Second).Info(message)
  149. assert.Equal(t, 0, writer.builder.Len())
  150. }
  151. func TestMustNil(t *testing.T) {
  152. Must(nil)
  153. }
  154. func TestSetup(t *testing.T) {
  155. MustSetup(LogConf{
  156. ServiceName: "any",
  157. Mode: "console",
  158. })
  159. MustSetup(LogConf{
  160. ServiceName: "any",
  161. Mode: "file",
  162. Path: os.TempDir(),
  163. })
  164. MustSetup(LogConf{
  165. ServiceName: "any",
  166. Mode: "volume",
  167. Path: os.TempDir(),
  168. })
  169. assert.NotNil(t, setupWithVolume(LogConf{}))
  170. assert.NotNil(t, setupWithFiles(LogConf{}))
  171. assert.Nil(t, setupWithFiles(LogConf{
  172. ServiceName: "any",
  173. Path: os.TempDir(),
  174. Compress: true,
  175. KeepDays: 1,
  176. }))
  177. setupLogLevel(LogConf{
  178. Level: levelInfo,
  179. })
  180. setupLogLevel(LogConf{
  181. Level: levelError,
  182. })
  183. setupLogLevel(LogConf{
  184. Level: levelSevere,
  185. })
  186. _, err := createOutput("")
  187. assert.NotNil(t, err)
  188. Disable()
  189. }
  190. func TestDisable(t *testing.T) {
  191. Disable()
  192. WithKeepDays(1)
  193. WithGzip()
  194. assert.Nil(t, Close())
  195. writeConsole = false
  196. assert.Nil(t, Close())
  197. }
  198. func TestWithGzip(t *testing.T) {
  199. fn := WithGzip()
  200. var opt logOptions
  201. fn(&opt)
  202. assert.True(t, opt.gzipEnabled)
  203. }
  204. func TestWithKeepDays(t *testing.T) {
  205. fn := WithKeepDays(1)
  206. var opt logOptions
  207. fn(&opt)
  208. assert.Equal(t, 1, opt.keepDays)
  209. }
  210. func BenchmarkCopyByteSliceAppend(b *testing.B) {
  211. for i := 0; i < b.N; i++ {
  212. var buf []byte
  213. buf = append(buf, getTimestamp()...)
  214. buf = append(buf, ' ')
  215. buf = append(buf, s...)
  216. _ = buf
  217. }
  218. }
  219. func BenchmarkCopyByteSliceAllocExactly(b *testing.B) {
  220. for i := 0; i < b.N; i++ {
  221. now := []byte(getTimestamp())
  222. buf := make([]byte, len(now)+1+len(s))
  223. n := copy(buf, now)
  224. buf[n] = ' '
  225. copy(buf[n+1:], s)
  226. }
  227. }
  228. func BenchmarkCopyByteSlice(b *testing.B) {
  229. var buf []byte
  230. for i := 0; i < b.N; i++ {
  231. buf = make([]byte, len(s))
  232. copy(buf, s)
  233. }
  234. fmt.Fprint(ioutil.Discard, buf)
  235. }
  236. func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
  237. var buf []byte
  238. for i := 0; i < b.N; i++ {
  239. size := len(s)
  240. buf = s[:size:size]
  241. }
  242. fmt.Fprint(ioutil.Discard, buf)
  243. }
  244. func BenchmarkCacheByteSlice(b *testing.B) {
  245. for i := 0; i < b.N; i++ {
  246. dup := fetch()
  247. copy(dup, s)
  248. put(dup)
  249. }
  250. }
  251. func BenchmarkLogs(b *testing.B) {
  252. b.ReportAllocs()
  253. log.SetOutput(ioutil.Discard)
  254. for i := 0; i < b.N; i++ {
  255. Info(i)
  256. }
  257. }
  258. func fetch() []byte {
  259. select {
  260. case b := <-pool:
  261. return b
  262. default:
  263. }
  264. return make([]byte, 4096)
  265. }
  266. func getFileLine() (string, int) {
  267. _, file, line, _ := runtime.Caller(1)
  268. short := file
  269. for i := len(file) - 1; i > 0; i-- {
  270. if file[i] == '/' {
  271. short = file[i+1:]
  272. break
  273. }
  274. }
  275. return short, line
  276. }
  277. func put(b []byte) {
  278. select {
  279. case pool <- b:
  280. default:
  281. }
  282. }
  283. func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteCloser),
  284. write func(...interface{})) {
  285. const message = "hello there"
  286. writer := new(mockWriter)
  287. setup(writer)
  288. atomic.StoreUint32(&initialized, 1)
  289. write(message)
  290. var entry logEntry
  291. if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
  292. t.Error(err)
  293. }
  294. assert.Equal(t, level, entry.Level)
  295. assert.True(t, strings.Contains(entry.Content, message))
  296. }
  297. func testSetLevelTwiceWithMode(t *testing.T, mode string) {
  298. SetUp(LogConf{
  299. Mode: mode,
  300. Level: "error",
  301. Path: "/dev/null",
  302. })
  303. SetUp(LogConf{
  304. Mode: mode,
  305. Level: "info",
  306. Path: "/dev/null",
  307. })
  308. const message = "hello there"
  309. writer := new(mockWriter)
  310. infoLog = writer
  311. atomic.StoreUint32(&initialized, 1)
  312. Info(message)
  313. assert.Equal(t, 0, writer.builder.Len())
  314. Infof(message)
  315. assert.Equal(t, 0, writer.builder.Len())
  316. ErrorStack(message)
  317. assert.Equal(t, 0, writer.builder.Len())
  318. ErrorStackf(message)
  319. assert.Equal(t, 0, writer.builder.Len())
  320. }