sampler_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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 zapcore_test
  21. import (
  22. "fmt"
  23. "sync"
  24. "testing"
  25. "time"
  26. "go.uber.org/atomic"
  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. )
  33. func fakeSampler(lvl LevelEnabler, tick time.Duration, first, thereafter int) (Core, *observer.ObservedLogs) {
  34. core, logs := observer.New(lvl)
  35. // Keep using deprecated constructor for cc.
  36. core = NewSampler(core, tick, first, thereafter)
  37. return core, logs
  38. }
  39. func assertSequence(t testing.TB, logs []observer.LoggedEntry, lvl Level, seq ...int64) {
  40. seen := make([]int64, len(logs))
  41. for i, entry := range logs {
  42. require.Equal(t, "", entry.Message, "Message wasn't created by writeSequence.")
  43. require.Equal(t, 1, len(entry.Context), "Unexpected number of fields.")
  44. require.Equal(t, lvl, entry.Level, "Unexpected level.")
  45. f := entry.Context[0]
  46. require.Equal(t, "iter", f.Key, "Unexpected field key.")
  47. require.Equal(t, Int64Type, f.Type, "Unexpected field type")
  48. seen[i] = f.Integer
  49. }
  50. assert.Equal(t, seq, seen, "Unexpected sequence logged at level %v.", lvl)
  51. }
  52. func writeSequence(core Core, n int, lvl Level) {
  53. // All tests using writeSequence verify that counters are shared between
  54. // parent and child cores.
  55. core = core.With([]Field{makeInt64Field("iter", n)})
  56. if ce := core.Check(Entry{Level: lvl, Time: time.Now()}, nil); ce != nil {
  57. ce.Write()
  58. }
  59. }
  60. func TestSampler(t *testing.T) {
  61. for _, lvl := range []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, FatalLevel} {
  62. sampler, logs := fakeSampler(DebugLevel, time.Minute, 2, 3)
  63. // Ensure that counts aren't shared between levels.
  64. probeLevel := DebugLevel
  65. if lvl == DebugLevel {
  66. probeLevel = InfoLevel
  67. }
  68. for i := 0; i < 10; i++ {
  69. writeSequence(sampler, 1, probeLevel)
  70. }
  71. // Clear any output.
  72. logs.TakeAll()
  73. for i := 1; i < 10; i++ {
  74. writeSequence(sampler, i, lvl)
  75. }
  76. assertSequence(t, logs.TakeAll(), lvl, 1, 2, 5, 8)
  77. }
  78. }
  79. func TestSamplerDisabledLevels(t *testing.T) {
  80. sampler, logs := fakeSampler(InfoLevel, time.Minute, 1, 100)
  81. // Shouldn't be counted, because debug logging isn't enabled.
  82. writeSequence(sampler, 1, DebugLevel)
  83. writeSequence(sampler, 2, InfoLevel)
  84. assertSequence(t, logs.TakeAll(), InfoLevel, 2)
  85. }
  86. func TestSamplerTicking(t *testing.T) {
  87. // Ensure that we're resetting the sampler's counter every tick.
  88. sampler, logs := fakeSampler(DebugLevel, 10*time.Millisecond, 5, 10)
  89. // If we log five or fewer messages every tick, none of them should be
  90. // dropped.
  91. for tick := 0; tick < 2; tick++ {
  92. for i := 1; i <= 5; i++ {
  93. writeSequence(sampler, i, InfoLevel)
  94. }
  95. ztest.Sleep(15 * time.Millisecond)
  96. }
  97. assertSequence(
  98. t,
  99. logs.TakeAll(),
  100. InfoLevel,
  101. 1, 2, 3, 4, 5, // first tick
  102. 1, 2, 3, 4, 5, // second tick
  103. )
  104. // If we log quickly, we should drop some logs. The first five statements
  105. // each tick should be logged, then every tenth.
  106. for tick := 0; tick < 3; tick++ {
  107. for i := 1; i < 18; i++ {
  108. writeSequence(sampler, i, InfoLevel)
  109. }
  110. ztest.Sleep(10 * time.Millisecond)
  111. }
  112. assertSequence(
  113. t,
  114. logs.TakeAll(),
  115. InfoLevel,
  116. 1, 2, 3, 4, 5, 15, // first tick
  117. 1, 2, 3, 4, 5, 15, // second tick
  118. 1, 2, 3, 4, 5, 15, // third tick
  119. )
  120. }
  121. type countingCore struct {
  122. logs atomic.Uint32
  123. }
  124. func (c *countingCore) Check(ent Entry, ce *CheckedEntry) *CheckedEntry {
  125. return ce.AddCore(ent, c)
  126. }
  127. func (c *countingCore) Write(Entry, []Field) error {
  128. c.logs.Inc()
  129. return nil
  130. }
  131. func (c *countingCore) With([]Field) Core { return c }
  132. func (*countingCore) Enabled(Level) bool { return true }
  133. func (*countingCore) Sync() error { return nil }
  134. func TestSamplerConcurrent(t *testing.T) {
  135. const (
  136. logsPerTick = 10
  137. numMessages = 5
  138. numTicks = 25
  139. numGoroutines = 10
  140. expectedCount = numMessages * logsPerTick * numTicks
  141. )
  142. tick := ztest.Timeout(10 * time.Millisecond)
  143. cc := &countingCore{}
  144. sampler := NewSamplerWithOptions(cc, tick, logsPerTick, 100000)
  145. var (
  146. done atomic.Bool
  147. wg sync.WaitGroup
  148. )
  149. for i := 0; i < numGoroutines; i++ {
  150. wg.Add(1)
  151. go func(i int) {
  152. defer wg.Done()
  153. for {
  154. if done.Load() {
  155. return
  156. }
  157. msg := fmt.Sprintf("msg%v", i%numMessages)
  158. ent := Entry{Level: DebugLevel, Message: msg, Time: time.Now()}
  159. if ce := sampler.Check(ent, nil); ce != nil {
  160. ce.Write()
  161. }
  162. // Give a chance for other goroutines to run.
  163. time.Sleep(time.Microsecond)
  164. }
  165. }(i)
  166. }
  167. time.AfterFunc(numTicks*tick, func() {
  168. done.Store(true)
  169. })
  170. wg.Wait()
  171. assert.InDelta(
  172. t,
  173. expectedCount,
  174. cc.logs.Load(),
  175. expectedCount/10,
  176. "Unexpected number of logs",
  177. )
  178. }
  179. func TestSamplerRaces(t *testing.T) {
  180. sampler, _ := fakeSampler(DebugLevel, time.Minute, 1, 1000)
  181. var wg sync.WaitGroup
  182. start := make(chan struct{})
  183. for i := 0; i < 100; i++ {
  184. wg.Add(1)
  185. go func() {
  186. <-start
  187. for j := 0; j < 100; j++ {
  188. writeSequence(sampler, j, InfoLevel)
  189. }
  190. wg.Done()
  191. }()
  192. }
  193. close(start)
  194. wg.Wait()
  195. }