Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lib/protoparser/opentelemetry: follow-up after 47892b4
- 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 https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/b8655058501bed61a06bb660869051491f46840b/pkg/translator/prometheus/normalize_name.go - Extract the code responsible for Prometheus metric names' normalization into a separate file (santize.go) Updates #6037 Updates #6035
- Loading branch information
Showing
9 changed files
with
291 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package stream | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/opentelemetry/pb" | ||
) | ||
|
||
func TestSanitizePrometheusLabelName(t *testing.T) { | ||
f := func(labelName, expectedResult string) { | ||
t.Helper() | ||
|
||
result := sanitizePrometheusLabelName(labelName) | ||
if result != expectedResult { | ||
t.Fatalf("unexpected result; got %q; want %q", result, expectedResult) | ||
} | ||
} | ||
|
||
f("", "") | ||
f("foo", "foo") | ||
f("foo_bar/baz:abc", "foo_bar_baz_abc") | ||
f("1foo", "key_1foo") | ||
f("_foo", "key_foo") | ||
f("__bar", "__bar") | ||
} | ||
|
||
func TestSanitizePrometheusMetricName(t *testing.T) { | ||
f := func(m *pb.Metric, expectedResult string) { | ||
t.Helper() | ||
|
||
result := sanitizePrometheusMetricName(m) | ||
if result != expectedResult { | ||
t.Fatalf("unexpected result; got %q; want %q", result, expectedResult) | ||
} | ||
} | ||
|
||
f(&pb.Metric{}, "") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo", | ||
}, "foo") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo", | ||
Unit: "s", | ||
}, "foo_seconds") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_seconds", | ||
Unit: "s", | ||
}, "foo_seconds") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo", | ||
Sum: &pb.Sum{ | ||
IsMonotonic: true, | ||
}, | ||
}, "foo_total") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_total", | ||
Sum: &pb.Sum{ | ||
IsMonotonic: true, | ||
}, | ||
}, "foo_total") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo", | ||
Sum: &pb.Sum{ | ||
IsMonotonic: true, | ||
}, | ||
Unit: "s", | ||
}, "foo_seconds_total") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_seconds", | ||
Sum: &pb.Sum{ | ||
IsMonotonic: true, | ||
}, | ||
Unit: "s", | ||
}, "foo_seconds_total") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_total", | ||
Sum: &pb.Sum{ | ||
IsMonotonic: true, | ||
}, | ||
Unit: "s", | ||
}, "foo_seconds_total") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_seconds_total", | ||
Sum: &pb.Sum{ | ||
IsMonotonic: true, | ||
}, | ||
Unit: "s", | ||
}, "foo_seconds_total") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_total_seconds", | ||
Sum: &pb.Sum{ | ||
IsMonotonic: true, | ||
}, | ||
Unit: "s", | ||
}, "foo_seconds_total") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo", | ||
Gauge: &pb.Gauge{}, | ||
Unit: "1", | ||
}, "foo_ratio") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo", | ||
Unit: "m/s", | ||
}, "foo_meters_per_second") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_second", | ||
Unit: "m/s", | ||
}, "foo_second_meters") | ||
|
||
f(&pb.Metric{ | ||
Name: "foo_meters", | ||
Unit: "m/s", | ||
}, "foo_meters_per_second") | ||
} |
Oops, something went wrong.