From 1cbfa232e82ab3576bc334d64dd587b6e8dfde53 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Tue, 11 Jul 2023 14:19:18 +0000 Subject: [PATCH] Add WithoutCounterSuffixes option in go.opentelemetry.io/otel/exporters/prometheus to disable addition of _total suffixes --- CHANGELOG.md | 1 + exporters/prometheus/config.go | 26 ++++++++++++---- exporters/prometheus/exporter.go | 28 +++++++++-------- exporters/prometheus/exporter_test.go | 30 +++++++++++++++++++ .../testdata/counter_disabled_suffix.txt | 10 +++++++ 5 files changed, 76 insertions(+), 19 deletions(-) create mode 100755 exporters/prometheus/testdata/counter_disabled_suffix.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f69a0ddfd67..6736d104f9d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add `PeriodicReader` struct in `go.opentelemetry.io/otel/sdk/metric`. (#4244) - Add `Exporter` struct in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#4272) - Add `Exporter` struct in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#4272) +- Add `WithoutCounterSuffixes` option in `go.opentelemetry.io/otel/exporters/prometheus` to disable addition of `_total` suffixes. (#TODO) ### Changed diff --git a/exporters/prometheus/config.go b/exporters/prometheus/config.go index fbca80092e72..dcaba515900d 100644 --- a/exporters/prometheus/config.go +++ b/exporters/prometheus/config.go @@ -24,12 +24,13 @@ import ( // config contains options for the exporter. type config struct { - registerer prometheus.Registerer - disableTargetInfo bool - withoutUnits bool - aggregation metric.AggregationSelector - disableScopeInfo bool - namespace string + registerer prometheus.Registerer + disableTargetInfo bool + withoutUnits bool + withoutCounterSuffixes bool + aggregation metric.AggregationSelector + disableScopeInfo bool + namespace string } // newConfig creates a validated config configured with options. @@ -110,6 +111,19 @@ func WithoutUnits() Option { }) } +// WithoutUnits disables exporter's addition _total suffixes on counters. +// +// By default, metric names include a _total suffix to follow Prometheus naming +// conventions. For example, the counter metric happy.people would become +// happy_people_total. With this option set, the name would instead be +// happy_people. +func WithoutCounterSuffixes() Option { + return optionFunc(func(cfg config) config { + cfg.withoutCounterSuffixes = true + return cfg + }) +} + // WithoutScopeInfo configures the Exporter to not export the otel_scope_info metric. // If not specified, the Exporter will create a otel_scope_info metric containing // the metrics' Instrumentation Scope, and also add labels about Instrumentation Scope to all metric points. diff --git a/exporters/prometheus/exporter.go b/exporters/prometheus/exporter.go index 4fdbcfa174f7..98949d8ca87b 100644 --- a/exporters/prometheus/exporter.go +++ b/exporters/prometheus/exporter.go @@ -59,9 +59,10 @@ var _ metric.Reader = &Exporter{} type collector struct { reader metric.Reader - withoutUnits bool - disableScopeInfo bool - namespace string + withoutUnits bool + withoutCounterSuffixes bool + disableScopeInfo bool + namespace string mu sync.Mutex // mu protects all members below from the concurrent access. disableTargetInfo bool @@ -70,7 +71,7 @@ type collector struct { metricFamilies map[string]*dto.MetricFamily } -// prometheus counters MUST have a _total suffix: +// 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" @@ -84,13 +85,14 @@ func New(opts ...Option) (*Exporter, error) { reader := metric.NewManualReader(cfg.manualReaderOptions()...) collector := &collector{ - reader: reader, - disableTargetInfo: cfg.disableTargetInfo, - withoutUnits: cfg.withoutUnits, - disableScopeInfo: cfg.disableScopeInfo, - scopeInfos: make(map[instrumentation.Scope]prometheus.Metric), - metricFamilies: make(map[string]*dto.MetricFamily), - namespace: cfg.namespace, + reader: reader, + disableTargetInfo: cfg.disableTargetInfo, + withoutUnits: cfg.withoutUnits, + withoutCounterSuffixes: cfg.withoutCounterSuffixes, + disableScopeInfo: cfg.disableScopeInfo, + scopeInfos: make(map[instrumentation.Scope]prometheus.Metric), + metricFamilies: make(map[string]*dto.MetricFamily), + namespace: cfg.namespace, } if err := cfg.registerer.Register(collector); err != nil { @@ -387,12 +389,12 @@ func (c *collector) metricTypeAndName(m metricdata.Metrics) (*dto.MetricType, st case metricdata.Histogram[int64], metricdata.Histogram[float64]: return dto.MetricType_HISTOGRAM.Enum(), name case metricdata.Sum[float64]: - if v.IsMonotonic { + if v.IsMonotonic && !c.withoutCounterSuffixes { return dto.MetricType_COUNTER.Enum(), name + counterSuffix } return dto.MetricType_GAUGE.Enum(), name case metricdata.Sum[int64]: - if v.IsMonotonic { + if v.IsMonotonic && !c.withoutCounterSuffixes { return dto.MetricType_COUNTER.Enum(), name + counterSuffix } return dto.MetricType_GAUGE.Enum(), name diff --git a/exporters/prometheus/exporter_test.go b/exporters/prometheus/exporter_test.go index be3914d3e383..fb234aa1f26b 100644 --- a/exporters/prometheus/exporter_test.go +++ b/exporters/prometheus/exporter_test.go @@ -71,6 +71,36 @@ func TestPrometheusExporter(t *testing.T) { counter.Add(ctx, 5, otelmetric.WithAttributeSet(attrs2)) }, }, + { + name: "counter with suffixes disabled", + expectedFile: "testdata/counter_disabled_suffix.txt", + options: []Option{WithoutCounterSuffixes()}, + 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", + otelmetric.WithDescription("a simple counter without a total suffix"), + otelmetric.WithUnit("ms"), + ) + 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: "gauge", expectedFile: "testdata/gauge.txt", diff --git a/exporters/prometheus/testdata/counter_disabled_suffix.txt b/exporters/prometheus/testdata/counter_disabled_suffix.txt new file mode 100755 index 000000000000..2e203b434a7b --- /dev/null +++ b/exporters/prometheus/testdata/counter_disabled_suffix.txt @@ -0,0 +1,10 @@ +# 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 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 +# HELP target_info Target metadata +# TYPE target_info gauge +target_info{service_name="prometheus_test",telemetry_sdk_language="go",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="latest"} 1