grafana/pkg/services/live/remotewrite/convert.go

140 lines
2.7 KiB
Go

package remotewrite
import (
"strings"
"unicode"
)
type table struct {
First *unicode.RangeTable
Rest *unicode.RangeTable
}
var metricNameTable = table{
First: &unicode.RangeTable{
R16: []unicode.Range16{
{0x003A, 0x003A, 1}, // :
{0x0041, 0x005A, 1}, // A-Z
{0x005F, 0x005F, 1}, // _
{0x0061, 0x007A, 1}, // a-z
},
LatinOffset: 4,
},
Rest: &unicode.RangeTable{
R16: []unicode.Range16{
{0x0030, 0x003A, 1}, // 0-:
{0x0041, 0x005A, 1}, // A-Z
{0x005F, 0x005F, 1}, // _
{0x0061, 0x007A, 1}, // a-z
},
LatinOffset: 4,
},
}
var labelNameTable = table{
First: &unicode.RangeTable{
R16: []unicode.Range16{
{0x0041, 0x005A, 1}, // A-Z
{0x005F, 0x005F, 1}, // _
{0x0061, 0x007A, 1}, // a-z
},
LatinOffset: 3,
},
Rest: &unicode.RangeTable{
R16: []unicode.Range16{
{0x0030, 0x0039, 1}, // 0-9
{0x0041, 0x005A, 1}, // A-Z
{0x005F, 0x005F, 1}, // _
{0x0061, 0x007A, 1}, // a-z
},
LatinOffset: 4,
},
}
func isValid(name string, table table) bool {
if name == "" {
return false
}
for i, r := range name {
switch {
case i == 0:
if !unicode.In(r, table.First) {
return false
}
default:
if !unicode.In(r, table.Rest) {
return false
}
}
}
return true
}
// Sanitize checks if the name is valid according to the table. If not, it
// attempts to replaces invalid runes with an underscore to create a valid
// name.
func sanitize(name string, table table) (string, bool) {
if isValid(name, table) {
return name, true
}
var b strings.Builder
for i, r := range name {
switch {
case i == 0:
if unicode.In(r, table.First) {
b.WriteRune(r)
}
default:
if unicode.In(r, table.Rest) {
b.WriteRune(r)
} else {
b.WriteString("_")
}
}
}
name = strings.Trim(b.String(), "_")
if name == "" {
return "", false
}
return name, true
}
// sanitizeMetricName checks if the name is a valid Prometheus metric name. If
// not, it attempts to replaces invalid runes with an underscore to create a
// valid name.
func sanitizeMetricName(name string) (string, bool) {
return sanitize(name, metricNameTable)
}
// sanitizeLabelName checks if the name is a valid Prometheus label name. If
// not, it attempts to replaces invalid runes with an underscore to create a
// valid name.
func sanitizeLabelName(name string) (string, bool) {
return sanitize(name, labelNameTable)
}
// sampleValue converts a field value into a value suitable for a simple sample value.
func sampleValue(value interface{}) (float64, bool) {
switch v := value.(type) {
case float64:
return v, true
case int64:
return float64(v), true
case uint64:
return float64(v), true
case bool:
if v {
return 1.0, true
}
return 0.0, true
default:
return 0, false
}
}