meter: BuildName func to combine metric name with labels into string

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2021-07-21 12:39:59 +03:00
parent 7ece08896f
commit c6ba2a91e6
2 changed files with 53 additions and 28 deletions

View File

@ -3,8 +3,9 @@ package meter
import ( import (
"io" "io"
"reflect"
"sort" "sort"
"strconv"
"strings"
"time" "time"
) )
@ -77,36 +78,50 @@ type Summary interface {
UpdateDuration(time.Time) UpdateDuration(time.Time)
} }
// sort labels alphabeticaly by label name
type byKey []string type byKey []string
func (k byKey) Len() int { return len(k) / 2 } func (k byKey) Len() int { return len(k) / 2 }
func (k byKey) Less(i, j int) bool { return k[i*2] < k[j*2] } func (k byKey) Less(i, j int) bool { return k[i*2] < k[j*2] }
func (k byKey) Swap(i, j int) { func (k byKey) Swap(i, j int) {
k[i*2], k[i*2+1], k[j*2], k[j*2+1] = k[j*2], k[j*2+1], k[i*2], k[i*2+1] k[i*2], k[j*2] = k[j*2], k[i*2]
k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1]
} }
func Sort(slice *[]string) { // BuildName used to combine metric with labels.
bk := byKey(*slice) // If labels count is odd, drop last element
if bk.Len() <= 1 { func BuildName(name string, labels ...string) string {
return if len(labels)%2 == 1 {
labels = labels[:len(labels)-1]
} }
sort.Sort(bk)
v := reflect.ValueOf(slice).Elem() sort.Sort(byKey(labels))
cnt := 0
key := 0 idx := 0
val := 1 for {
for key < v.Len() { if labels[idx] == labels[idx+2] {
if len(bk) > key+2 && bk[key] == bk[key+2] { copy(labels[idx:], labels[idx+2:])
key += 2 labels = labels[:len(labels)-2]
val += 2 } else {
continue idx += 2
}
if idx+2 >= len(labels) {
break
} }
v.Index(cnt).Set(v.Index(key))
cnt++
v.Index(cnt).Set(v.Index(val))
cnt++
key += 2
val += 2
} }
v.SetLen(cnt)
var b strings.Builder
_, _ = b.WriteString(name)
_, _ = b.WriteRune('{')
for idx := 0; idx < len(labels); idx += 2 {
if idx > 0 {
_, _ = b.WriteRune(',')
}
_, _ = b.WriteString(labels[idx])
_, _ = b.WriteString(`=`)
_, _ = b.WriteString(strconv.Quote(labels[idx+1]))
}
_, _ = b.WriteRune('}')
return b.String()
} }

View File

@ -14,11 +14,21 @@ func TestNoopMeter(t *testing.T) {
cnt.Inc() cnt.Inc()
} }
func TestLabelsSort(t *testing.T) { func TestBuildName(t *testing.T) {
ls := []string{"server", "http", "register", "mdns", "broker", "broker1", "broker", "broker2", "server", "tcp"} data := map[string][]string{
Sort(&ls) // `my_metric{firstlabel="value2",zerolabel="value3"}`: []string{
// "my_metric",
// "zerolabel", "value3", "firstlabel", "value2",
// },
`my_metric{broker="broker2",register="mdns",server="tcp"}`: []string{
"my_metric",
"broker", "broker1", "broker", "broker2", "server", "http", "server", "tcp", "register", "mdns",
},
}
if ls[0] != "broker" || ls[1] != "broker2" { for e, d := range data {
t.Fatalf("sort error: %v", ls) if x := BuildName(d[0], d[1:]...); x != e {
t.Fatalf("expect: %s, result: %s", e, x)
}
} }
} }