package metric import ( "crypto/md5" "encoding/binary" "encoding/hex" "encoding/json" "math" "os" "sync" ) const ( METRIC_HEAD_INLINE = byte(1) METRIC_TAIL_INLINE = byte(2) ) type MetricType int const ( MetricCounter = MetricType(1) MetricGuage = MetricType(2) metric_key_size = 8 ) type MetricDescription struct { Key string Type MetricType Name string `json:",omitempty"` Help string `json:",omitempty"` ConstLabels map[string]string `json:",omitempty"` } type writeRequest struct { key string val float64 } type metricCollection struct { writerChan chan *writeRequest } var mc = metricCollection{ writerChan: make(chan *writeRequest, 100), } type MetricWriter func(float64) func (mc *metricCollection) metricWriter() { // head + metric_key_size + 8byte + tail + cr = 19 var buff [20]byte buff[0] = METRIC_HEAD_INLINE buff[17] = METRIC_TAIL_INLINE buff[18] = '\n' for req := range mc.writerChan { copy(buff[1:], []byte(req.key)) binary.BigEndian.PutUint64(buff[9:], math.Float64bits(req.val)) os.Stdout.Write(buff[:]) } } var metricWriterFlag sync.Once func NewMetric(mt MetricType, name string, help string, constLabel ...string) (writer MetricWriter) { metricWriterFlag.Do(func() { go mc.metricWriter() }) hash := md5.New() hash.Write([]byte(name)) var constLabels map[string]string if len(constLabel) > 0 { constLabels = make(map[string]string) for i := 0; i < len(constLabel); i = i + 2 { constLabels[constLabel[i]] = "" hash.Write([]byte(constLabel[i])) } for i := 1; i < len(constLabel); i = i + 2 { constLabels[constLabel[i-1]] = constLabel[i] hash.Write([]byte(constLabel[i])) } } key := hex.EncodeToString(hash.Sum(nil))[:metric_key_size] temp, _ := json.Marshal(MetricDescription{ Key: key, Type: mt, Name: name, Help: help, ConstLabels: constLabels, }) writer = func(val float64) { mc.writerChan <- &writeRequest{ key: key, val: val, } } output := append([]byte{METRIC_HEAD_INLINE}, temp...) output = append(output, METRIC_TAIL_INLINE, '\n') os.Stdout.Write(output) return }