138 lines
2.6 KiB
Go
138 lines
2.6 KiB
Go
|
package statsd
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"math/rand"
|
||
|
"net"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"go-common/library/log"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
_quit = ""
|
||
|
_size = 1400
|
||
|
)
|
||
|
|
||
|
// Config statsd config.
|
||
|
type Config struct {
|
||
|
Project string
|
||
|
Addr string
|
||
|
ChanSize int
|
||
|
}
|
||
|
|
||
|
// Statsd statsd struct.
|
||
|
type Statsd struct {
|
||
|
// project.hostname.api
|
||
|
// Make sure no '/' in the api.
|
||
|
c *Config
|
||
|
business string
|
||
|
r *rand.Rand
|
||
|
stats chan string
|
||
|
}
|
||
|
|
||
|
// New new a statsd struct.
|
||
|
func New(c *Config) (s *Statsd) {
|
||
|
s = new(Statsd)
|
||
|
s.c = c
|
||
|
s.business = fmt.Sprintf("%s", c.Project)
|
||
|
// init rand
|
||
|
s.r = rand.New(rand.NewSource(time.Now().Unix()))
|
||
|
// init stat channel
|
||
|
s.stats = make(chan string, c.ChanSize)
|
||
|
go s.writeproc()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// send data to udp statsd daemon
|
||
|
func (s *Statsd) send(data string, rate float32) {
|
||
|
if rate < 1 && s.r != nil {
|
||
|
if s.r.Float32() < rate {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
select {
|
||
|
case s.stats <- data:
|
||
|
default:
|
||
|
log.Warn("Statsd stat channel is full")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// writeproc write data into connection.
|
||
|
func (s *Statsd) writeproc() {
|
||
|
var (
|
||
|
err error
|
||
|
l int
|
||
|
stat string
|
||
|
conn net.Conn
|
||
|
buf bytes.Buffer
|
||
|
tick = time.Tick(1 * time.Second)
|
||
|
)
|
||
|
for {
|
||
|
select {
|
||
|
case stat = <-s.stats:
|
||
|
if stat == _quit {
|
||
|
if conn != nil {
|
||
|
conn.Close()
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
case <-tick:
|
||
|
if l = buf.Len(); l > 0 {
|
||
|
conn.Write(buf.Bytes()[:l-1])
|
||
|
buf.Reset()
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
if conn == nil {
|
||
|
if conn, err = net.Dial("udp", s.c.Addr); err != nil {
|
||
|
log.Error("net.Dial('udp', %s) error(%v)", s.c.Addr, err)
|
||
|
time.Sleep(time.Second)
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
if l = buf.Len(); l+len(stat) >= _size {
|
||
|
conn.Write(buf.Bytes()[:l-1])
|
||
|
buf.Reset()
|
||
|
}
|
||
|
buf.WriteString(stat)
|
||
|
buf.WriteByte('\n')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Close close the connection.
|
||
|
func (s *Statsd) Close() {
|
||
|
s.stats <- _quit
|
||
|
}
|
||
|
|
||
|
// Timing log timing information (in milliseconds) without sampling
|
||
|
func (s *Statsd) Timing(name string, time int64, extra ...string) {
|
||
|
val := formatTiming(s.business, name, time, extra...)
|
||
|
s.send(val, 1)
|
||
|
}
|
||
|
|
||
|
// Incr increments one stat counter without sampling
|
||
|
func (s *Statsd) Incr(name string, extra ...string) {
|
||
|
val := formatIncr(s.business, name, extra...)
|
||
|
s.send(val, 1)
|
||
|
}
|
||
|
|
||
|
// State set state
|
||
|
func (s *Statsd) State(stat string, val int64, extra ...string) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func formatIncr(business, name string, extra ...string) string {
|
||
|
ss := []string{business, name}
|
||
|
ss = append(ss, extra...)
|
||
|
return strings.Join(ss, ".") + ":1|c"
|
||
|
}
|
||
|
|
||
|
func formatTiming(business, name string, time int64, extra ...string) string {
|
||
|
ss := []string{business, name}
|
||
|
ss = append(ss, extra...)
|
||
|
return strings.Join(ss, ".") + fmt.Sprintf(":%d|ms", time)
|
||
|
}
|