summaryrefslogtreecommitdiff
path: root/vendor/github.com/prometheus/otlptranslator/unit_namer.go
blob: 4bbf93ef97c3adb93dbaa27e201236b561a57349 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2025 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and

package otlptranslator

import "strings"

// UnitNamer is a helper for building compliant unit names.
type UnitNamer struct {
	UTF8Allowed bool
}

// Build builds a unit name for the specified unit string.
// It processes the unit by splitting it into main and per components,
// applying appropriate unit mappings, and cleaning up invalid characters
// when the whole UTF-8 character set is not allowed.
func (un *UnitNamer) Build(unit string) string {
	mainUnit, perUnit := buildUnitSuffixes(unit)
	if !un.UTF8Allowed {
		mainUnit, perUnit = cleanUpUnit(mainUnit), cleanUpUnit(perUnit)
	}

	var u string
	switch {
	case mainUnit != "" && perUnit != "":
		u = mainUnit + "_" + perUnit
	case mainUnit != "":
		u = mainUnit
	default:
		u = perUnit
	}

	// Clean up leading and trailing underscores
	if len(u) > 0 && u[0:1] == "_" {
		u = u[1:]
	}
	if len(u) > 0 && u[len(u)-1:] == "_" {
		u = u[:len(u)-1]
	}

	return u
}

// 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 {
	if promUnit, ok := unitMap[unit]; ok {
		return promUnit
	}
	return unit
}

// Retrieve the Prometheus "per" unit corresponding to the specified "per" unit.
// Returns the specified unit if not found in perUnitMap.
func perUnitMapGetOrDefault(perUnit string) string {
	if promPerUnit, ok := perUnitMap[perUnit]; ok {
		return promPerUnit
	}
	return perUnit
}

// 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) {
	// Split unit at the '/' if any
	unitTokens := strings.SplitN(unit, "/", 2)

	if len(unitTokens) > 0 {
		// Main unit
		// Update if not blank and doesn't contain '{}'
		mainUnitOTel := strings.TrimSpace(unitTokens[0])
		if mainUnitOTel != "" && !strings.ContainsAny(mainUnitOTel, "{}") {
			mainUnitSuffix = unitMapGetOrDefault(mainUnitOTel)
		}

		// Per unit
		// Update if not blank and doesn't contain '{}'
		if len(unitTokens) > 1 && unitTokens[1] != "" {
			perUnitOTel := strings.TrimSpace(unitTokens[1])
			if perUnitOTel != "" && !strings.ContainsAny(perUnitOTel, "{}") {
				perUnitSuffix = perUnitMapGetOrDefault(perUnitOTel)
			}
			if perUnitSuffix != "" {
				perUnitSuffix = "per_" + perUnitSuffix
			}
		}
	}

	return mainUnitSuffix, perUnitSuffix
}

// cleanUpUnit cleans up unit so it matches model.LabelNameRE.
func cleanUpUnit(unit string) string {
	// Multiple consecutive underscores are replaced 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.
	return strings.TrimPrefix(multipleUnderscoresRE.ReplaceAllString(
		strings.Map(replaceInvalidMetricChar, unit),
		"_",
	), "_")
}