github.com/msales/pkg/v3@v3.24.0/stats/statsd.go (about) 1 package stats 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/cactus/go-statsd-client/statsd" 8 "github.com/msales/pkg/v3/bytes" 9 ) 10 11 // Statsd represents a statsd client. 12 type Statsd struct { 13 client statsd.Statter 14 } 15 16 // NewStatsd create a Statsd instance. 17 func NewStatsd(addr, prefix string) (Stats, error) { 18 c, err := statsd.NewClient(addr, prefix) 19 if err != nil { 20 return nil, err 21 } 22 23 return &Statsd{ 24 client: c, 25 }, nil 26 } 27 28 // Inc increments a count by the value. 29 func (s *Statsd) Inc(name string, value int64, rate float32, tags ...interface{}) error { 30 name += formatStatsdTags(tags) 31 return s.client.Inc(name, value, rate) 32 } 33 34 // Dec decrements a count by the value. 35 func (s *Statsd) Dec(name string, value int64, rate float32, tags ...interface{}) error { 36 name += formatStatsdTags(tags) 37 return s.client.Dec(name, value, rate) 38 } 39 40 // Gauge measures the value of a metric. 41 func (s *Statsd) Gauge(name string, value float64, rate float32, tags ...interface{}) error { 42 name += formatStatsdTags(tags) 43 return s.client.Gauge(name, int64(value), rate) 44 } 45 46 // Timing sends the value of a Duration. 47 func (s *Statsd) Timing(name string, value time.Duration, rate float32, tags ...interface{}) error { 48 name += formatStatsdTags(tags) 49 return s.client.TimingDuration(name, value, rate) 50 } 51 52 // Close closes the client and flushes buffered stats, if applicable 53 func (s *Statsd) Close() error { 54 return s.client.Close() 55 } 56 57 // BufferedStatsdFunc represents an configuration function for BufferedStatsd. 58 type BufferedStatsdFunc func(*BufferedStatsd) 59 60 // WithFlushInterval sets the maximum flushInterval for packet sending. 61 // Defaults to 300ms. 62 func WithFlushInterval(interval time.Duration) BufferedStatsdFunc { 63 return func(s *BufferedStatsd) { 64 s.flushInterval = interval 65 } 66 } 67 68 // WithFlushBytes sets the maximum udp packet size that will be sent. 69 // Defaults to 1432 flushBytes. 70 func WithFlushBytes(bytes int) BufferedStatsdFunc { 71 return func(s *BufferedStatsd) { 72 s.flushBytes = bytes 73 } 74 } 75 76 // BufferedStatsd represents a buffered statsd client. 77 type BufferedStatsd struct { 78 client statsd.Statter 79 80 flushInterval time.Duration 81 flushBytes int 82 } 83 84 // NewBufferedStatsd create a buffered Statsd instance. 85 func NewBufferedStatsd(addr, prefix string, opts ...BufferedStatsdFunc) (*BufferedStatsd, error) { 86 s := &BufferedStatsd{} 87 88 for _, o := range opts { 89 o(s) 90 } 91 92 c, err := statsd.NewBufferedClient(addr, prefix, s.flushInterval, s.flushBytes) 93 if err != nil { 94 return nil, err 95 } 96 s.client = c 97 98 return s, nil 99 } 100 101 // Inc increments a count by the value. 102 func (s *BufferedStatsd) Inc(name string, value int64, rate float32, tags ...interface{}) error { 103 name += formatStatsdTags(tags) 104 return s.client.Inc(name, value, rate) 105 } 106 107 // Dec decrements a count by the value. 108 func (s *BufferedStatsd) Dec(name string, value int64, rate float32, tags ...interface{}) error { 109 name += formatStatsdTags(tags) 110 return s.client.Dec(name, value, rate) 111 } 112 113 // Gauge measures the value of a metric. 114 func (s *BufferedStatsd) Gauge(name string, value float64, rate float32, tags ...interface{}) error { 115 name += formatStatsdTags(tags) 116 return s.client.Gauge(name, int64(value), rate) 117 } 118 119 // Timing sends the value of a Duration. 120 func (s *BufferedStatsd) Timing(name string, value time.Duration, rate float32, tags ...interface{}) error { 121 name += formatStatsdTags(tags) 122 return s.client.TimingDuration(name, value, rate) 123 } 124 125 // Close closes the client and flushes buffered stats, if applicable 126 func (s *BufferedStatsd) Close() error { 127 return s.client.Close() 128 } 129 130 var statsdPool = bytes.NewPool(100) 131 132 // formatStatsdTags formats into an InfluxDB style string 133 func formatStatsdTags(tags []interface{}) string { 134 if len(tags) == 0 { 135 return "" 136 } 137 138 tags = deduplicateTags(normalizeTags(tags)) 139 140 buf := statsdPool.Get() 141 for i := 0; i < len(tags); i += 2 { 142 buf.WriteByte(',') 143 formatStatsdValue(buf, tags[i]) 144 buf.WriteByte('=') 145 formatStatsdValue(buf, tags[i+1]) 146 } 147 148 s := string(buf.Bytes()) 149 statsdPool.Put(buf) 150 return s 151 } 152 153 // formatStatsdValue formats a value, adding it to the Buffer. 154 func formatStatsdValue(buf *bytes.Buffer, value interface{}) { 155 if value == nil { 156 buf.WriteString("nil") 157 return 158 } 159 160 switch v := value.(type) { 161 case bool: 162 buf.AppendBool(v) 163 case float32: 164 buf.AppendFloat(float64(v), 'g', -1, 64) 165 case float64: 166 buf.AppendFloat(v, 'g', -1, 64) 167 case int: 168 buf.AppendInt(int64(v)) 169 case int8: 170 buf.AppendInt(int64(v)) 171 case int16: 172 buf.AppendInt(int64(v)) 173 case int32: 174 buf.AppendInt(int64(v)) 175 case int64: 176 buf.AppendInt(v) 177 case uint: 178 buf.AppendUint(uint64(v)) 179 case uint8: 180 buf.AppendUint(uint64(v)) 181 case uint16: 182 buf.AppendUint(uint64(v)) 183 case uint32: 184 buf.AppendUint(uint64(v)) 185 case uint64: 186 buf.AppendUint(v) 187 case string: 188 buf.WriteString(v) 189 default: 190 buf.WriteString(fmt.Sprintf("%+v", value)) 191 } 192 }