global_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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 zap
  21. import (
  22. "log"
  23. "sync"
  24. "testing"
  25. "time"
  26. "go.uber.org/zap/internal/exit"
  27. "go.uber.org/zap/internal/ztest"
  28. "go.uber.org/zap/zapcore"
  29. "go.uber.org/zap/zaptest/observer"
  30. "github.com/stretchr/testify/assert"
  31. "github.com/stretchr/testify/require"
  32. "go.uber.org/atomic"
  33. )
  34. func TestReplaceGlobals(t *testing.T) {
  35. initialL := *L()
  36. initialS := *S()
  37. withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
  38. L().Info("no-op")
  39. S().Info("no-op")
  40. assert.Equal(t, 0, logs.Len(), "Expected initial logs to go to default no-op global.")
  41. defer ReplaceGlobals(l)()
  42. L().Info("captured")
  43. S().Info("captured")
  44. expected := observer.LoggedEntry{
  45. Entry: zapcore.Entry{Message: "captured"},
  46. Context: []Field{},
  47. }
  48. assert.Equal(
  49. t,
  50. []observer.LoggedEntry{expected, expected},
  51. logs.AllUntimed(),
  52. "Unexpected global log output.",
  53. )
  54. })
  55. assert.Equal(t, initialL, *L(), "Expected func returned from ReplaceGlobals to restore initial L.")
  56. assert.Equal(t, initialS, *S(), "Expected func returned from ReplaceGlobals to restore initial S.")
  57. }
  58. func TestGlobalsConcurrentUse(t *testing.T) {
  59. var (
  60. stop atomic.Bool
  61. wg sync.WaitGroup
  62. )
  63. for i := 0; i < 100; i++ {
  64. wg.Add(2)
  65. go func() {
  66. for !stop.Load() {
  67. ReplaceGlobals(NewNop())
  68. }
  69. wg.Done()
  70. }()
  71. go func() {
  72. for !stop.Load() {
  73. L().With(Int("foo", 42)).Named("main").WithOptions(Development()).Info("")
  74. S().Info("")
  75. }
  76. wg.Done()
  77. }()
  78. }
  79. ztest.Sleep(100 * time.Millisecond)
  80. stop.Toggle()
  81. wg.Wait()
  82. }
  83. func TestNewStdLog(t *testing.T) {
  84. withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
  85. std := NewStdLog(l)
  86. std.Print("redirected")
  87. checkStdLogMessage(t, "redirected", logs)
  88. })
  89. }
  90. func TestNewStdLogAt(t *testing.T) {
  91. // include DPanicLevel here, but do not include Development in options
  92. levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}
  93. for _, level := range levels {
  94. withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
  95. std, err := NewStdLogAt(l, level)
  96. require.NoError(t, err, "Unexpected error.")
  97. std.Print("redirected")
  98. checkStdLogMessage(t, "redirected", logs)
  99. })
  100. }
  101. }
  102. func TestNewStdLogAtPanics(t *testing.T) {
  103. // include DPanicLevel here and enable Development in options
  104. levels := []zapcore.Level{DPanicLevel, PanicLevel}
  105. for _, level := range levels {
  106. withLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) {
  107. std, err := NewStdLogAt(l, level)
  108. require.NoError(t, err, "Unexpected error")
  109. assert.Panics(t, func() { std.Print("redirected") }, "Expected log to panic.")
  110. checkStdLogMessage(t, "redirected", logs)
  111. })
  112. }
  113. }
  114. func TestNewStdLogAtFatal(t *testing.T) {
  115. withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
  116. stub := exit.WithStub(func() {
  117. std, err := NewStdLogAt(l, FatalLevel)
  118. require.NoError(t, err, "Unexpected error.")
  119. std.Print("redirected")
  120. checkStdLogMessage(t, "redirected", logs)
  121. })
  122. assert.True(t, true, stub.Exited, "Expected Fatal logger call to terminate process.")
  123. stub.Unstub()
  124. })
  125. }
  126. func TestNewStdLogAtInvalid(t *testing.T) {
  127. _, err := NewStdLogAt(NewNop(), zapcore.Level(99))
  128. assert.Error(t, err, "Expected to get error.")
  129. assert.Contains(t, err.Error(), "99", "Expected level code in error message")
  130. }
  131. func TestRedirectStdLog(t *testing.T) {
  132. initialFlags := log.Flags()
  133. initialPrefix := log.Prefix()
  134. withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
  135. defer RedirectStdLog(l)()
  136. log.Print("redirected")
  137. assert.Equal(t, []observer.LoggedEntry{{
  138. Entry: zapcore.Entry{Message: "redirected"},
  139. Context: []Field{},
  140. }}, logs.AllUntimed(), "Unexpected global log output.")
  141. })
  142. assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
  143. assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
  144. }
  145. func TestRedirectStdLogCaller(t *testing.T) {
  146. withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
  147. defer RedirectStdLog(l)()
  148. log.Print("redirected")
  149. entries := logs.All()
  150. require.Len(t, entries, 1, "Unexpected number of logs.")
  151. assert.Contains(t, entries[0].Entry.Caller.File, "global_test.go", "Unexpected caller annotation.")
  152. })
  153. }
  154. func TestRedirectStdLogAt(t *testing.T) {
  155. initialFlags := log.Flags()
  156. initialPrefix := log.Prefix()
  157. // include DPanicLevel here, but do not include Development in options
  158. levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}
  159. for _, level := range levels {
  160. withLogger(t, DebugLevel, nil, func(l *Logger, logs *observer.ObservedLogs) {
  161. restore, err := RedirectStdLogAt(l, level)
  162. require.NoError(t, err, "Unexpected error.")
  163. defer restore()
  164. log.Print("redirected")
  165. assert.Equal(t, []observer.LoggedEntry{{
  166. Entry: zapcore.Entry{Level: level, Message: "redirected"},
  167. Context: []Field{},
  168. }}, logs.AllUntimed(), "Unexpected global log output.")
  169. })
  170. }
  171. assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
  172. assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
  173. }
  174. func TestRedirectStdLogAtCaller(t *testing.T) {
  175. // include DPanicLevel here, but do not include Development in options
  176. levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}
  177. for _, level := range levels {
  178. withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
  179. restore, err := RedirectStdLogAt(l, level)
  180. require.NoError(t, err, "Unexpected error.")
  181. defer restore()
  182. log.Print("redirected")
  183. entries := logs.All()
  184. require.Len(t, entries, 1, "Unexpected number of logs.")
  185. assert.Contains(t, entries[0].Entry.Caller.File, "global_test.go", "Unexpected caller annotation.")
  186. })
  187. }
  188. }
  189. func TestRedirectStdLogAtPanics(t *testing.T) {
  190. initialFlags := log.Flags()
  191. initialPrefix := log.Prefix()
  192. // include DPanicLevel here and enable Development in options
  193. levels := []zapcore.Level{DPanicLevel, PanicLevel}
  194. for _, level := range levels {
  195. withLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) {
  196. restore, err := RedirectStdLogAt(l, level)
  197. require.NoError(t, err, "Unexpected error.")
  198. defer restore()
  199. assert.Panics(t, func() { log.Print("redirected") }, "Expected log to panic.")
  200. checkStdLogMessage(t, "redirected", logs)
  201. })
  202. }
  203. assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
  204. assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
  205. }
  206. func TestRedirectStdLogAtFatal(t *testing.T) {
  207. initialFlags := log.Flags()
  208. initialPrefix := log.Prefix()
  209. withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
  210. stub := exit.WithStub(func() {
  211. restore, err := RedirectStdLogAt(l, FatalLevel)
  212. require.NoError(t, err, "Unexpected error.")
  213. defer restore()
  214. log.Print("redirected")
  215. checkStdLogMessage(t, "redirected", logs)
  216. })
  217. assert.True(t, true, stub.Exited, "Expected Fatal logger call to terminate process.")
  218. stub.Unstub()
  219. })
  220. assert.Equal(t, initialFlags, log.Flags(), "Expected to reset initial flags.")
  221. assert.Equal(t, initialPrefix, log.Prefix(), "Expected to reset initial prefix.")
  222. }
  223. func TestRedirectStdLogAtInvalid(t *testing.T) {
  224. restore, err := RedirectStdLogAt(NewNop(), zapcore.Level(99))
  225. defer func() {
  226. if restore != nil {
  227. restore()
  228. }
  229. }()
  230. require.Error(t, err, "Expected to get error.")
  231. assert.Contains(t, err.Error(), "99", "Expected level code in error message")
  232. }
  233. func checkStdLogMessage(t *testing.T, msg string, logs *observer.ObservedLogs) {
  234. require.Equal(t, 1, logs.Len(), "Expected exactly one entry to be logged")
  235. entry := logs.AllUntimed()[0]
  236. assert.Equal(t, []Field{}, entry.Context, "Unexpected entry context.")
  237. assert.Equal(t, "redirected", entry.Entry.Message, "Unexpected entry message.")
  238. assert.Regexp(
  239. t,
  240. `/global_test.go:\d+$`,
  241. entry.Entry.Caller.String(),
  242. "Unexpected caller annotation.",
  243. )
  244. }