Allow colons in the prometheus metric name (#6751)
This commit is contained in:
parent
09f9b70354
commit
1f7b68a2b2
|
@ -113,7 +113,7 @@ func (c *Collection) createLabels(metric telegraf.Metric) []LabelPair {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
name, ok := SanitizeName(tag.Key)
|
name, ok := SanitizeLabelName(tag.Key)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ func (c *Collection) createLabels(metric telegraf.Metric) []LabelPair {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
name, ok := SanitizeName(field.Key)
|
name, ok := SanitizeLabelName(field.Key)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ func (c *Collection) Add(metric telegraf.Metric) {
|
||||||
labels := c.createLabels(metric)
|
labels := c.createLabels(metric)
|
||||||
for _, field := range metric.FieldList() {
|
for _, field := range metric.FieldList() {
|
||||||
metricName := MetricName(metric.Name(), field.Key, metric.Type())
|
metricName := MetricName(metric.Name(), field.Key, metric.Type())
|
||||||
metricName, ok := SanitizeName(metricName)
|
metricName, ok := SanitizeMetricName(metricName)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,26 +8,53 @@ import (
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
var FirstTable = &unicode.RangeTable{
|
type Table struct {
|
||||||
R16: []unicode.Range16{
|
First *unicode.RangeTable
|
||||||
{0x0041, 0x005A, 1}, // A-Z
|
Rest *unicode.RangeTable
|
||||||
{0x005F, 0x005F, 1}, // _
|
|
||||||
{0x0061, 0x007A, 1}, // a-z
|
|
||||||
},
|
|
||||||
LatinOffset: 3,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var RestTable = &unicode.RangeTable{
|
var MetricNameTable = Table{
|
||||||
R16: []unicode.Range16{
|
First: &unicode.RangeTable{
|
||||||
{0x0030, 0x0039, 1}, // 0-9
|
R16: []unicode.Range16{
|
||||||
{0x0041, 0x005A, 1}, // A-Z
|
{0x003A, 0x003A, 1}, // :
|
||||||
{0x005F, 0x005F, 1}, // _
|
{0x0041, 0x005A, 1}, // A-Z
|
||||||
{0x0061, 0x007A, 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,
|
||||||
},
|
},
|
||||||
LatinOffset: 4,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValid(name string) bool {
|
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 == "" {
|
if name == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -35,11 +62,11 @@ func isValid(name string) bool {
|
||||||
for i, r := range name {
|
for i, r := range name {
|
||||||
switch {
|
switch {
|
||||||
case i == 0:
|
case i == 0:
|
||||||
if !unicode.In(r, FirstTable) {
|
if !unicode.In(r, table.First) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if !unicode.In(r, RestTable) {
|
if !unicode.In(r, table.Rest) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,12 +75,11 @@ func isValid(name string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SanitizeName check if the name is a valid Prometheus metric name and label
|
// Sanitize checks if the name is valid according to the table. If not, it
|
||||||
// name. If not, it attempts to replaces invalid runes with an underscore to
|
// attempts to replaces invalid runes with an underscore to create a valid
|
||||||
// create a valid name. Returns the metric name and true if the name is valid
|
// name.
|
||||||
// to use.
|
func sanitize(name string, table Table) (string, bool) {
|
||||||
func SanitizeName(name string) (string, bool) {
|
if isValid(name, table) {
|
||||||
if isValid(name) {
|
|
||||||
return name, true
|
return name, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,11 +88,11 @@ func SanitizeName(name string) (string, bool) {
|
||||||
for i, r := range name {
|
for i, r := range name {
|
||||||
switch {
|
switch {
|
||||||
case i == 0:
|
case i == 0:
|
||||||
if unicode.In(r, FirstTable) {
|
if unicode.In(r, table.First) {
|
||||||
b.WriteRune(r)
|
b.WriteRune(r)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if unicode.In(r, RestTable) {
|
if unicode.In(r, table.Rest) {
|
||||||
b.WriteRune(r)
|
b.WriteRune(r)
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("_")
|
b.WriteString("_")
|
||||||
|
@ -82,6 +108,20 @@ func SanitizeName(name string) (string, bool) {
|
||||||
return name, true
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
// MetricName returns the Prometheus metric name.
|
// MetricName returns the Prometheus metric name.
|
||||||
func MetricName(measurement, fieldKey string, valueType telegraf.ValueType) string {
|
func MetricName(measurement, fieldKey string, valueType telegraf.ValueType) string {
|
||||||
switch valueType {
|
switch valueType {
|
||||||
|
|
|
@ -409,6 +409,42 @@ rpc_duration_seconds_count 2693
|
||||||
# HELP cpu_time_idle Telegraf collected metric
|
# HELP cpu_time_idle Telegraf collected metric
|
||||||
# TYPE cpu_time_idle untyped
|
# TYPE cpu_time_idle untyped
|
||||||
cpu_time_idle 43
|
cpu_time_idle 43
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "colons are not replaced in metric name from measurement",
|
||||||
|
metrics: []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"cpu::xyzzy",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"time_idle": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
expected: []byte(`
|
||||||
|
# HELP cpu::xyzzy_time_idle Telegraf collected metric
|
||||||
|
# TYPE cpu::xyzzy_time_idle untyped
|
||||||
|
cpu::xyzzy_time_idle 42
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "colons are not replaced in metric name from field",
|
||||||
|
metrics: []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"time:idle": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
expected: []byte(`
|
||||||
|
# HELP cpu_time:idle Telegraf collected metric
|
||||||
|
# TYPE cpu_time:idle untyped
|
||||||
|
cpu_time:idle 42
|
||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -429,6 +465,26 @@ cpu_time_idle 43
|
||||||
# HELP cpu_time_idle Telegraf collected metric
|
# HELP cpu_time_idle Telegraf collected metric
|
||||||
# TYPE cpu_time_idle untyped
|
# TYPE cpu_time_idle untyped
|
||||||
cpu_time_idle{host_name="example.org"} 42
|
cpu_time_idle{host_name="example.org"} 42
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "colons are replaced in label name",
|
||||||
|
metrics: []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{
|
||||||
|
"host:name": "example.org",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"time_idle": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
expected: []byte(`
|
||||||
|
# HELP cpu_time_idle Telegraf collected metric
|
||||||
|
# TYPE cpu_time_idle untyped
|
||||||
|
cpu_time_idle{host_name="example.org"} 42
|
||||||
`),
|
`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue