Skip to content

Commit

Permalink
fix prometheus tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dashpole committed Jan 22, 2025
1 parent c6e6acc commit 2bff18e
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 35 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Changed

- Update `github.com/prometheus/common` to v0.62.0., which changes the `NameValidationScheme` to `NoEscaping`. This allows metrics names to keep original delimiters (e.g. `.`), rather than replacing with underscores. This is controlled by the `Content-Type` header, or can be reverted by setting `NameValidationScheme` to `LegacyValidation` in `github.com/prometheus/common/model` (#6198)

### Added

- Add `ValueFromAttribute` and `KeyValueFromAttribute` in `go.opentelemetry.io/otel/log`. (#6180)
Expand Down
2 changes: 1 addition & 1 deletion exporters/prometheus/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func TestNewConfig(t *testing.T) {
},
wantConfig: config{
registerer: prometheus.DefaultRegisterer,
namespace: "test_",
namespace: "test/_",
},
},
}
Expand Down
70 changes: 41 additions & 29 deletions exporters/prometheus/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ type collector struct {

// prometheus counters MUST have a _total suffix by default:
// https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/compatibility/prometheus_and_openmetrics.md
const counterSuffix = "_total"
const counterSuffix = "total"

// New returns a Prometheus Exporter.
func New(opts ...Option) (*Exporter, error) {
Expand Down Expand Up @@ -368,38 +368,38 @@ func createScopeInfoMetric(scope instrumentation.Scope) (prometheus.Metric, erro

var unitSuffixes = map[string]string{
// Time
"d": "_days",
"h": "_hours",
"min": "_minutes",
"s": "_seconds",
"ms": "_milliseconds",
"us": "_microseconds",
"ns": "_nanoseconds",
"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",
"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",
"m": "meters",
"V": "volts",
"A": "amperes",
"J": "joules",
"W": "watts",
"g": "grams",

// Misc
"Cel": "_celsius",
"Hz": "_hertz",
"1": "_ratio",
"%": "_percent",
"Cel": "celsius",
"Hz": "hertz",
"1": "ratio",
"%": "percent",
}

// getName returns the sanitized name, prefixed with the namespace and suffixed with unit.
Expand All @@ -414,19 +414,31 @@ func (c *collector) getName(m metricdata.Metrics, typ *dto.MetricType) string {
// Remove the _total suffix here, as we will re-add the total suffix
// later, and it needs to come after the unit suffix.
name = strings.TrimSuffix(name, counterSuffix)
// If the last character is an underscore, or would be converted to an underscore, trim it from the name.
// an underscore will be added back in later.
if convertsToUnderscore(rune(name[len(name)-1])) {
name = name[:len(name)-1]
}
}
if c.namespace != "" {
name = c.namespace + name
}
if suffix, ok := unitSuffixes[m.Unit]; ok && !c.withoutUnits && !strings.HasSuffix(name, suffix) {
name += suffix
name += "_" + suffix
}
if addCounterSuffix {
name += counterSuffix
name += "_" + counterSuffix
}
return name
}

// convertsToUnderscore returns true if the character would be converted to an
// underscore when the escaping scheme is underscore escaping. This is meant to
// capture any character that should be considered a "delimiter".
func convertsToUnderscore(b rune) bool {
return !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == ':' || (b >= '0' && b <= '9'))
}

func (c *collector) metricType(m metricdata.Metrics) *dto.MetricType {
switch v := m.Data.(type) {
case metricdata.Histogram[int64], metricdata.Histogram[float64]:
Expand Down
10 changes: 5 additions & 5 deletions exporters/prometheus/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestPrometheusExporter(t *testing.T) {
recordMetrics func(ctx context.Context, meter otelmetric.Meter)
options []Option
expectedFile string
enableUTF8 bool
disableUTF8 bool
}{
{
name: "counter",
Expand Down Expand Up @@ -195,6 +195,7 @@ func TestPrometheusExporter(t *testing.T) {
{
name: "sanitized attributes to labels",
expectedFile: "testdata/sanitized_labels.txt",
disableUTF8: true,
options: []Option{WithoutUnits()},
recordMetrics: func(ctx context.Context, meter otelmetric.Meter) {
opt := otelmetric.WithAttributes(
Expand Down Expand Up @@ -404,7 +405,6 @@ func TestPrometheusExporter(t *testing.T) {
{
name: "counter utf-8",
expectedFile: "testdata/counter_utf8.txt",
enableUTF8: true,
recordMetrics: func(ctx context.Context, meter otelmetric.Meter) {
opt := otelmetric.WithAttributes(
attribute.Key("A.G").String("B"),
Expand Down Expand Up @@ -471,11 +471,11 @@ func TestPrometheusExporter(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.enableUTF8 {
model.NameValidationScheme = model.UTF8Validation
if tc.disableUTF8 {
model.NameValidationScheme = model.LegacyValidation
defer func() {
// Reset to defaults
model.NameValidationScheme = model.LegacyValidation
model.NameValidationScheme = model.UTF8Validation
}()
}
ctx := context.Background()
Expand Down

0 comments on commit 2bff18e

Please sign in to comment.