From c78e63de57454291dba1e38c655c5eb17bb4360b Mon Sep 17 00:00:00 2001 From: Naman-B-Parlecha Date: Fri, 10 Apr 2026 23:08:59 +0530 Subject: [PATCH 1/6] Fix unitMap entries for TiBy and kilobytes Signed-off-by: Naman-B-Parlecha Signed-off-by: Naman-B-Parlecha --- metric_namer.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metric_namer.go b/metric_namer.go index d958a0f..30a8ef6 100644 --- a/metric_namer.go +++ b/metric_namer.go @@ -46,8 +46,8 @@ var unitMap = map[string]string{ "KiBy": "kibibytes", "MiBy": "mebibytes", "GiBy": "gibibytes", - "TiBy": "tibibytes", - "KBy": "kilobytes", + "TiBy": "tebibytes", + "kBy": "kilobytes", "MBy": "megabytes", "GBy": "gigabytes", "TBy": "terabytes", From 64689d9772347623a5b3b62b4334ddb67d809c34 Mon Sep 17 00:00:00 2001 From: Naman-B-Parlecha Date: Fri, 10 Apr 2026 23:14:48 +0530 Subject: [PATCH 2/6] fix: UCUM units for tebibytes and kilobytes in tests Signed-off-by: Naman-B-Parlecha --- metric_namer_test.go | 8 ++++---- unit_namer.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/metric_namer_test.go b/metric_namer_test.go index adfd2db..f5f2541 100644 --- a/metric_namer_test.go +++ b/metric_namer_test.go @@ -791,7 +791,7 @@ func TestMetricNamer_Build(t *testing.T) { wantUnitName: "gibibytes", }, { - name: "metric with tibibytes unit", + name: "metric with tebibytes unit", namer: MetricNamer{ UTF8Allowed: false, WithMetricSuffixes: true, @@ -801,8 +801,8 @@ func TestMetricNamer_Build(t *testing.T) { Unit: "TiBy", Type: MetricTypeGauge, }, - wantMetricName: "capacity_tibibytes", - wantUnitName: "tibibytes", + wantMetricName: "capacity_tebibytes", + wantUnitName: "tebibytes", }, { name: "metric with kilobytes unit", @@ -812,7 +812,7 @@ func TestMetricNamer_Build(t *testing.T) { }, metric: Metric{ Name: "transfer", - Unit: "KBy", + Unit: "kBy", Type: MetricTypeGauge, }, wantMetricName: "transfer_kilobytes", diff --git a/unit_namer.go b/unit_namer.go index bb6d4f8..6118cde 100644 --- a/unit_namer.go +++ b/unit_namer.go @@ -33,7 +33,7 @@ type UnitNamer struct { // // Unit mappings include: // - Time: s→seconds, ms→milliseconds, h→hours -// - Bytes: By→bytes, KBy→kilobytes, MBy→megabytes +// - Bytes: By→bytes, kBy→kilobytes, MBy→megabytes // - SI: m→meters, V→volts, W→watts // - Special: 1→"" (empty), %→percent // From e52f11d193d5d534fa9cac0be2d331cd059594de Mon Sep 17 00:00:00 2001 From: Naman-B-Parlecha Date: Sun, 12 Apr 2026 20:45:23 +0530 Subject: [PATCH 3/6] fix: add backwards compatibility for kilobytes unit in unitMap Signed-off-by: Naman-B-Parlecha --- metric_namer.go | 8 +++++--- metric_namer_test.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/metric_namer.go b/metric_namer.go index 30a8ef6..b5d38a1 100644 --- a/metric_namer.go +++ b/metric_namer.go @@ -48,9 +48,11 @@ var unitMap = map[string]string{ "GiBy": "gibibytes", "TiBy": "tebibytes", "kBy": "kilobytes", - "MBy": "megabytes", - "GBy": "gigabytes", - "TBy": "terabytes", + // for backwards compatibility. + "KBy": "kilobytes", + "MBy": "megabytes", + "GBy": "gigabytes", + "TBy": "terabytes", // SI "m": "meters", diff --git a/metric_namer_test.go b/metric_namer_test.go index f5f2541..acdc70f 100644 --- a/metric_namer_test.go +++ b/metric_namer_test.go @@ -818,6 +818,20 @@ func TestMetricNamer_Build(t *testing.T) { wantMetricName: "transfer_kilobytes", wantUnitName: "kilobytes", }, + { + name: "metric with kilobytes unit (backwards compatibility)", + namer: MetricNamer{ + UTF8Allowed: false, + WithMetricSuffixes: true, + }, + metric: Metric{ + Name: "transfer", + Unit: "KBy", + Type: MetricTypeGauge, + }, + wantMetricName: "transfer_kilobytes", + wantUnitName: "kilobytes", + }, { name: "metric with megabytes unit", namer: MetricNamer{ From 3df83722ff47c437761b95c7490ac807309cf664 Mon Sep 17 00:00:00 2001 From: Naman-B-Parlecha Date: Wed, 29 Apr 2026 01:40:45 +0530 Subject: [PATCH 4/6] feat: added WithUpdatedMetricsMapping option for corrected UCUM unit suffixes Signed-off-by: Naman-B-Parlecha --- README.md | 6 +++++- metric_namer.go | 46 +++++++++++++++++++++++++++++++------------- metric_namer_test.go | 32 +++++++++++++++++++++++------- unit_namer.go | 19 +++++++++++++----- 4 files changed, 77 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 663d736..3c64309 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Part of the [Prometheus](https://prometheus.io/) ecosystem, following the [OpenT - **Namespace Support**: Add configurable namespace prefixes - **UTF-8 Support**: Choose between Prometheus legacy scheme compliant metric/label names (`[a-zA-Z0-9:_]`) or untranslated metric/label names - **Translation Strategy Configuration**: Select a translation strategy with a standard set of strings. +- **Configurable Updated Unit Mappings**: Opt in to corrected UCUM unit suffixes via `WithUpdatedMetricsMapping()` option. ## Installation @@ -32,7 +33,10 @@ import ( func main() { // Create a metric namer using traditional Prometheus name translation, with suffixes added and UTF-8 disallowed. strategy := otlptranslator.UnderscoreEscapingWithSuffixes - namer := otlptranslator.NewMetricNamer("myapp", strategy) + + // WithUpdatedMetricsMapping enables corrected UCUM unit mappings: + // TiBy -> "tebibytes" instead of "tibibytes" and kBy -> "kilobytes". + namer := otlptranslator.NewMetricNamer("myapp", strategy, otlptranslator.WithUpdatedMetricsMapping()) // Translate OTLP metric to Prometheus format metric := otlptranslator.Metric{ diff --git a/metric_namer.go b/metric_namer.go index b5d38a1..1466899 100644 --- a/metric_namer.go +++ b/metric_namer.go @@ -46,13 +46,11 @@ var unitMap = map[string]string{ "KiBy": "kibibytes", "MiBy": "mebibytes", "GiBy": "gibibytes", - "TiBy": "tebibytes", - "kBy": "kilobytes", - // for backwards compatibility. - "KBy": "kilobytes", - "MBy": "megabytes", - "GBy": "gigabytes", - "TBy": "terabytes", + "TiBy": "tibibytes", + "KBy": "kilobytes", + "MBy": "megabytes", + "GBy": "gigabytes", + "TBy": "terabytes", // SI "m": "meters", @@ -69,6 +67,11 @@ var unitMap = map[string]string{ "%": "percent", } +var updatedUnitMap = map[string]string{ + "TiBy": "tebibytes", + "kBy": "kilobytes", +} + // The map that translates the "per" unit. // Example: s => per second (singular). var perUnitMap = map[string]string{ @@ -102,16 +105,33 @@ type MetricNamer struct { Namespace string WithMetricSuffixes bool UTF8Allowed bool + // to fix the UCUM metrics suffix tebibyte and kBy + UpdatedMetricsMapping bool +} + +type Option func(*MetricNamer) + +// WithUpdatedMetricsMapping enables corrected UCUM unit mappings. +// It maps TiBy to "tebibytes" instead of "tibibytes" +// and adds kBy as "kilobytes" the correct notation. +func WithUpdatedMetricsMapping() Option { + return func(mn *MetricNamer) { + mn.UpdatedMetricsMapping = true + } } // NewMetricNamer creates a MetricNamer with the specified namespace (can be // blank) and the requested Translation Strategy. -func NewMetricNamer(namespace string, strategy TranslationStrategyOption) MetricNamer { - return MetricNamer{ +func NewMetricNamer(namespace string, strategy TranslationStrategyOption, opts ...Option) MetricNamer { + mn := MetricNamer{ Namespace: namespace, WithMetricSuffixes: strategy.ShouldAddSuffixes(), UTF8Allowed: !strategy.ShouldEscape(), } + for _, opt := range opts { + opt(&mn) + } + return mn } // Metric is a helper struct that holds information about a metric. @@ -180,7 +200,7 @@ func (mn *MetricNamer) buildCompliantMetricName(name, unit string, metricType Me // Full normalization following standard Prometheus naming conventions if mn.WithMetricSuffixes { - normalizedName = normalizeName(name, unit, metricType, mn.Namespace) + normalizedName = normalizeName(name, unit, metricType, mn.Namespace, mn.UpdatedMetricsMapping) return } @@ -224,7 +244,7 @@ func replaceInvalidMetricChar(r rune) rune { } // Build a normalized name for the specified metric. -func normalizeName(name, unit string, metricType MetricType, namespace string) string { +func normalizeName(name, unit string, metricType MetricType, namespace string, updatedMetricsMapping bool) string { // Split metric name into "tokens" (of supported metric name runes). // Note that this has the side effect of replacing multiple consecutive underscores with a single underscore. // This is part of the OTel to Prometheus specification: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus. @@ -233,7 +253,7 @@ func normalizeName(name, unit string, metricType MetricType, namespace string) s func(r rune) bool { return !isValidCompliantMetricChar(r) }, ) - mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit) + mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit, updatedMetricsMapping) nameTokens = addUnitTokens(nameTokens, cleanUpUnit(mainUnitSuffix), cleanUpUnit(perUnitSuffix)) // Append _total for Counters @@ -337,7 +357,7 @@ func (mn *MetricNamer) buildMetricName(inputName, unit string, metricType Metric }() } - mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit) + mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit, mn.UpdatedMetricsMapping) if perUnitSuffix != "" { name = trimSuffixAndDelimiter(name, perUnitSuffix) defer func() { diff --git a/metric_namer_test.go b/metric_namer_test.go index acdc70f..bf7a8bf 100644 --- a/metric_namer_test.go +++ b/metric_namer_test.go @@ -791,7 +791,7 @@ func TestMetricNamer_Build(t *testing.T) { wantUnitName: "gibibytes", }, { - name: "metric with tebibytes unit", + name: "metric with tibibytes unit", namer: MetricNamer{ UTF8Allowed: false, WithMetricSuffixes: true, @@ -801,6 +801,21 @@ func TestMetricNamer_Build(t *testing.T) { Unit: "TiBy", Type: MetricTypeGauge, }, + wantMetricName: "capacity_tibibytes", + wantUnitName: "tibibytes", + }, + { + name: "metric with tebibytes unit with flag", + namer: MetricNamer{ + UTF8Allowed: false, + WithMetricSuffixes: true, + UpdatedMetricsMapping: true, + }, + metric: Metric{ + Name: "capacity", + Unit: "TiBy", + Type: MetricTypeGauge, + }, wantMetricName: "capacity_tebibytes", wantUnitName: "tebibytes", }, @@ -812,21 +827,23 @@ func TestMetricNamer_Build(t *testing.T) { }, metric: Metric{ Name: "transfer", - Unit: "kBy", + Unit: "KBy", Type: MetricTypeGauge, }, wantMetricName: "transfer_kilobytes", wantUnitName: "kilobytes", }, + { - name: "metric with kilobytes unit (backwards compatibility)", + name: "metric with kilobytes unit with flag", namer: MetricNamer{ - UTF8Allowed: false, - WithMetricSuffixes: true, + UTF8Allowed: false, + WithMetricSuffixes: true, + UpdatedMetricsMapping: true, }, metric: Metric{ Name: "transfer", - Unit: "KBy", + Unit: "kBy", Type: MetricTypeGauge, }, wantMetricName: "transfer_kilobytes", @@ -1221,7 +1238,8 @@ func TestMetricNamer_Build(t *testing.T) { // Build unit name using UnitNamer to verify correlation when suffixes are enabled if tt.namer.WithMetricSuffixes { unitNamer := UnitNamer{ - UTF8Allowed: tt.namer.UTF8Allowed, + UTF8Allowed: tt.namer.UTF8Allowed, + UpdatedMetricsMapping: tt.namer.UpdatedMetricsMapping, } gotUnitName := unitNamer.Build(tt.metric.Unit) if gotUnitName != tt.wantUnitName { diff --git a/unit_namer.go b/unit_namer.go index 6118cde..b0b4a84 100644 --- a/unit_namer.go +++ b/unit_namer.go @@ -24,7 +24,8 @@ import "strings" // result := namer.Build("s") // "seconds" // result = namer.Build("By/s") // "bytes_per_second" type UnitNamer struct { - UTF8Allowed bool + UTF8Allowed bool + UpdatedMetricsMapping bool } // Build builds a unit name for the specified unit string. @@ -44,7 +45,7 @@ type UnitNamer struct { // namer.Build("requests/s") // "requests_per_second" // namer.Build("1") // "" (dimensionless) func (un *UnitNamer) Build(unit string) string { - mainUnit, perUnit := buildUnitSuffixes(unit) + mainUnit, perUnit := buildUnitSuffixes(unit, un.UpdatedMetricsMapping) if !un.UTF8Allowed { mainUnit, perUnit = cleanUpUnit(mainUnit), cleanUpUnit(perUnit) } @@ -72,7 +73,15 @@ func (un *UnitNamer) Build(unit string) string { // Retrieve the Prometheus "basic" unit corresponding to the specified "basic" unit. // Returns the specified unit if not found in unitMap. -func unitMapGetOrDefault(unit string) string { +func unitMapGetOrDefault(unit string, updatedSuffix bool) string { + if updatedSuffix { + // checks for updatd values, TiBy <-> tebibytes and kBy <-> kilobytes. + if promUnit, ok := updatedUnitMap[unit]; ok { + return promUnit + } + // doesnt return cause what if the unit is not in the updated map but is + // in the original map, we want to check that as well. + } if promUnit, ok := unitMap[unit]; ok { return promUnit } @@ -91,7 +100,7 @@ func perUnitMapGetOrDefault(perUnit string) string { // buildUnitSuffixes builds the main and per unit suffixes for the specified unit // but doesn't do any special character transformation to accommodate Prometheus naming conventions. // Removing trailing underscores or appending suffixes is done in the caller. -func buildUnitSuffixes(unit string) (mainUnitSuffix, perUnitSuffix string) { +func buildUnitSuffixes(unit string, updatedMetricName bool) (mainUnitSuffix, perUnitSuffix string) { // Split unit at the '/' if any unitTokens := strings.SplitN(unit, "/", 2) @@ -100,7 +109,7 @@ func buildUnitSuffixes(unit string) (mainUnitSuffix, perUnitSuffix string) { // Update if not blank and doesn't contain '{}' mainUnitOTel := strings.TrimSpace(unitTokens[0]) if mainUnitOTel != "" && !strings.ContainsAny(mainUnitOTel, "{}") { - mainUnitSuffix = unitMapGetOrDefault(mainUnitOTel) + mainUnitSuffix = unitMapGetOrDefault(mainUnitOTel, updatedMetricName) } // Per unit From 52e495d3cd61fd4f1de808363656029b8776b11b Mon Sep 17 00:00:00 2001 From: Naman-B-Parlecha Date: Wed, 29 Apr 2026 01:46:53 +0530 Subject: [PATCH 5/6] fix: rename UpdatedMetricsMapping to UpdatedMetricMapping Signed-off-by: Naman-B-Parlecha --- README.md | 6 +++--- metric_namer.go | 12 ++++++------ metric_namer_test.go | 6 +++--- unit_namer.go | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 3c64309..3eb4789 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Part of the [Prometheus](https://prometheus.io/) ecosystem, following the [OpenT - **Namespace Support**: Add configurable namespace prefixes - **UTF-8 Support**: Choose between Prometheus legacy scheme compliant metric/label names (`[a-zA-Z0-9:_]`) or untranslated metric/label names - **Translation Strategy Configuration**: Select a translation strategy with a standard set of strings. -- **Configurable Updated Unit Mappings**: Opt in to corrected UCUM unit suffixes via `WithUpdatedMetricsMapping()` option. +- **Configurable Updated Unit Mappings**: Opt in to corrected UCUM unit suffixes via `WithUpdatedMetricMapping()` option. ## Installation @@ -34,9 +34,9 @@ func main() { // Create a metric namer using traditional Prometheus name translation, with suffixes added and UTF-8 disallowed. strategy := otlptranslator.UnderscoreEscapingWithSuffixes - // WithUpdatedMetricsMapping enables corrected UCUM unit mappings: + // WithUpdatedMetricMapping enables corrected UCUM unit mappings: // TiBy -> "tebibytes" instead of "tibibytes" and kBy -> "kilobytes". - namer := otlptranslator.NewMetricNamer("myapp", strategy, otlptranslator.WithUpdatedMetricsMapping()) + namer := otlptranslator.NewMetricNamer("myapp", strategy, otlptranslator.WithUpdatedMetricMapping()) // Translate OTLP metric to Prometheus format metric := otlptranslator.Metric{ diff --git a/metric_namer.go b/metric_namer.go index 1466899..ca69eef 100644 --- a/metric_namer.go +++ b/metric_namer.go @@ -106,17 +106,17 @@ type MetricNamer struct { WithMetricSuffixes bool UTF8Allowed bool // to fix the UCUM metrics suffix tebibyte and kBy - UpdatedMetricsMapping bool + UpdatedMetricMapping bool } type Option func(*MetricNamer) -// WithUpdatedMetricsMapping enables corrected UCUM unit mappings. +// WithUpdatedMetricMapping enables corrected UCUM unit mappings. // It maps TiBy to "tebibytes" instead of "tibibytes" // and adds kBy as "kilobytes" the correct notation. -func WithUpdatedMetricsMapping() Option { +func WithUpdatedMetricMapping() Option { return func(mn *MetricNamer) { - mn.UpdatedMetricsMapping = true + mn.UpdatedMetricMapping = true } } @@ -200,7 +200,7 @@ func (mn *MetricNamer) buildCompliantMetricName(name, unit string, metricType Me // Full normalization following standard Prometheus naming conventions if mn.WithMetricSuffixes { - normalizedName = normalizeName(name, unit, metricType, mn.Namespace, mn.UpdatedMetricsMapping) + normalizedName = normalizeName(name, unit, metricType, mn.Namespace, mn.UpdatedMetricMapping) return } @@ -357,7 +357,7 @@ func (mn *MetricNamer) buildMetricName(inputName, unit string, metricType Metric }() } - mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit, mn.UpdatedMetricsMapping) + mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit, mn.UpdatedMetricMapping) if perUnitSuffix != "" { name = trimSuffixAndDelimiter(name, perUnitSuffix) defer func() { diff --git a/metric_namer_test.go b/metric_namer_test.go index bf7a8bf..99f7869 100644 --- a/metric_namer_test.go +++ b/metric_namer_test.go @@ -809,7 +809,7 @@ func TestMetricNamer_Build(t *testing.T) { namer: MetricNamer{ UTF8Allowed: false, WithMetricSuffixes: true, - UpdatedMetricsMapping: true, + UpdatedMetricMapping: true, }, metric: Metric{ Name: "capacity", @@ -839,7 +839,7 @@ func TestMetricNamer_Build(t *testing.T) { namer: MetricNamer{ UTF8Allowed: false, WithMetricSuffixes: true, - UpdatedMetricsMapping: true, + UpdatedMetricMapping: true, }, metric: Metric{ Name: "transfer", @@ -1239,7 +1239,7 @@ func TestMetricNamer_Build(t *testing.T) { if tt.namer.WithMetricSuffixes { unitNamer := UnitNamer{ UTF8Allowed: tt.namer.UTF8Allowed, - UpdatedMetricsMapping: tt.namer.UpdatedMetricsMapping, + UpdatedMetricMapping: tt.namer.UpdatedMetricMapping, } gotUnitName := unitNamer.Build(tt.metric.Unit) if gotUnitName != tt.wantUnitName { diff --git a/unit_namer.go b/unit_namer.go index b0b4a84..dceb8d8 100644 --- a/unit_namer.go +++ b/unit_namer.go @@ -25,7 +25,7 @@ import "strings" // result = namer.Build("By/s") // "bytes_per_second" type UnitNamer struct { UTF8Allowed bool - UpdatedMetricsMapping bool + UpdatedMetricMapping bool } // Build builds a unit name for the specified unit string. @@ -45,7 +45,7 @@ type UnitNamer struct { // namer.Build("requests/s") // "requests_per_second" // namer.Build("1") // "" (dimensionless) func (un *UnitNamer) Build(unit string) string { - mainUnit, perUnit := buildUnitSuffixes(unit, un.UpdatedMetricsMapping) + mainUnit, perUnit := buildUnitSuffixes(unit, un.UpdatedMetricMapping) if !un.UTF8Allowed { mainUnit, perUnit = cleanUpUnit(mainUnit), cleanUpUnit(perUnit) } From 939d618ef823c8bd7648d20f503b987f7fb6b54a Mon Sep 17 00:00:00 2001 From: Naman-B-Parlecha Date: Tue, 12 May 2026 20:26:20 +0530 Subject: [PATCH 6/6] feat: add strategy UnderscoreEscapingWithUpdatedSuffixes and NoUTF8EscapingWithUpdatedSuffixes Signed-off-by: Naman-B-Parlecha --- README.md | 9 ++++----- metric_namer.go | 32 +++++++++----------------------- metric_namer_test.go | 32 +++++++++++++++++++++++++++----- strategy.go | 28 +++++++++++++++++++++++++--- unit_namer.go | 2 +- 5 files changed, 66 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 3eb4789..f24d7a6 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Part of the [Prometheus](https://prometheus.io/) ecosystem, following the [OpenT - **Namespace Support**: Add configurable namespace prefixes - **UTF-8 Support**: Choose between Prometheus legacy scheme compliant metric/label names (`[a-zA-Z0-9:_]`) or untranslated metric/label names - **Translation Strategy Configuration**: Select a translation strategy with a standard set of strings. -- **Configurable Updated Unit Mappings**: Opt in to corrected UCUM unit suffixes via `WithUpdatedMetricMapping()` option. +- **Updated UCUM Unit Mappings**: Opt in to corrected UCUM unit suffixes (`TiBy`: `tebibytes`, `kBy`: `kilobytes`) by selecting the `UnderscoreEscapingWithUpdatedSuffixes` or `NoUTF8EscapingWithUpdatedSuffixes` translation strategy. ## Installation @@ -32,11 +32,10 @@ import ( func main() { // Create a metric namer using traditional Prometheus name translation, with suffixes added and UTF-8 disallowed. - strategy := otlptranslator.UnderscoreEscapingWithSuffixes - - // WithUpdatedMetricMapping enables corrected UCUM unit mappings: + // Use UnderscoreEscapingWithUpdatedSuffixes to opt into corrected UCUM unit mappings: // TiBy -> "tebibytes" instead of "tibibytes" and kBy -> "kilobytes". - namer := otlptranslator.NewMetricNamer("myapp", strategy, otlptranslator.WithUpdatedMetricMapping()) + strategy := otlptranslator.UnderscoreEscapingWithUpdatedSuffixes + namer := otlptranslator.NewMetricNamer("myapp", strategy) // Translate OTLP metric to Prometheus format metric := otlptranslator.Metric{ diff --git a/metric_namer.go b/metric_namer.go index ca69eef..15f1334 100644 --- a/metric_namer.go +++ b/metric_namer.go @@ -102,34 +102,20 @@ var perUnitMap = map[string]string{ // // result := namer.Build(metric) // "http_server_duration_seconds" type MetricNamer struct { - Namespace string - WithMetricSuffixes bool - UTF8Allowed bool - // to fix the UCUM metrics suffix tebibyte and kBy - UpdatedMetricMapping bool -} - -type Option func(*MetricNamer) - -// WithUpdatedMetricMapping enables corrected UCUM unit mappings. -// It maps TiBy to "tebibytes" instead of "tibibytes" -// and adds kBy as "kilobytes" the correct notation. -func WithUpdatedMetricMapping() Option { - return func(mn *MetricNamer) { - mn.UpdatedMetricMapping = true - } + Namespace string + WithMetricSuffixes bool + UTF8Allowed bool + UpdatedMetricMapping bool // to fix the UCUM metrics suffix tebibyte and kBy } // NewMetricNamer creates a MetricNamer with the specified namespace (can be // blank) and the requested Translation Strategy. -func NewMetricNamer(namespace string, strategy TranslationStrategyOption, opts ...Option) MetricNamer { +func NewMetricNamer(namespace string, strategy TranslationStrategyOption) MetricNamer { mn := MetricNamer{ - Namespace: namespace, - WithMetricSuffixes: strategy.ShouldAddSuffixes(), - UTF8Allowed: !strategy.ShouldEscape(), - } - for _, opt := range opts { - opt(&mn) + Namespace: namespace, + WithMetricSuffixes: strategy.ShouldAddSuffixes(), + UTF8Allowed: !strategy.ShouldEscape(), + UpdatedMetricMapping: strategy.ShouldUseUpdatedSuffixes(), } return mn } diff --git a/metric_namer_test.go b/metric_namer_test.go index 99f7869..3137365 100644 --- a/metric_namer_test.go +++ b/metric_namer_test.go @@ -807,8 +807,8 @@ func TestMetricNamer_Build(t *testing.T) { { name: "metric with tebibytes unit with flag", namer: MetricNamer{ - UTF8Allowed: false, - WithMetricSuffixes: true, + UTF8Allowed: false, + WithMetricSuffixes: true, UpdatedMetricMapping: true, }, metric: Metric{ @@ -819,6 +819,28 @@ func TestMetricNamer_Build(t *testing.T) { wantMetricName: "capacity_tebibytes", wantUnitName: "tebibytes", }, + { + name: "metrics with updated mapping using NoUTF8EscapingWithUpdatedSuffixes flag", + namer: NewMetricNamer("mock", NoUTF8EscapingWithUpdatedSuffixes), + metric: Metric{ + Name: "capacity", + Unit: "TiBy", + Type: MetricTypeGauge, + }, + wantMetricName: "mock_capacity_tebibytes", + wantUnitName: "tebibytes", + }, + { + name: "metrics with updated mapping using UnderscoreEscapingWithUpdatedSuffixes flag", + namer: NewMetricNamer("mock", UnderscoreEscapingWithUpdatedSuffixes), + metric: Metric{ + Name: "capacity", + Unit: "TiBy", + Type: MetricTypeGauge, + }, + wantMetricName: "mock_capacity_tebibytes", + wantUnitName: "tebibytes", + }, { name: "metric with kilobytes unit", namer: MetricNamer{ @@ -837,8 +859,8 @@ func TestMetricNamer_Build(t *testing.T) { { name: "metric with kilobytes unit with flag", namer: MetricNamer{ - UTF8Allowed: false, - WithMetricSuffixes: true, + UTF8Allowed: false, + WithMetricSuffixes: true, UpdatedMetricMapping: true, }, metric: Metric{ @@ -1238,7 +1260,7 @@ func TestMetricNamer_Build(t *testing.T) { // Build unit name using UnitNamer to verify correlation when suffixes are enabled if tt.namer.WithMetricSuffixes { unitNamer := UnitNamer{ - UTF8Allowed: tt.namer.UTF8Allowed, + UTF8Allowed: tt.namer.UTF8Allowed, UpdatedMetricMapping: tt.namer.UpdatedMetricMapping, } gotUnitName := unitNamer.Build(tt.metric.Unit) diff --git a/strategy.go b/strategy.go index 20fe019..37e5b3f 100644 --- a/strategy.go +++ b/strategy.go @@ -57,15 +57,26 @@ var ( // (https://github.com/prometheus/proposals/pull/39) once released, as // potential mitigation of the above risks. NoTranslation TranslationStrategyOption = "NoTranslation" + // NoUTF8EscapingWithUpdatedSuffixes will accept metric/label names as they are. Unit + // and type suffixes may be added to metric names, according to certain rules. + // This option includes an updated mapping for the UCUM metrics suffixes tebibyte and kBy. + NoUTF8EscapingWithUpdatedSuffixes TranslationStrategyOption = "NoUTF8EscapingWithUpdatedSuffixes" + // UnderscoreEscapingWithSuffixes is the default option for translating OTLP + // to Prometheus. This option will translate metric name characters that are + // not alphanumerics/underscores/colons to underscores, and label name + // characters that are not alphanumerics/underscores to underscores. Unit and + // type suffixes may be appended to metric names, according to certain rules. + // This option includes an updated mapping for the UCUM metrics suffixes tebibyte and kBy. + UnderscoreEscapingWithUpdatedSuffixes TranslationStrategyOption = "UnderscoreEscapingWithUpdatedSuffixes" ) // ShouldEscape returns true if the translation strategy requires that metric // names be escaped. func (o TranslationStrategyOption) ShouldEscape() bool { switch o { - case UnderscoreEscapingWithSuffixes, UnderscoreEscapingWithoutSuffixes: + case UnderscoreEscapingWithSuffixes, UnderscoreEscapingWithoutSuffixes, UnderscoreEscapingWithUpdatedSuffixes: return true - case NoTranslation, NoUTF8EscapingWithSuffixes: + case NoTranslation, NoUTF8EscapingWithSuffixes, NoUTF8EscapingWithUpdatedSuffixes: return false default: return false @@ -76,7 +87,7 @@ func (o TranslationStrategyOption) ShouldEscape() bool { // strategy should have suffixes added. func (o TranslationStrategyOption) ShouldAddSuffixes() bool { switch o { - case UnderscoreEscapingWithSuffixes, NoUTF8EscapingWithSuffixes: + case UnderscoreEscapingWithSuffixes, NoUTF8EscapingWithSuffixes, UnderscoreEscapingWithUpdatedSuffixes, NoUTF8EscapingWithUpdatedSuffixes: return true case UnderscoreEscapingWithoutSuffixes, NoTranslation: return false @@ -84,3 +95,14 @@ func (o TranslationStrategyOption) ShouldAddSuffixes() bool { return false } } + +func (o TranslationStrategyOption) ShouldUseUpdatedSuffixes() bool { + switch o { + case NoUTF8EscapingWithUpdatedSuffixes, UnderscoreEscapingWithUpdatedSuffixes: + return true + case NoUTF8EscapingWithSuffixes, UnderscoreEscapingWithSuffixes, UnderscoreEscapingWithoutSuffixes, NoTranslation: + return false + default: + return false + } +} diff --git a/unit_namer.go b/unit_namer.go index dceb8d8..5f21517 100644 --- a/unit_namer.go +++ b/unit_namer.go @@ -24,7 +24,7 @@ import "strings" // result := namer.Build("s") // "seconds" // result = namer.Build("By/s") // "bytes_per_second" type UnitNamer struct { - UTF8Allowed bool + UTF8Allowed bool UpdatedMetricMapping bool }