diff --git a/metric/metric.go b/metric/metric.go new file mode 100644 index 0000000..1893a3e --- /dev/null +++ b/metric/metric.go @@ -0,0 +1,108 @@ +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 +}