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) }