diff --git a/CHANGELOG.md b/CHANGELOG.md index f73501f88d8..2390da03225 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The `go.opentelemetry.io/otel/semconv/v1.21.0` package. The package contains semantic conventions from the `v1.21.0` version of the OpenTelemetry Semantic Conventions. (#4362) - Document the `Temporality` and `Aggregation` methods of the `"go.opentelemetry.io/otel/sdk/metric".Exporter"` need to be concurrent safe. (#4381) +- Expand the set of units supported by the prometheus exporter, and don't add unit suffixes if they are already present in `go.opentelemetry.op/otel/exporters/prometheus` (#4374) ### Changed diff --git a/exporters/prometheus/exporter.go b/exporters/prometheus/exporter.go index 04f26e7cb4a..4007f8915b2 100644 --- a/exporters/prometheus/exporter.go +++ b/exporters/prometheus/exporter.go @@ -330,9 +330,39 @@ func sanitizeRune(r rune) rune { } var unitSuffixes = map[string]string{ - "1": "_ratio", - "By": "_bytes", - "ms": "_milliseconds", + // 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": "_ratio", + "%": "_percent", } // getName returns the sanitized name, prefixed with the namespace and suffixed with unit. @@ -344,7 +374,7 @@ func (c *collector) getName(m metricdata.Metrics) string { if c.withoutUnits { return name } - if suffix, ok := unitSuffixes[m.Unit]; ok { + if suffix, ok := unitSuffixes[m.Unit]; ok && !strings.HasSuffix(name, suffix) { name += suffix } return name diff --git a/exporters/prometheus/exporter_test.go b/exporters/prometheus/exporter_test.go index fb234aa1f26..49e4adc754b 100644 --- a/exporters/prometheus/exporter_test.go +++ b/exporters/prometheus/exporter_test.go @@ -55,7 +55,36 @@ func TestPrometheusExporter(t *testing.T) { counter, err := meter.Float64Counter( "foo", otelmetric.WithDescription("a simple counter"), - otelmetric.WithUnit("ms"), + otelmetric.WithUnit("s"), + ) + require.NoError(t, err) + counter.Add(ctx, 5, opt) + counter.Add(ctx, 10.3, opt) + counter.Add(ctx, 9, opt) + + attrs2 := attribute.NewSet( + attribute.Key("A").String("D"), + attribute.Key("C").String("B"), + attribute.Key("E").Bool(true), + attribute.Key("F").Int(42), + ) + counter.Add(ctx, 5, otelmetric.WithAttributeSet(attrs2)) + }, + }, + { + name: "counter that already has the unit suffix", + expectedFile: "testdata/counter.txt", + recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { + opt := otelmetric.WithAttributes( + attribute.Key("A").String("B"), + attribute.Key("C").String("D"), + attribute.Key("E").Bool(true), + attribute.Key("F").Int(42), + ) + counter, err := meter.Float64Counter( + "foo.seconds", + otelmetric.WithDescription("a simple counter"), + otelmetric.WithUnit("s"), ) require.NoError(t, err) counter.Add(ctx, 5, opt) @@ -85,7 +114,7 @@ func TestPrometheusExporter(t *testing.T) { counter, err := meter.Float64Counter( "foo", otelmetric.WithDescription("a simple counter without a total suffix"), - otelmetric.WithUnit("ms"), + otelmetric.WithUnit("s"), ) require.NoError(t, err) counter.Add(ctx, 5, opt) @@ -415,7 +444,7 @@ func TestMultiScopes(t *testing.T) { fooCounter, err := provider.Meter("meterfoo", otelmetric.WithInstrumentationVersion("v0.1.0")). Int64Counter( "foo", - otelmetric.WithUnit("ms"), + otelmetric.WithUnit("s"), otelmetric.WithDescription("meter foo counter")) assert.NoError(t, err) fooCounter.Add(ctx, 100, otelmetric.WithAttributes(attribute.String("type", "foo"))) @@ -423,7 +452,7 @@ func TestMultiScopes(t *testing.T) { barCounter, err := provider.Meter("meterbar", otelmetric.WithInstrumentationVersion("v0.1.0")). Int64Counter( "bar", - otelmetric.WithUnit("ms"), + otelmetric.WithUnit("s"), otelmetric.WithDescription("meter bar counter")) assert.NoError(t, err) barCounter.Add(ctx, 200, otelmetric.WithAttributes(attribute.String("type", "bar"))) @@ -571,7 +600,7 @@ func TestDuplicateMetrics(t *testing.T) { bazA.Add(ctx, 100, withTypeBar) bazB, err := meterB.Int64Counter("bar", - otelmetric.WithUnit("ms"), + otelmetric.WithUnit("s"), otelmetric.WithDescription("meter bar")) assert.NoError(t, err) bazB.Add(ctx, 100, withTypeBar) @@ -589,7 +618,7 @@ func TestDuplicateMetrics(t *testing.T) { barA.Add(ctx, 100, withTypeBar) barB, err := meterB.Int64UpDownCounter("bar", - otelmetric.WithUnit("ms"), + otelmetric.WithUnit("s"), otelmetric.WithDescription("meter gauge bar")) assert.NoError(t, err) barB.Add(ctx, 100, withTypeBar) @@ -607,7 +636,7 @@ func TestDuplicateMetrics(t *testing.T) { barA.Record(ctx, 100, withAB) barB, err := meterB.Int64Histogram("bar", - otelmetric.WithUnit("ms"), + otelmetric.WithUnit("s"), otelmetric.WithDescription("meter histogram bar")) assert.NoError(t, err) barB.Record(ctx, 100, withAB) diff --git a/exporters/prometheus/testdata/counter.txt b/exporters/prometheus/testdata/counter.txt index 79a1e7a5b37..4eb32991a64 100755 --- a/exporters/prometheus/testdata/counter.txt +++ b/exporters/prometheus/testdata/counter.txt @@ -1,7 +1,7 @@ -# HELP foo_milliseconds_total a simple counter -# TYPE foo_milliseconds_total counter -foo_milliseconds_total{A="B",C="D",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 24.3 -foo_milliseconds_total{A="D",C="B",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 5 +# HELP foo_seconds_total a simple counter +# TYPE foo_seconds_total counter +foo_seconds_total{A="B",C="D",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 24.3 +foo_seconds_total{A="D",C="B",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 5 # HELP otel_scope_info Instrumentation Scope metadata # TYPE otel_scope_info gauge otel_scope_info{otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 1 diff --git a/exporters/prometheus/testdata/counter_disabled_suffix.txt b/exporters/prometheus/testdata/counter_disabled_suffix.txt index 2e203b434a7..ca6f8967dfb 100755 --- a/exporters/prometheus/testdata/counter_disabled_suffix.txt +++ b/exporters/prometheus/testdata/counter_disabled_suffix.txt @@ -1,7 +1,7 @@ -# HELP foo_milliseconds a simple counter without a total suffix -# TYPE foo_milliseconds counter -foo_milliseconds{A="B",C="D",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 24.3 -foo_milliseconds{A="D",C="B",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 5 +# HELP foo_seconds a simple counter without a total suffix +# TYPE foo_seconds counter +foo_seconds{A="B",C="D",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 24.3 +foo_seconds{A="D",C="B",E="true",F="42",otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 5 # HELP otel_scope_info Instrumentation Scope metadata # TYPE otel_scope_info gauge otel_scope_info{otel_scope_name="testmeter",otel_scope_version="v0.1.0"} 1 diff --git a/exporters/prometheus/testdata/multi_scopes.txt b/exporters/prometheus/testdata/multi_scopes.txt index 38d84c79ede..674e1ac814c 100644 --- a/exporters/prometheus/testdata/multi_scopes.txt +++ b/exporters/prometheus/testdata/multi_scopes.txt @@ -1,9 +1,9 @@ -# HELP bar_milliseconds_total meter bar counter -# TYPE bar_milliseconds_total counter -bar_milliseconds_total{otel_scope_name="meterbar",otel_scope_version="v0.1.0",type="bar"} 200 -# HELP foo_milliseconds_total meter foo counter -# TYPE foo_milliseconds_total counter -foo_milliseconds_total{otel_scope_name="meterfoo",otel_scope_version="v0.1.0",type="foo"} 100 +# HELP bar_seconds_total meter bar counter +# TYPE bar_seconds_total counter +bar_seconds_total{otel_scope_name="meterbar",otel_scope_version="v0.1.0",type="bar"} 200 +# HELP foo_seconds_total meter foo counter +# TYPE foo_seconds_total counter +foo_seconds_total{otel_scope_name="meterfoo",otel_scope_version="v0.1.0",type="foo"} 100 # HELP otel_scope_info Instrumentation Scope metadata # TYPE otel_scope_info gauge otel_scope_info{otel_scope_name="meterfoo",otel_scope_version="v0.1.0"} 1