blob: 2360f0d5cb0795259eb43ce935150e77362332db [file] [log] [blame]
khenaidooac637102019-01-14 15:44:34 -05001package zerolog
2
3import (
4 "math/rand"
5 "sync/atomic"
6 "time"
7)
8
9var (
10 // Often samples log every ~ 10 events.
11 Often = RandomSampler(10)
12 // Sometimes samples log every ~ 100 events.
13 Sometimes = RandomSampler(100)
14 // Rarely samples log every ~ 1000 events.
15 Rarely = RandomSampler(1000)
16)
17
18// Sampler defines an interface to a log sampler.
19type Sampler interface {
20 // Sample returns true if the event should be part of the sample, false if
21 // the event should be dropped.
22 Sample(lvl Level) bool
23}
24
25// RandomSampler use a PRNG to randomly sample an event out of N events,
26// regardless of their level.
27type RandomSampler uint32
28
29// Sample implements the Sampler interface.
30func (s RandomSampler) Sample(lvl Level) bool {
31 if s <= 0 {
32 return false
33 }
34 if rand.Intn(int(s)) != 0 {
35 return false
36 }
37 return true
38}
39
40// BasicSampler is a sampler that will send every Nth events, regardless of
41// there level.
42type BasicSampler struct {
43 N uint32
44 counter uint32
45}
46
47// Sample implements the Sampler interface.
48func (s *BasicSampler) Sample(lvl Level) bool {
49 c := atomic.AddUint32(&s.counter, 1)
50 return c%s.N == s.N-1
51}
52
53// BurstSampler lets Burst events pass per Period then pass the decision to
54// NextSampler. If Sampler is not set, all subsequent events are rejected.
55type BurstSampler struct {
56 // Burst is the maximum number of event per period allowed before calling
57 // NextSampler.
58 Burst uint32
59 // Period defines the burst period. If 0, NextSampler is always called.
60 Period time.Duration
61 // NextSampler is the sampler used after the burst is reached. If nil,
62 // events are always rejected after the burst.
63 NextSampler Sampler
64
65 counter uint32
66 resetAt int64
67}
68
69// Sample implements the Sampler interface.
70func (s *BurstSampler) Sample(lvl Level) bool {
71 if s.Burst > 0 && s.Period > 0 {
72 if s.inc() <= s.Burst {
73 return true
74 }
75 }
76 if s.NextSampler == nil {
77 return false
78 }
79 return s.NextSampler.Sample(lvl)
80}
81
82func (s *BurstSampler) inc() uint32 {
83 now := time.Now().UnixNano()
84 resetAt := atomic.LoadInt64(&s.resetAt)
85 var c uint32
86 if now > resetAt {
87 c = 1
88 atomic.StoreUint32(&s.counter, c)
89 newResetAt := now + s.Period.Nanoseconds()
90 reset := atomic.CompareAndSwapInt64(&s.resetAt, resetAt, newResetAt)
91 if !reset {
92 // Lost the race with another goroutine trying to reset.
93 c = atomic.AddUint32(&s.counter, 1)
94 }
95 } else {
96 c = atomic.AddUint32(&s.counter, 1)
97 }
98 return c
99}
100
101// LevelSampler applies a different sampler for each level.
102type LevelSampler struct {
103 DebugSampler, InfoSampler, WarnSampler, ErrorSampler Sampler
104}
105
106func (s LevelSampler) Sample(lvl Level) bool {
107 switch lvl {
108 case DebugLevel:
109 if s.DebugSampler != nil {
110 return s.DebugSampler.Sample(lvl)
111 }
112 case InfoLevel:
113 if s.InfoSampler != nil {
114 return s.InfoSampler.Sample(lvl)
115 }
116 case WarnLevel:
117 if s.WarnSampler != nil {
118 return s.WarnSampler.Sample(lvl)
119 }
120 case ErrorLevel:
121 if s.ErrorSampler != nil {
122 return s.ErrorSampler.Sample(lvl)
123 }
124 }
125 return true
126}