Skip to content

Commit 5a975ab

Browse files
authored
prometheusreceiver: Add trim_metric_suffixes configuration option (#24256)
**Description:** Adds a new configuration option: `trim_metric_suffixes` to the prometheus receiver. When set to true, suffixes will be trimmed from metrics. This replaces use of the`pkg.translator.prometheus.NormalizeName` feature-gate in the prometheus receiver, but leaves it for exporters. The first commit simplifies the usage of the feature-gate. The tests and implementation were passing around an entire registry when it wasn't necessary. **Link to tracking Issue:** Part of #21743 Part of #8950
1 parent f0ba02b commit 5a975ab

23 files changed

+148
-158
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Use this changelog template to create an entry for release notes.
2+
# If your change doesn't affect end users, such as a test fix or a tooling change,
3+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
4+
5+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
6+
change_type: 'breaking'
7+
8+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
9+
component: prometheusreceiver
10+
11+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
12+
note: "Add the `trim_metric_suffixes` configuration option to allow enable metric suffix trimming."
13+
14+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
15+
issues: [21743, 8950]
16+
17+
# (Optional) One or more lines of additional information to render under the primary note.
18+
# These lines will be padded with 2 spaces and then inserted directly into the document.
19+
# Use pipe (|) for multiline entries.
20+
subtext: |
21+
When enabled, suffixes for unit and type are trimmed from metric names.
22+
If you previously enabled the `pkg.translator.prometheus.NormalizeName`
23+
feature gate, you will need to enable this option to have suffixes trimmed.

pkg/translator/prometheus/normalize_name.go

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -177,36 +177,11 @@ func normalizeName(metric pmetric.Metric, namespace string) string {
177177
return normalizedName
178178
}
179179

180-
type Normalizer struct {
181-
gate *featuregate.Gate
182-
}
183-
184-
func NewNormalizer(registry *featuregate.Registry) *Normalizer {
185-
var normalizeGate *featuregate.Gate
186-
registry.VisitAll(func(gate *featuregate.Gate) {
187-
if gate.ID() == normalizeNameGate.ID() {
188-
normalizeGate = gate
189-
}
190-
})
191-
// the registry didn't contain the flag, fallback to the global
192-
// flag. Overriding the registry is really only done in tests
193-
if normalizeGate == nil {
194-
normalizeGate = normalizeNameGate
195-
}
196-
return &Normalizer{
197-
gate: normalizeGate,
198-
}
199-
}
200-
201180
// TrimPromSuffixes trims type and unit prometheus suffixes from a metric name.
202181
// Following the [OpenTelemetry specs] for converting Prometheus Metric points to OTLP.
203182
//
204183
// [OpenTelemetry specs]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#metric-metadata
205-
func (n *Normalizer) TrimPromSuffixes(promName string, metricType pmetric.MetricType, unit string) string {
206-
if !n.gate.IsEnabled() {
207-
return promName
208-
}
209-
184+
func TrimPromSuffixes(promName string, metricType pmetric.MetricType, unit string) string {
210185
nameTokens := strings.Split(promName, "_")
211186
if len(nameTokens) == 1 {
212187
return promName

pkg/translator/prometheus/normalize_name_test.go

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88

99
"github.com/stretchr/testify/assert"
1010
"github.com/stretchr/testify/require"
11-
"go.opentelemetry.io/collector/featuregate"
1211
"go.opentelemetry.io/collector/pdata/pmetric"
1312

1413
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil"
@@ -139,48 +138,36 @@ func TestOtelReceivers(t *testing.T) {
139138
}
140139

141140
func TestTrimPromSuffixes(t *testing.T) {
142-
registry := featuregate.NewRegistry()
143-
_, err := registry.Register(normalizeNameGate.ID(), featuregate.StageBeta)
144-
require.NoError(t, err)
145-
normalizer := NewNormalizer(registry)
146-
147-
assert.Equal(t, "active_directory_ds_replication_network_io", normalizer.TrimPromSuffixes("active_directory_ds_replication_network_io_bytes_total", pmetric.MetricTypeSum, "bytes"))
148-
assert.Equal(t, "active_directory_ds_name_cache_hit_rate", normalizer.TrimPromSuffixes("active_directory_ds_name_cache_hit_rate_percent", pmetric.MetricTypeGauge, "percent"))
149-
assert.Equal(t, "active_directory_ds_ldap_bind_last_successful_time", normalizer.TrimPromSuffixes("active_directory_ds_ldap_bind_last_successful_time_milliseconds", pmetric.MetricTypeGauge, "milliseconds"))
150-
assert.Equal(t, "apache_requests", normalizer.TrimPromSuffixes("apache_requests_total", pmetric.MetricTypeSum, "1"))
151-
assert.Equal(t, "system_cpu_utilization", normalizer.TrimPromSuffixes("system_cpu_utilization_ratio", pmetric.MetricTypeGauge, "ratio"))
152-
assert.Equal(t, "mongodbatlas_process_journaling_data_files", normalizer.TrimPromSuffixes("mongodbatlas_process_journaling_data_files_mebibytes", pmetric.MetricTypeGauge, "mebibytes"))
153-
assert.Equal(t, "mongodbatlas_process_network_io", normalizer.TrimPromSuffixes("mongodbatlas_process_network_io_bytes_per_second", pmetric.MetricTypeGauge, "bytes_per_second"))
154-
assert.Equal(t, "mongodbatlas_process_oplog_rate", normalizer.TrimPromSuffixes("mongodbatlas_process_oplog_rate_gibibytes_per_hour", pmetric.MetricTypeGauge, "gibibytes_per_hour"))
155-
assert.Equal(t, "nsxt_node_memory_usage", normalizer.TrimPromSuffixes("nsxt_node_memory_usage_kilobytes", pmetric.MetricTypeGauge, "kilobytes"))
156-
assert.Equal(t, "redis_latest_fork", normalizer.TrimPromSuffixes("redis_latest_fork_microseconds", pmetric.MetricTypeGauge, "microseconds"))
157-
assert.Equal(t, "up", normalizer.TrimPromSuffixes("up", pmetric.MetricTypeGauge, ""))
141+
assert.Equal(t, "active_directory_ds_replication_network_io", TrimPromSuffixes("active_directory_ds_replication_network_io_bytes_total", pmetric.MetricTypeSum, "bytes"))
142+
assert.Equal(t, "active_directory_ds_name_cache_hit_rate", TrimPromSuffixes("active_directory_ds_name_cache_hit_rate_percent", pmetric.MetricTypeGauge, "percent"))
143+
assert.Equal(t, "active_directory_ds_ldap_bind_last_successful_time", TrimPromSuffixes("active_directory_ds_ldap_bind_last_successful_time_milliseconds", pmetric.MetricTypeGauge, "milliseconds"))
144+
assert.Equal(t, "apache_requests", TrimPromSuffixes("apache_requests_total", pmetric.MetricTypeSum, "1"))
145+
assert.Equal(t, "system_cpu_utilization", TrimPromSuffixes("system_cpu_utilization_ratio", pmetric.MetricTypeGauge, "ratio"))
146+
assert.Equal(t, "mongodbatlas_process_journaling_data_files", TrimPromSuffixes("mongodbatlas_process_journaling_data_files_mebibytes", pmetric.MetricTypeGauge, "mebibytes"))
147+
assert.Equal(t, "mongodbatlas_process_network_io", TrimPromSuffixes("mongodbatlas_process_network_io_bytes_per_second", pmetric.MetricTypeGauge, "bytes_per_second"))
148+
assert.Equal(t, "mongodbatlas_process_oplog_rate", TrimPromSuffixes("mongodbatlas_process_oplog_rate_gibibytes_per_hour", pmetric.MetricTypeGauge, "gibibytes_per_hour"))
149+
assert.Equal(t, "nsxt_node_memory_usage", TrimPromSuffixes("nsxt_node_memory_usage_kilobytes", pmetric.MetricTypeGauge, "kilobytes"))
150+
assert.Equal(t, "redis_latest_fork", TrimPromSuffixes("redis_latest_fork_microseconds", pmetric.MetricTypeGauge, "microseconds"))
151+
assert.Equal(t, "up", TrimPromSuffixes("up", pmetric.MetricTypeGauge, ""))
158152

159153
// These are not necessarily valid OM units, only tested for the sake of completeness.
160-
assert.Equal(t, "active_directory_ds_replication_sync_object_pending", normalizer.TrimPromSuffixes("active_directory_ds_replication_sync_object_pending_total", pmetric.MetricTypeSum, "{objects}"))
161-
assert.Equal(t, "apache_current", normalizer.TrimPromSuffixes("apache_current_connections", pmetric.MetricTypeGauge, "connections"))
162-
assert.Equal(t, "bigip_virtual_server_request_count", normalizer.TrimPromSuffixes("bigip_virtual_server_request_count_total", pmetric.MetricTypeSum, "{requests}"))
163-
assert.Equal(t, "mongodbatlas_process_db_query_targeting_scanned_per_returned", normalizer.TrimPromSuffixes("mongodbatlas_process_db_query_targeting_scanned_per_returned", pmetric.MetricTypeGauge, "{scanned}/{returned}"))
164-
assert.Equal(t, "nginx_connections_accepted", normalizer.TrimPromSuffixes("nginx_connections_accepted", pmetric.MetricTypeGauge, "connections"))
165-
assert.Equal(t, "apache_workers", normalizer.TrimPromSuffixes("apache_workers_connections", pmetric.MetricTypeGauge, "connections"))
166-
assert.Equal(t, "nginx", normalizer.TrimPromSuffixes("nginx_requests", pmetric.MetricTypeGauge, "requests"))
154+
assert.Equal(t, "active_directory_ds_replication_sync_object_pending", TrimPromSuffixes("active_directory_ds_replication_sync_object_pending_total", pmetric.MetricTypeSum, "{objects}"))
155+
assert.Equal(t, "apache_current", TrimPromSuffixes("apache_current_connections", pmetric.MetricTypeGauge, "connections"))
156+
assert.Equal(t, "bigip_virtual_server_request_count", TrimPromSuffixes("bigip_virtual_server_request_count_total", pmetric.MetricTypeSum, "{requests}"))
157+
assert.Equal(t, "mongodbatlas_process_db_query_targeting_scanned_per_returned", TrimPromSuffixes("mongodbatlas_process_db_query_targeting_scanned_per_returned", pmetric.MetricTypeGauge, "{scanned}/{returned}"))
158+
assert.Equal(t, "nginx_connections_accepted", TrimPromSuffixes("nginx_connections_accepted", pmetric.MetricTypeGauge, "connections"))
159+
assert.Equal(t, "apache_workers", TrimPromSuffixes("apache_workers_connections", pmetric.MetricTypeGauge, "connections"))
160+
assert.Equal(t, "nginx", TrimPromSuffixes("nginx_requests", pmetric.MetricTypeGauge, "requests"))
167161

168162
// Units shouldn't be trimmed if the unit is not a direct match with the suffix, i.e, a suffix "_seconds" shouldn't be removed if unit is "sec" or "s"
169-
assert.Equal(t, "system_cpu_load_average_15m_ratio", normalizer.TrimPromSuffixes("system_cpu_load_average_15m_ratio", pmetric.MetricTypeGauge, "1"))
170-
assert.Equal(t, "mongodbatlas_process_asserts_per_second", normalizer.TrimPromSuffixes("mongodbatlas_process_asserts_per_second", pmetric.MetricTypeGauge, "{assertions}/s"))
171-
assert.Equal(t, "memcached_operation_hit_ratio_percent", normalizer.TrimPromSuffixes("memcached_operation_hit_ratio_percent", pmetric.MetricTypeGauge, "%"))
172-
assert.Equal(t, "active_directory_ds_replication_object_rate_per_second", normalizer.TrimPromSuffixes("active_directory_ds_replication_object_rate_per_second", pmetric.MetricTypeGauge, "{objects}/s"))
173-
assert.Equal(t, "system_disk_operation_time_seconds", normalizer.TrimPromSuffixes("system_disk_operation_time_seconds_total", pmetric.MetricTypeSum, "s"))
163+
assert.Equal(t, "system_cpu_load_average_15m_ratio", TrimPromSuffixes("system_cpu_load_average_15m_ratio", pmetric.MetricTypeGauge, "1"))
164+
assert.Equal(t, "mongodbatlas_process_asserts_per_second", TrimPromSuffixes("mongodbatlas_process_asserts_per_second", pmetric.MetricTypeGauge, "{assertions}/s"))
165+
assert.Equal(t, "memcached_operation_hit_ratio_percent", TrimPromSuffixes("memcached_operation_hit_ratio_percent", pmetric.MetricTypeGauge, "%"))
166+
assert.Equal(t, "active_directory_ds_replication_object_rate_per_second", TrimPromSuffixes("active_directory_ds_replication_object_rate_per_second", pmetric.MetricTypeGauge, "{objects}/s"))
167+
assert.Equal(t, "system_disk_operation_time_seconds", TrimPromSuffixes("system_disk_operation_time_seconds_total", pmetric.MetricTypeSum, "s"))
174168

175169
}
176170

177-
func TestTrimPromSuffixesWithFeatureGateDisabled(t *testing.T) {
178-
normalizer := NewNormalizer(featuregate.NewRegistry())
179-
180-
assert.Equal(t, "apache_current_connections", normalizer.TrimPromSuffixes("apache_current_connections", pmetric.MetricTypeGauge, "connections"))
181-
assert.Equal(t, "apache_requests_total", normalizer.TrimPromSuffixes("apache_requests_total", pmetric.MetricTypeSum, "1"))
182-
}
183-
184171
func TestNamespace(t *testing.T) {
185172
require.Equal(t, "space_test", normalizeName(createGauge("test", ""), "space"))
186173
require.Equal(t, "space_test", normalizeName(createGauge("#test", ""), "space"))

receiver/prometheusreceiver/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,28 @@ receivers:
102102
action: keep
103103
```
104104
105+
The prometheus receiver also supports additional top-level options:
106+
107+
- **trim_metric_suffixes**: [**Experimental**] When set to true, this enables trimming unit and some counter type suffixes from metric names. For example, it would cause `singing_duration_seconds_total` to be trimmed to `singing_duration`. This can be useful when trying to restore the original metric names used in OpenTelemetry instrumentation. Defaults to false.
108+
- **use_start_time_metric**: When set to true, this enables retrieving the start time of all counter metrics from the process_start_time_seconds metric. This is only correct if all counters on that endpoint started after the process start time, and the process is the only actor exporting the metric after the process started. It should not be used in "exporters" which export counters that may have started before the process itself. Use only if you know what you are doing, as this may result in incorrect rate calculations. Defaults to false.
109+
- **start_time_metric_regex**: The regular expression for the start time metric, and is only applied when use_start_time_metric is enabled. Defaults to process_start_time_seconds.
110+
111+
For example,
112+
113+
```yaml
114+
receivers:
115+
prometheus:
116+
trim_metric_suffixes: true
117+
use_start_time_metric: true
118+
start_time_metric_regex: foo_bar_.*
119+
config:
120+
scrape_configs:
121+
- job_name: 'otel-collector'
122+
scrape_interval: 5s
123+
static_configs:
124+
- targets: ['0.0.0.0:8888']
125+
```
126+
105127
## OpenTelemetry Operator
106128
Additional to this static job definitions this receiver allows to query a list of jobs from the
107129
OpenTelemetryOperators TargetAllocator or a compatible endpoint.

receiver/prometheusreceiver/config.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ const (
3232

3333
// Config defines configuration for Prometheus receiver.
3434
type Config struct {
35-
PrometheusConfig *promconfig.Config `mapstructure:"-"`
36-
BufferPeriod time.Duration `mapstructure:"buffer_period"`
37-
BufferCount int `mapstructure:"buffer_count"`
35+
PrometheusConfig *promconfig.Config `mapstructure:"-"`
36+
TrimMetricSuffixes bool `mapstructure:"trim_metric_suffixes"`
37+
BufferPeriod time.Duration `mapstructure:"buffer_period"`
38+
BufferCount int `mapstructure:"buffer_count"`
3839
// UseStartTimeMetric enables retrieving the start time of all counter metrics
3940
// from the process_start_time_seconds metric. This is only correct if all counters on that endpoint
4041
// started after the process start time, and the process is the only actor exporting the metric after

receiver/prometheusreceiver/config_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func TestLoadConfig(t *testing.T) {
4040
assert.Equal(t, r1.PrometheusConfig.ScrapeConfigs[0].JobName, "demo")
4141
assert.Equal(t, time.Duration(r1.PrometheusConfig.ScrapeConfigs[0].ScrapeInterval), 5*time.Second)
4242
assert.Equal(t, r1.UseStartTimeMetric, true)
43+
assert.Equal(t, r1.TrimMetricSuffixes, true)
4344
assert.Equal(t, r1.StartTimeMetricRegex, "^(.+_)*process_start_time_seconds$")
4445

4546
assert.Equal(t, "http://my-targetallocator-service", r1.TargetAllocator.Endpoint)

receiver/prometheusreceiver/factory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ func createMetricsReceiver(
4949
cfg component.Config,
5050
nextConsumer consumer.Metrics,
5151
) (receiver.Metrics, error) {
52-
return newPrometheusReceiver(set, cfg.(*Config), nextConsumer, featuregate.GlobalRegistry()), nil
52+
return newPrometheusReceiver(set, cfg.(*Config), nextConsumer), nil
5353
}

receiver/prometheusreceiver/internal/appendable.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/prometheus/prometheus/model/labels"
1212
"github.com/prometheus/prometheus/storage"
1313
"go.opentelemetry.io/collector/consumer"
14-
"go.opentelemetry.io/collector/featuregate"
1514
"go.opentelemetry.io/collector/obsreport"
1615
"go.opentelemetry.io/collector/receiver"
1716
)
@@ -21,12 +20,12 @@ type appendable struct {
2120
sink consumer.Metrics
2221
metricAdjuster MetricsAdjuster
2322
useStartTimeMetric bool
23+
trimSuffixes bool
2424
startTimeMetricRegex *regexp.Regexp
2525
externalLabels labels.Labels
2626

2727
settings receiver.CreateSettings
2828
obsrecv *obsreport.Receiver
29-
registry *featuregate.Registry
3029
}
3130

3231
// NewAppendable returns a storage.Appendable instance that emits metrics to the sink.
@@ -38,7 +37,7 @@ func NewAppendable(
3837
startTimeMetricRegex *regexp.Regexp,
3938
useCreatedMetric bool,
4039
externalLabels labels.Labels,
41-
registry *featuregate.Registry) (storage.Appendable, error) {
40+
trimSuffixes bool) (storage.Appendable, error) {
4241
var metricAdjuster MetricsAdjuster
4342
if !useStartTimeMetric {
4443
metricAdjuster = NewInitialPointAdjuster(set.Logger, gcInterval, useCreatedMetric)
@@ -59,10 +58,10 @@ func NewAppendable(
5958
startTimeMetricRegex: startTimeMetricRegex,
6059
externalLabels: externalLabels,
6160
obsrecv: obsrecv,
62-
registry: registry,
61+
trimSuffixes: trimSuffixes,
6362
}, nil
6463
}
6564

6665
func (o *appendable) Appender(ctx context.Context) storage.Appender {
67-
return newTransaction(ctx, o.metricAdjuster, o.sink, o.externalLabels, o.settings, o.obsrecv, o.registry)
66+
return newTransaction(ctx, o.metricAdjuster, o.sink, o.externalLabels, o.settings, o.obsrecv, o.trimSuffixes)
6867
}

receiver/prometheusreceiver/internal/metricfamily.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,14 @@ func (mf *metricFamily) addSeries(seriesRef uint64, metricName string, ls labels
301301
return nil
302302
}
303303

304-
func (mf *metricFamily) appendMetric(metrics pmetric.MetricSlice, normalizer *prometheus.Normalizer) {
304+
func (mf *metricFamily) appendMetric(metrics pmetric.MetricSlice, trimSuffixes bool) {
305305
metric := pmetric.NewMetric()
306-
// Trims type's and unit's suffixes from metric name
307-
metric.SetName(normalizer.TrimPromSuffixes(mf.name, mf.mtype, mf.metadata.Unit))
306+
// Trims type and unit suffixes from metric name
307+
name := mf.name
308+
if trimSuffixes {
309+
name = prometheus.TrimPromSuffixes(name, mf.mtype, mf.metadata.Unit)
310+
}
311+
metric.SetName(name)
308312
metric.SetDescription(mf.metadata.Help)
309313
metric.SetUnit(mf.metadata.Unit)
310314

receiver/prometheusreceiver/internal/metricfamily_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@ import (
1313
"github.com/prometheus/prometheus/model/value"
1414
"github.com/prometheus/prometheus/scrape"
1515
"github.com/stretchr/testify/require"
16-
"go.opentelemetry.io/collector/featuregate"
1716
"go.opentelemetry.io/collector/pdata/pcommon"
1817
"go.opentelemetry.io/collector/pdata/pmetric"
1918
"go.uber.org/zap"
20-
21-
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus"
2219
)
2320

2421
type testMetadataStore map[string]scrape.MetricMetadata
@@ -252,7 +249,7 @@ func TestMetricGroupData_toDistributionUnitTest(t *testing.T) {
252249
require.Len(t, mp.groups, 1)
253250

254251
sl := pmetric.NewMetricSlice()
255-
mp.appendMetric(sl, prometheus.NewNormalizer(featuregate.GlobalRegistry()))
252+
mp.appendMetric(sl, false)
256253

257254
require.Equal(t, 1, sl.Len(), "Exactly one metric expected")
258255
metric := sl.At(0)
@@ -540,7 +537,7 @@ func TestMetricGroupData_toSummaryUnitTest(t *testing.T) {
540537
require.Len(t, mp.groups, 1)
541538

542539
sl := pmetric.NewMetricSlice()
543-
mp.appendMetric(sl, prometheus.NewNormalizer(featuregate.GlobalRegistry()))
540+
mp.appendMetric(sl, false)
544541

545542
require.Equal(t, 1, sl.Len(), "Exactly one metric expected")
546543
metric := sl.At(0)
@@ -646,7 +643,7 @@ func TestMetricGroupData_toNumberDataUnitTest(t *testing.T) {
646643
require.Len(t, mp.groups, 1)
647644

648645
sl := pmetric.NewMetricSlice()
649-
mp.appendMetric(sl, prometheus.NewNormalizer(featuregate.GlobalRegistry()))
646+
mp.appendMetric(sl, false)
650647

651648
require.Equal(t, 1, sl.Len(), "Exactly one metric expected")
652649
metric := sl.At(0)

0 commit comments

Comments
 (0)