Skip to content

Commit

Permalink
prometheusreceiver: add trim_metric_suffixes configuration option
Browse files Browse the repository at this point in the history
  • Loading branch information
dashpole committed Jul 14, 2023
1 parent 9cdf416 commit e1631f8
Show file tree
Hide file tree
Showing 22 changed files with 124 additions and 81 deletions.
23 changes: 23 additions & 0 deletions .chloggen/prometheus-receiver-normalization-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Use this changelog template to create an entry for release notes.
# If your change doesn't affect end users, such as a test fix or a tooling change,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: 'breaking'

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: prometheusreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: "Add the `trim_metric_suffixes` configuration option to allow enable metric suffix trimming."

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [21743, 8950]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |
When enabled, suffixes for unit and type are trimmed from metric names.
If you previously enabled the `pkg.translator.prometheus.NormalizeName`
feature gate, you will need to enable this option to have suffixes trimmed.
8 changes: 2 additions & 6 deletions pkg/translator/prometheus/normalize_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ var perUnitMap = map[string]string{
"y": "year",
}

var NormalizeNameGate = featuregate.GlobalRegistry().MustRegister(
var normalizeNameGate = featuregate.GlobalRegistry().MustRegister(
"pkg.translator.prometheus.NormalizeName",
featuregate.StageAlpha,
featuregate.WithRegisterDescription("Controls whether metrics names are automatically normalized to follow Prometheus naming convention"),
Expand All @@ -90,7 +90,7 @@ func BuildPromCompliantName(metric pmetric.Metric, namespace string) string {
var metricName string

// Full normalization following standard Prometheus naming conventions
if NormalizeNameGate.IsEnabled() {
if normalizeNameGate.IsEnabled() {
return normalizeName(metric, namespace)
}

Expand Down Expand Up @@ -182,10 +182,6 @@ func normalizeName(metric pmetric.Metric, namespace string) string {
//
// [OpenTelemetry specs]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#metric-metadata
func TrimPromSuffixes(promName string, metricType pmetric.MetricType, unit string) string {
if !NormalizeNameGate.IsEnabled() {
return promName
}

nameTokens := strings.Split(promName, "_")
if len(nameTokens) == 1 {
return promName
Expand Down
12 changes: 2 additions & 10 deletions pkg/translator/prometheus/normalize_name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ func TestOtelReceivers(t *testing.T) {
}

func TestTrimPromSuffixes(t *testing.T) {
defer testutil.SetFeatureGateForTest(t, NormalizeNameGate, true)()

assert.Equal(t, "active_directory_ds_replication_network_io", TrimPromSuffixes("active_directory_ds_replication_network_io_bytes_total", pmetric.MetricTypeSum, "bytes"))
assert.Equal(t, "active_directory_ds_name_cache_hit_rate", TrimPromSuffixes("active_directory_ds_name_cache_hit_rate_percent", pmetric.MetricTypeGauge, "percent"))
assert.Equal(t, "active_directory_ds_ldap_bind_last_successful_time", TrimPromSuffixes("active_directory_ds_ldap_bind_last_successful_time_milliseconds", pmetric.MetricTypeGauge, "milliseconds"))
Expand Down Expand Up @@ -170,12 +168,6 @@ func TestTrimPromSuffixes(t *testing.T) {

}

func TestTrimPromSuffixesWithFeatureGateDisabled(t *testing.T) {
defer testutil.SetFeatureGateForTest(t, NormalizeNameGate, false)()
assert.Equal(t, "apache_current_connections", TrimPromSuffixes("apache_current_connections", pmetric.MetricTypeGauge, "connections"))
assert.Equal(t, "apache_requests_total", TrimPromSuffixes("apache_requests_total", pmetric.MetricTypeSum, "1"))
}

func TestNamespace(t *testing.T) {
require.Equal(t, "space_test", normalizeName(createGauge("test", ""), "space"))
require.Equal(t, "space_test", normalizeName(createGauge("#test", ""), "space"))
Expand Down Expand Up @@ -220,7 +212,7 @@ func TestRemoveItem(t *testing.T) {

func TestBuildPromCompliantNameWithNormalize(t *testing.T) {

defer testutil.SetFeatureGateForTest(t, NormalizeNameGate, true)()
defer testutil.SetFeatureGateForTest(t, normalizeNameGate, true)()
require.Equal(t, "system_io_bytes_total", BuildPromCompliantName(createCounter("system.io", "By"), ""))
require.Equal(t, "system_network_io_bytes_total", BuildPromCompliantName(createCounter("network.io", "By"), "system"))
require.Equal(t, "_3_14_digits", BuildPromCompliantName(createGauge("3.14 digits", ""), ""))
Expand All @@ -232,7 +224,7 @@ func TestBuildPromCompliantNameWithNormalize(t *testing.T) {

func TestBuildPromCompliantNameWithoutNormalize(t *testing.T) {

defer testutil.SetFeatureGateForTest(t, NormalizeNameGate, false)()
defer testutil.SetFeatureGateForTest(t, normalizeNameGate, false)()
require.Equal(t, "system_io", BuildPromCompliantName(createCounter("system.io", "By"), ""))
require.Equal(t, "system_network_io", BuildPromCompliantName(createCounter("network.io", "By"), "system"))
require.Equal(t, "system_network_I_O", BuildPromCompliantName(createCounter("network (I/O)", "By"), "system"))
Expand Down
22 changes: 22 additions & 0 deletions receiver/prometheusreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,28 @@ receivers:
action: keep
```
The prometheus receiver also supports additional top-level options:
- **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.
- **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.
- **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.

For example,

```yaml
receivers:
prometheus:
trim_metric_suffixes: true
use_start_time_metric: true
start_time_metric_regex: foo_bar_.*
config:
scrape_configs:
- job_name: 'otel-collector'
scrape_interval: 5s
static_configs:
- targets: ['0.0.0.0:8888']
```

## OpenTelemetry Operator
Additional to this static job definitions this receiver allows to query a list of jobs from the
OpenTelemetryOperators TargetAllocator or a compatible endpoint.
Expand Down
7 changes: 4 additions & 3 deletions receiver/prometheusreceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ const (

// Config defines configuration for Prometheus receiver.
type Config struct {
PrometheusConfig *promconfig.Config `mapstructure:"-"`
BufferPeriod time.Duration `mapstructure:"buffer_period"`
BufferCount int `mapstructure:"buffer_count"`
PrometheusConfig *promconfig.Config `mapstructure:"-"`
TrimMetricSuffixes bool `mapstructure:"trim_metric_suffixes"`
BufferPeriod time.Duration `mapstructure:"buffer_period"`
BufferCount int `mapstructure:"buffer_count"`
// UseStartTimeMetric 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
Expand Down
1 change: 1 addition & 0 deletions receiver/prometheusreceiver/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func TestLoadConfig(t *testing.T) {
assert.Equal(t, r1.PrometheusConfig.ScrapeConfigs[0].JobName, "demo")
assert.Equal(t, time.Duration(r1.PrometheusConfig.ScrapeConfigs[0].ScrapeInterval), 5*time.Second)
assert.Equal(t, r1.UseStartTimeMetric, true)
assert.Equal(t, r1.TrimMetricSuffixes, true)
assert.Equal(t, r1.StartTimeMetricRegex, "^(.+_)*process_start_time_seconds$")

assert.Equal(t, "http://my-targetallocator-service", r1.TargetAllocator.Endpoint)
Expand Down
1 change: 0 additions & 1 deletion receiver/prometheusreceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ require (
github.com/golang/snappy v0.0.4
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusremotewriteexporter v0.81.0
github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.81.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.81.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.81.0
github.com/prometheus/common v0.44.0
Expand Down
7 changes: 5 additions & 2 deletions receiver/prometheusreceiver/internal/appendable.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type appendable struct {
sink consumer.Metrics
metricAdjuster MetricsAdjuster
useStartTimeMetric bool
trimSuffixes bool
startTimeMetricRegex *regexp.Regexp
externalLabels labels.Labels

Expand All @@ -35,7 +36,8 @@ func NewAppendable(
useStartTimeMetric bool,
startTimeMetricRegex *regexp.Regexp,
useCreatedMetric bool,
externalLabels labels.Labels) (storage.Appendable, error) {
externalLabels labels.Labels,
trimSuffixes bool) (storage.Appendable, error) {
var metricAdjuster MetricsAdjuster
if !useStartTimeMetric {
metricAdjuster = NewInitialPointAdjuster(set.Logger, gcInterval, useCreatedMetric)
Expand All @@ -56,9 +58,10 @@ func NewAppendable(
startTimeMetricRegex: startTimeMetricRegex,
externalLabels: externalLabels,
obsrecv: obsrecv,
trimSuffixes: trimSuffixes,
}, nil
}

func (o *appendable) Appender(ctx context.Context) storage.Appender {
return newTransaction(ctx, o.metricAdjuster, o.sink, o.externalLabels, o.settings, o.obsrecv)
return newTransaction(ctx, o.metricAdjuster, o.sink, o.externalLabels, o.settings, o.obsrecv, o.trimSuffixes)
}
10 changes: 7 additions & 3 deletions receiver/prometheusreceiver/internal/metricfamily.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,14 @@ func (mf *metricFamily) addSeries(seriesRef uint64, metricName string, ls labels
return nil
}

func (mf *metricFamily) appendMetric(metrics pmetric.MetricSlice) {
func (mf *metricFamily) appendMetric(metrics pmetric.MetricSlice, trimSuffixes bool) {
metric := pmetric.NewMetric()
// Trims type's and unit's suffixes from metric name
metric.SetName(prometheus.TrimPromSuffixes(mf.name, mf.mtype, mf.metadata.Unit))
// Trims type and unit suffixes from metric name
name := mf.name
if trimSuffixes {
name = prometheus.TrimPromSuffixes(name, mf.mtype, mf.metadata.Unit)
}
metric.SetName(name)
metric.SetDescription(mf.metadata.Help)
metric.SetUnit(mf.metadata.Unit)

Expand Down
6 changes: 3 additions & 3 deletions receiver/prometheusreceiver/internal/metricfamily_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func TestMetricGroupData_toDistributionUnitTest(t *testing.T) {
require.Len(t, mp.groups, 1)

sl := pmetric.NewMetricSlice()
mp.appendMetric(sl)
mp.appendMetric(sl, false)

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

sl := pmetric.NewMetricSlice()
mp.appendMetric(sl)
mp.appendMetric(sl, false)

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

sl := pmetric.NewMetricSlice()
mp.appendMetric(sl)
mp.appendMetric(sl, false)

require.Equal(t, 1, sl.Len(), "Exactly one metric expected")
metric := sl.At(0)
Expand Down
7 changes: 5 additions & 2 deletions receiver/prometheusreceiver/internal/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (

type transaction struct {
isNew bool
trimSuffixes bool
ctx context.Context
families map[string]*metricFamily
mc scrape.MetricMetadataStore
Expand All @@ -53,11 +54,13 @@ func newTransaction(
sink consumer.Metrics,
externalLabels labels.Labels,
settings receiver.CreateSettings,
obsrecv *obsreport.Receiver) *transaction {
obsrecv *obsreport.Receiver,
trimSuffixes bool) *transaction {
return &transaction{
ctx: ctx,
families: make(map[string]*metricFamily),
isNew: true,
trimSuffixes: trimSuffixes,
sink: sink,
metricAdjuster: metricAdjuster,
externalLabels: externalLabels,
Expand Down Expand Up @@ -200,7 +203,7 @@ func (t *transaction) getMetrics(resource pcommon.Resource) (pmetric.Metrics, er
metrics := ils.Metrics()

for _, mf := range t.families {
mf.appendMetric(metrics)
mf.appendMetric(metrics, t.trimSuffixes)
}

return md, nil
Expand Down
Loading

0 comments on commit e1631f8

Please sign in to comment.