198 lines
3.8 KiB
Go
198 lines
3.8 KiB
Go
package vos
|
||
|
||
import (
|
||
"bytes"
|
||
"fmt"
|
||
"sort"
|
||
"strconv"
|
||
"strings"
|
||
"sync"
|
||
|
||
"github.com/didi/nightingale/v5/pkg/istr"
|
||
)
|
||
|
||
const (
|
||
SPLIT = "/"
|
||
)
|
||
|
||
type MetricPoint struct {
|
||
PK string `json:"pk"` // 内部字段,ident、metric、sorted(tags)拼接之后算md5
|
||
Ident string `json:"ident"` // 资源标识,跟资源无关的监控数据,该字段为空
|
||
Alias string `json:"alias"` // 资源名称,跟资源无关的监控数据,该字段为空
|
||
Metric string `json:"metric"` // 监控指标名称
|
||
TagsMap map[string]string `json:"tags"` // 监控数据标签
|
||
Time int64 `json:"time"` // 时间戳,单位是秒
|
||
ValueUntyped interface{} `json:"value"` // 监控数据数值,可以是int float string,但最终要能转换为float64
|
||
Value float64 `json:"-"` // 内部字段,最终转换之后的float64数值
|
||
}
|
||
|
||
func (m *MetricPoint) Tidy(now int64) error {
|
||
if m == nil {
|
||
return fmt.Errorf("point is nil")
|
||
}
|
||
|
||
// 时间超前5分钟则报错
|
||
if m.Time-now > 300 {
|
||
return fmt.Errorf("point_time(%d) - server_time(%d) = %d. use ntp to calibrate host time?", m.Time, now, m.Time-now)
|
||
}
|
||
|
||
// 时间延迟30分钟则报错
|
||
if m.Time-now < -1800 {
|
||
return fmt.Errorf("point_time(%d) - server_time(%d) = %d. use ntp to calibrate host time?", m.Time, now, m.Time-now)
|
||
}
|
||
|
||
if m.Time <= 0 {
|
||
m.Time = now
|
||
}
|
||
|
||
if m.Metric == "" {
|
||
return fmt.Errorf("metric is blank")
|
||
}
|
||
|
||
if istr.SampleKeyInvalid(m.Metric) {
|
||
return fmt.Errorf("metric:%s contains reserved words", m.Metric)
|
||
}
|
||
|
||
if istr.SampleKeyInvalid(m.Ident) {
|
||
return fmt.Errorf("ident:%s contains reserved words", m.Ident)
|
||
}
|
||
|
||
if m.ValueUntyped == nil {
|
||
return fmt.Errorf("value is nil")
|
||
}
|
||
|
||
safemap := make(map[string]string)
|
||
for k, v := range m.TagsMap {
|
||
if istr.SampleKeyInvalid(k) {
|
||
return fmt.Errorf("tag key: %s contains reserved words", k)
|
||
}
|
||
|
||
if len(k) == 0 {
|
||
return fmt.Errorf("tag key is blank, metric: %s", m.Metric)
|
||
}
|
||
|
||
v = strings.Map(func(r rune) rune {
|
||
if r == '\t' ||
|
||
r == '\r' ||
|
||
r == '\n' ||
|
||
r == ',' {
|
||
return '_'
|
||
}
|
||
return r
|
||
}, v)
|
||
|
||
if len(v) == 0 {
|
||
safemap[k] = "nil"
|
||
} else {
|
||
safemap[k] = v
|
||
}
|
||
}
|
||
|
||
m.TagsMap = safemap
|
||
|
||
valid := true
|
||
var vv float64
|
||
var err error
|
||
|
||
switch cv := m.ValueUntyped.(type) {
|
||
case string:
|
||
vv, err = strconv.ParseFloat(cv, 64)
|
||
if err != nil {
|
||
valid = false
|
||
}
|
||
case float64:
|
||
vv = cv
|
||
case uint64:
|
||
vv = float64(cv)
|
||
case int64:
|
||
vv = float64(cv)
|
||
case int:
|
||
vv = float64(cv)
|
||
default:
|
||
valid = false
|
||
}
|
||
|
||
if !valid {
|
||
return fmt.Errorf("value(%v) is illegal", m.Value)
|
||
}
|
||
|
||
m.Value = vv
|
||
|
||
return nil
|
||
}
|
||
|
||
func DictedTagList(tags []string) map[string]string {
|
||
rmap := make(map[string]string)
|
||
if len(tags) == 0 {
|
||
return rmap
|
||
}
|
||
|
||
for _, tag := range tags {
|
||
pair := strings.SplitN(tag, "=", 2)
|
||
if len(pair) != 2 {
|
||
continue
|
||
}
|
||
|
||
if pair[0] == "" {
|
||
continue
|
||
}
|
||
|
||
if pair[1] == "" {
|
||
rmap[pair[0]] = "nil"
|
||
} else {
|
||
rmap[pair[0]] = pair[1]
|
||
}
|
||
}
|
||
|
||
return rmap
|
||
}
|
||
|
||
var bufferPool = sync.Pool{
|
||
New: func() interface{} {
|
||
return new(bytes.Buffer)
|
||
},
|
||
}
|
||
|
||
func SortedTags(tags map[string]string) string {
|
||
if tags == nil {
|
||
return ""
|
||
}
|
||
|
||
size := len(tags)
|
||
if size == 0 {
|
||
return ""
|
||
}
|
||
|
||
ret := bufferPool.Get().(*bytes.Buffer)
|
||
ret.Reset()
|
||
defer bufferPool.Put(ret)
|
||
|
||
if size == 1 {
|
||
for k, v := range tags {
|
||
ret.WriteString(k)
|
||
ret.WriteString("=")
|
||
ret.WriteString(v)
|
||
}
|
||
return ret.String()
|
||
}
|
||
|
||
keys := make([]string, size)
|
||
i := 0
|
||
for k := range tags {
|
||
keys[i] = k
|
||
i++
|
||
}
|
||
sort.Strings(keys)
|
||
|
||
for j, key := range keys {
|
||
ret.WriteString(key)
|
||
ret.WriteString("=")
|
||
ret.WriteString(tags[key])
|
||
if j != size-1 {
|
||
ret.WriteString(",")
|
||
}
|
||
}
|
||
|
||
return ret.String()
|
||
}
|