gotosocial/vendor/github.com/prometheus/otlptranslator/unit_namer.go
kim 78defcd916 [chore] bump dependencies (#4406)
- codeberg.org/gruf/go-ffmpreg: v0.6.9 -> v0.6.10
- github.com/ncruces/go-sqlite3: v0.27.1 -> v0.28.0
- github.com/stretchr/testify: v1.10.0 -> v1.11.1
- github.com/tdewolff/minify/v2 v2.23.11 -> v2.24.2
- go.opentelemetry.io/otel{,/*}: v1.37.0 -> v1.38.0
- go.opentelemetry.io/contrib/*: v0.62.0 -> v0.63.0

Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4406
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
2025-09-04 15:29:27 +02:00

130 lines
4.3 KiB
Go

// 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.
// It processes OpenTelemetry Protocol (OTLP) unit strings and converts them
// to Prometheus-compliant unit names.
//
// Example usage:
//
// namer := UnitNamer{UTF8Allowed: false}
// result := namer.Build("s") // "seconds"
// result = namer.Build("By/s") // "bytes_per_second"
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 unit mappings, and cleaning up invalid characters when UTF8Allowed is false.
//
// Unit mappings include:
// - Time: s→seconds, ms→milliseconds, h→hours
// - Bytes: By→bytes, KBy→kilobytes, MBy→megabytes
// - SI: m→meters, V→volts, W→watts
// - Special: 1→"" (empty), %→percent
//
// Examples:
//
// namer := UnitNamer{UTF8Allowed: false}
// namer.Build("s") // "seconds"
// namer.Build("requests/s") // "requests_per_second"
// namer.Build("1") // "" (dimensionless)
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),
"_",
), "_")
}