2020-04-23 08:34:04 +00:00
|
|
|
package discoveryutils
|
|
|
|
|
|
|
|
import (
|
2020-04-24 14:50:21 +00:00
|
|
|
"encoding/json"
|
2020-04-23 08:34:04 +00:00
|
|
|
"net"
|
|
|
|
"regexp"
|
2020-04-24 14:50:21 +00:00
|
|
|
"sort"
|
2020-04-23 08:34:04 +00:00
|
|
|
"strconv"
|
2022-08-26 21:12:39 +00:00
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
2020-04-24 14:50:21 +00:00
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
2020-04-23 08:34:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// SanitizeLabelName replaces anything that doesn't match
|
|
|
|
// client_label.LabelNameRE with an underscore.
|
|
|
|
//
|
|
|
|
// This has been copied from Prometheus sources at util/strutil/strconv.go
|
|
|
|
func SanitizeLabelName(name string) string {
|
2022-08-26 21:12:39 +00:00
|
|
|
m := sanitizedLabelNames.Load().(*sync.Map)
|
|
|
|
v, ok := m.Load(name)
|
|
|
|
if ok {
|
|
|
|
// Fast path - the sanitized label name is found in the cache.
|
|
|
|
sp := v.(*string)
|
|
|
|
return *sp
|
|
|
|
}
|
|
|
|
// Slow path - sanitize name and store it in the cache.
|
|
|
|
sanitizedName := invalidLabelCharRE.ReplaceAllString(name, "_")
|
|
|
|
// Make a copy of name in order to limit memory usage to the name length,
|
|
|
|
// since the name may point to bigger string.
|
|
|
|
s := string(append([]byte{}, name...))
|
2022-08-26 21:28:36 +00:00
|
|
|
if sanitizedName == name {
|
|
|
|
// point sanitizedName to just allocated s, since it may point to name,
|
|
|
|
// which, in turn, can point to bigger string.
|
|
|
|
sanitizedName = s
|
|
|
|
}
|
2022-08-26 21:12:39 +00:00
|
|
|
sp := &sanitizedName
|
|
|
|
m.Store(s, sp)
|
|
|
|
n := atomic.AddUint64(&sanitizedLabelNamesLen, 1)
|
|
|
|
if n > 100e3 {
|
|
|
|
atomic.StoreUint64(&sanitizedLabelNamesLen, 0)
|
|
|
|
sanitizedLabelNames.Store(&sync.Map{})
|
|
|
|
}
|
|
|
|
return sanitizedName
|
2020-04-23 08:34:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2022-08-26 21:12:39 +00:00
|
|
|
sanitizedLabelNames atomic.Value
|
|
|
|
sanitizedLabelNamesLen uint64
|
|
|
|
|
2020-04-23 08:34:04 +00:00
|
|
|
invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)
|
|
|
|
)
|
|
|
|
|
2022-08-26 21:12:39 +00:00
|
|
|
func init() {
|
|
|
|
sanitizedLabelNames.Store(&sync.Map{})
|
|
|
|
}
|
|
|
|
|
2020-04-23 08:34:04 +00:00
|
|
|
// JoinHostPort returns host:port.
|
|
|
|
//
|
|
|
|
// Host may be dns name, ipv4 or ipv6 address.
|
|
|
|
func JoinHostPort(host string, port int) string {
|
|
|
|
portStr := strconv.Itoa(port)
|
|
|
|
return net.JoinHostPort(host, portStr)
|
|
|
|
}
|
2020-04-24 14:50:21 +00:00
|
|
|
|
|
|
|
// SortedLabels represents sorted labels.
|
|
|
|
type SortedLabels []prompbmarshal.Label
|
|
|
|
|
2021-10-19 10:19:18 +00:00
|
|
|
// GetByName returns the label with the given name from sls.
|
|
|
|
func (sls *SortedLabels) GetByName(name string) string {
|
|
|
|
for _, lb := range *sls {
|
|
|
|
if lb.Name == name {
|
|
|
|
return lb.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2020-04-24 14:50:21 +00:00
|
|
|
// UnmarshalJSON unmarshals JSON from data.
|
|
|
|
func (sls *SortedLabels) UnmarshalJSON(data []byte) error {
|
|
|
|
var m map[string]string
|
|
|
|
if err := json.Unmarshal(data, &m); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*sls = GetSortedLabels(m)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSortedLabels returns SortedLabels built from m.
|
|
|
|
func GetSortedLabels(m map[string]string) SortedLabels {
|
|
|
|
a := make([]prompbmarshal.Label, 0, len(m))
|
|
|
|
for k, v := range m {
|
|
|
|
a = append(a, prompbmarshal.Label{
|
|
|
|
Name: k,
|
|
|
|
Value: v,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
sort.Slice(a, func(i, j int) bool {
|
|
|
|
return a[i].Name < a[j].Name
|
|
|
|
})
|
|
|
|
return a
|
|
|
|
}
|