mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
fb42380ef3
- Rename -opentelemetry.sanitizeMetrics command-line flag to more clear -opentelemetry.usePrometheusNaming
- Clarify the description of the change at docs/CHANGELOG.md
- Rename promrelabel.SanitizeLabelNameParts to more clear promrelabel.SplitMetricNameToTokens
- Properly split metric names at '_' char in promerlabel.SplitMetricNameToTokens.
- Add tests for various edge cases for Prometheus metric names' normalization
according to the code at b865505850/pkg/translator/prometheus/normalize_name.go
- Extract the code responsible for Prometheus metric names' normalization into a separate file (santize.go)
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6037
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6035
138 lines
3.7 KiB
Go
138 lines
3.7 KiB
Go
package stream
|
|
|
|
import (
|
|
"flag"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/opentelemetry/pb"
|
|
)
|
|
|
|
var (
|
|
usePrometheusNaming = flag.Bool("opentelemetry.usePrometheusNaming", false, "Whether to convert metric names and labels into Prometheus-compatible format for the metrics ingested "+
|
|
"via OpenTelemetry protocol; see https://docs.victoriametrics.com/#sending-data-via-opentelemetry")
|
|
)
|
|
|
|
// unitMap is obtained from https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/b8655058501bed61a06bb660869051491f46840b/pkg/translator/prometheus/normalize_name.go#L19
|
|
var unitMap = map[string]string{
|
|
// Time
|
|
"d": "days",
|
|
"h": "hours",
|
|
"min": "minutes",
|
|
"s": "seconds",
|
|
"ms": "milliseconds",
|
|
"us": "microseconds",
|
|
"ns": "nanoseconds",
|
|
|
|
// Bytes
|
|
"By": "bytes",
|
|
"KiBy": "kibibytes",
|
|
"MiBy": "mebibytes",
|
|
"GiBy": "gibibytes",
|
|
"TiBy": "tibibytes",
|
|
"KBy": "kilobytes",
|
|
"MBy": "megabytes",
|
|
"GBy": "gigabytes",
|
|
"TBy": "terabytes",
|
|
|
|
// SI
|
|
"m": "meters",
|
|
"V": "volts",
|
|
"A": "amperes",
|
|
"J": "joules",
|
|
"W": "watts",
|
|
"g": "grams",
|
|
|
|
// Misc
|
|
"Cel": "celsius",
|
|
"Hz": "hertz",
|
|
"1": "",
|
|
"%": "percent",
|
|
}
|
|
|
|
// perUnitMap is copied from https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/b8655058501bed61a06bb660869051491f46840b/pkg/translator/prometheus/normalize_name.go#L58
|
|
var perUnitMap = map[string]string{
|
|
"s": "second",
|
|
"m": "minute",
|
|
"h": "hour",
|
|
"d": "day",
|
|
"w": "week",
|
|
"mo": "month",
|
|
"y": "year",
|
|
}
|
|
|
|
// See https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/b8655058501bed61a06bb660869051491f46840b/pkg/translator/prometheus/normalize_label.go#L26
|
|
func sanitizeLabelName(labelName string) string {
|
|
if !*usePrometheusNaming {
|
|
return labelName
|
|
}
|
|
return sanitizePrometheusLabelName(labelName)
|
|
}
|
|
|
|
func sanitizePrometheusLabelName(labelName string) string {
|
|
if len(labelName) == 0 {
|
|
return ""
|
|
}
|
|
labelName = promrelabel.SanitizeLabelName(labelName)
|
|
if labelName[0] >= '0' && labelName[0] <= '9' {
|
|
return "key_" + labelName
|
|
} else if strings.HasPrefix(labelName, "_") && !strings.HasPrefix(labelName, "__") {
|
|
return "key" + labelName
|
|
}
|
|
return labelName
|
|
}
|
|
|
|
// See https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/b8655058501bed61a06bb660869051491f46840b/pkg/translator/prometheus/normalize_name.go#L83
|
|
func sanitizeMetricName(m *pb.Metric) string {
|
|
if !*usePrometheusNaming {
|
|
return m.Name
|
|
}
|
|
return sanitizePrometheusMetricName(m)
|
|
}
|
|
|
|
func sanitizePrometheusMetricName(m *pb.Metric) string {
|
|
nameTokens := promrelabel.SplitMetricNameToTokens(m.Name)
|
|
|
|
unitTokens := strings.SplitN(m.Unit, "/", 2)
|
|
if len(unitTokens) > 0 {
|
|
mainUnit := strings.TrimSpace(unitTokens[0])
|
|
if mainUnit != "" && !strings.ContainsAny(mainUnit, "{}") {
|
|
if u, ok := unitMap[mainUnit]; ok {
|
|
mainUnit = u
|
|
}
|
|
if mainUnit != "" && !slices.Contains(nameTokens, mainUnit) {
|
|
nameTokens = append(nameTokens, mainUnit)
|
|
}
|
|
}
|
|
|
|
if len(unitTokens) > 1 {
|
|
perUnit := strings.TrimSpace(unitTokens[1])
|
|
if perUnit != "" && !strings.ContainsAny(perUnit, "{}") {
|
|
if u, ok := perUnitMap[perUnit]; ok {
|
|
perUnit = u
|
|
}
|
|
if perUnit != "" && !slices.Contains(nameTokens, perUnit) {
|
|
nameTokens = append(nameTokens, "per", perUnit)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if m.Sum != nil && m.Sum.IsMonotonic {
|
|
nameTokens = moveOrAppend(nameTokens, "total")
|
|
} else if m.Unit == "1" && m.Gauge != nil {
|
|
nameTokens = moveOrAppend(nameTokens, "ratio")
|
|
}
|
|
return strings.Join(nameTokens, "_")
|
|
}
|
|
|
|
func moveOrAppend(tokens []string, value string) []string {
|
|
for i := range tokens {
|
|
if tokens[i] == value {
|
|
tokens = append(tokens[:i], tokens[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
return append(tokens, value)
|
|
}
|