diff --git a/.github/ALLOWLIST b/.github/ALLOWLIST index 871c58610899..14fbb6bac629 100644 --- a/.github/ALLOWLIST +++ b/.github/ALLOWLIST @@ -28,6 +28,7 @@ internal/sharedcomponent internal/tools pkg/batchperresourceattr pkg/experimentalmetricmetadata +pkg/translator/prometheus pkg/translator/prometheusremotewrite pkg/translator/signalfx pkg/winperfcounters diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d3c30659b6b5..4e8dd68bda89 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,7 +14,7 @@ # NOTE: Lines should be entered in the following format: # /.. # extension/oauth2clientauthextension/ @open-telemetry/collector-contrib-approvers @pavankrish123 @jpkrohling -# Path separator and minimum of 1 space between component path and owners is +# Path separator and minimum of 1 space between component path and owners is # important for validation steps # diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7d0156546523..dbc5c1b4b238 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -374,6 +374,10 @@ updates: directory: "/pkg/translator/opencensus" schedule: interval: "weekly" + - package-ecosystem: "gomod" + directory: "/pkg/translator/prometheus" + schedule: + interval: "weekly" - package-ecosystem: "gomod" directory: "/pkg/translator/prometheusremotewrite" schedule: diff --git a/CHANGELOG.md b/CHANGELOG.md index 9631473fe050..a9b058c54d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,10 @@ - `pkg/stanza`: Removed reference to deprecated `ClusterName` (#10426) - `couchbasereceiver`: Fully removed unimplemented Couchbase receiver (#10482) - `hostmetricsreciever`: Fix Load Scraper to normalize 1m, 5m, and 15m averages independently (#8267) +- `prometheusexporter`: Automatically rename metrics with units to follow Prometheus naming convention (#8950) + +### 🚩 Deprecations 🚩 + ### 🚀 New components 🚀 diff --git a/cmd/configschema/go.mod b/cmd/configschema/go.mod index 01ae8363ae8e..64c93ce02cb4 100644 --- a/cmd/configschema/go.mod +++ b/cmd/configschema/go.mod @@ -324,6 +324,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus v0.54.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.54.0 // indirect @@ -714,6 +715,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ../../pkg/translator/opencensus +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx => ../../pkg/translator/signalfx diff --git a/exporter/awsprometheusremotewriteexporter/go.mod b/exporter/awsprometheusremotewriteexporter/go.mod index cac7f5c40e06..0d0f10b6b9b7 100644 --- a/exporter/awsprometheusremotewriteexporter/go.mod +++ b/exporter/awsprometheusremotewriteexporter/go.mod @@ -31,6 +31,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.54.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite v0.54.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/common v0.35.0 // indirect @@ -66,4 +67,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/corei replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry => ../../pkg/resourcetotelemetry +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite diff --git a/exporter/awsprometheusremotewriteexporter/go.sum b/exporter/awsprometheusremotewriteexporter/go.sum index 2b0c7376ca7c..ec3047ad9438 100644 --- a/exporter/awsprometheusremotewriteexporter/go.sum +++ b/exporter/awsprometheusremotewriteexporter/go.sum @@ -228,8 +228,8 @@ github.com/knadh/koanf v1.4.2/go.mod h1:4NCo0q4pmU398vF9vq2jStF9MWQZ8JEDcDMHlDCr github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -262,6 +262,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.53.0 h1:jQDPrr2ndsDm3G3fpkD6kyNdr4L5Qc3EgWaAwQ8kNFQ= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= diff --git a/exporter/prometheusexporter/README.md b/exporter/prometheusexporter/README.md index 2f96a1a3ea4e..a304ce2be512 100644 --- a/exporter/prometheusexporter/README.md +++ b/exporter/prometheusexporter/README.md @@ -40,6 +40,10 @@ exporters: enabled: true ``` +## Metric names and labels normalization + +OpenTelemetry metric names and attributes are normalized to be compliant with Prometheus naming rules. [Details on this normalization process are described in the Prometheus translator module](../../pkg/translator/prometheus/). + [beta]:https://github.com/open-telemetry/opentelemetry-collector#beta [contrib]:https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib -[core]:https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol +[core]:https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol \ No newline at end of file diff --git a/exporter/prometheusexporter/collector.go b/exporter/prometheusexporter/collector.go index d3050a543dae..868f61d70f34 100644 --- a/exporter/prometheusexporter/collector.go +++ b/exporter/prometheusexporter/collector.go @@ -24,26 +24,26 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" conventions "go.opentelemetry.io/collector/semconv/v1.6.1" "go.uber.org/zap" + + prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" ) type collector struct { accumulator accumulator logger *zap.Logger - sendTimestamps bool - namespace string - constLabels prometheus.Labels - skipSanitizeLabel bool + sendTimestamps bool + namespace string + constLabels prometheus.Labels } func newCollector(config *Config, logger *zap.Logger) *collector { return &collector{ - accumulator: newAccumulator(logger, config.MetricExpiration), - logger: logger, - namespace: sanitize(config.Namespace, config.skipSanitizeLabel), - sendTimestamps: config.SendTimestamps, - constLabels: config.ConstLabels, - skipSanitizeLabel: config.skipSanitizeLabel, + accumulator: newAccumulator(logger, config.MetricExpiration), + logger: logger, + namespace: prometheustranslator.CleanUpString(config.Namespace), + sendTimestamps: config.SendTimestamps, + constLabels: config.ConstLabels, } } @@ -75,19 +75,12 @@ func (c *collector) convertMetric(metric pmetric.Metric, resourceAttrs pcommon.M return nil, errUnknownMetricType } -func (c *collector) metricName(namespace string, metric pmetric.Metric) string { - if namespace != "" { - return namespace + "_" + sanitize(metric.Name(), c.skipSanitizeLabel) - } - return sanitize(metric.Name(), c.skipSanitizeLabel) -} - func (c *collector) getMetricMetadata(metric pmetric.Metric, attributes pcommon.Map, resourceAttrs pcommon.Map) (*prometheus.Desc, []string) { keys := make([]string, 0, attributes.Len()+2) // +2 for job and instance labels. values := make([]string, 0, attributes.Len()+2) attributes.Range(func(k string, v pcommon.Value) bool { - keys = append(keys, sanitize(k, c.skipSanitizeLabel)) + keys = append(keys, prometheustranslator.NormalizeLabel(k)) values = append(values, v.AsString()) return true }) @@ -108,7 +101,7 @@ func (c *collector) getMetricMetadata(metric pmetric.Metric, attributes pcommon. } return prometheus.NewDesc( - c.metricName(c.namespace, metric), + prometheustranslator.BuildPromCompliantName(metric, c.namespace), metric.Description(), keys, c.constLabels, diff --git a/exporter/prometheusexporter/config.go b/exporter/prometheusexporter/config.go index 41ab87ad0f4f..505bd18b53a6 100644 --- a/exporter/prometheusexporter/config.go +++ b/exporter/prometheusexporter/config.go @@ -19,7 +19,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/collector/config" - "go.opentelemetry.io/collector/service/featuregate" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" ) @@ -45,19 +44,10 @@ type Config struct { // ResourceToTelemetrySettings defines configuration for converting resource attributes to metric labels. ResourceToTelemetrySettings resourcetotelemetry.Settings `mapstructure:"resource_to_telemetry_conversion"` - - // skipSanitizeLabel if enabled, labels that start with _ are not sanitized - skipSanitizeLabel bool } var _ config.Exporter = (*Config)(nil) -var dropSanitizationGate = featuregate.Gate{ - ID: "exporter.prometheus.PermissiveLabelSanitization", - Enabled: false, - Description: "Controls whether to change labels starting with '_' to 'key_'", -} - // Validate checks if the exporter configuration is valid func (cfg *Config) Validate() error { return nil diff --git a/exporter/prometheusexporter/config_test.go b/exporter/prometheusexporter/config_test.go index 8ea29c71882b..5313d987c851 100644 --- a/exporter/prometheusexporter/config_test.go +++ b/exporter/prometheusexporter/config_test.go @@ -27,6 +27,7 @@ import ( ) func TestLoadConfig(t *testing.T) { + factories, err := componenttest.NopFactories() assert.NoError(t, err) @@ -50,8 +51,8 @@ func TestLoadConfig(t *testing.T) { "label1": "value1", "another label": "spaced value", }, - SendTimestamps: true, - MetricExpiration: 60 * time.Minute, - skipSanitizeLabel: false, + SendTimestamps: true, + MetricExpiration: 60 * time.Minute, }) + } diff --git a/exporter/prometheusexporter/factory.go b/exporter/prometheusexporter/factory.go index 05af3cdde06f..30602e6dd731 100644 --- a/exporter/prometheusexporter/factory.go +++ b/exporter/prometheusexporter/factory.go @@ -22,7 +22,6 @@ import ( "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.opentelemetry.io/collector/service/featuregate" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" ) @@ -42,11 +41,10 @@ func NewFactory() component.ExporterFactory { func createDefaultConfig() config.Exporter { return &Config{ - ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)), - ConstLabels: map[string]string{}, - SendTimestamps: false, - MetricExpiration: time.Minute * 5, - skipSanitizeLabel: featuregate.GetRegistry().IsEnabled(dropSanitizationGate.ID), + ExporterSettings: config.NewExporterSettings(config.NewComponentID(typeStr)), + ConstLabels: map[string]string{}, + SendTimestamps: false, + MetricExpiration: time.Minute * 5, } } diff --git a/exporter/prometheusexporter/go.mod b/exporter/prometheusexporter/go.mod index 3e252e5759e5..240d17acdc67 100644 --- a/exporter/prometheusexporter/go.mod +++ b/exporter/prometheusexporter/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.54.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.54.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.54.0 github.com/prometheus/client_golang v1.12.2 github.com/prometheus/client_model v0.2.0 @@ -153,8 +154,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/corei replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ../../pkg/translator/opencensus -replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite - replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry => ../../pkg/resourcetotelemetry replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver => ../../receiver/prometheusreceiver + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus diff --git a/exporter/prometheusexporter/go.sum b/exporter/prometheusexporter/go.sum index 19344f5a2ea0..1d52294d908d 100644 --- a/exporter/prometheusexporter/go.sum +++ b/exporter/prometheusexporter/go.sum @@ -448,8 +448,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -535,6 +535,8 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.53.0 h1:jQDPrr2ndsDm3G3fpkD6kyNdr4L5Qc3EgWaAwQ8kNFQ= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite v0.54.0 h1:UrpuDtHmOIL/WOKvGoIoRXUazFsAXnzNQAXbCOW08+E= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= diff --git a/exporter/prometheusexporter/prometheus_test.go b/exporter/prometheusexporter/prometheus_test.go index 160ef09e3da5..53d482642266 100644 --- a/exporter/prometheusexporter/prometheus_test.go +++ b/exporter/prometheusexporter/prometheus_test.go @@ -141,14 +141,14 @@ func TestPrometheusExporter_endToEnd(t *testing.T) { blob, _ := ioutil.ReadAll(res.Body) _ = res.Body.Close() want := []string{ - `# HELP test_metric_1_this_one_there_where_ Extra ones`, - `# TYPE test_metric_1_this_one_there_where_ counter`, - fmt.Sprintf(`test_metric_1_this_one_there_where_{arch="x86",code1="one1",foo1="bar1",os="windows"} %v`, 99+128), - fmt.Sprintf(`test_metric_1_this_one_there_where_{arch="x86",code1="one1",foo1="bar1",os="linux"} %v`, 100+128), - `# HELP test_metric_2_this_one_there_where_ Extra ones`, - `# TYPE test_metric_2_this_one_there_where_ counter`, - fmt.Sprintf(`test_metric_2_this_one_there_where_{arch="x86",code1="one1",foo1="bar1",os="windows"} %v`, 99+delta), - fmt.Sprintf(`test_metric_2_this_one_there_where_{arch="x86",code1="one1",foo1="bar1",os="linux"} %v`, 100+delta), + `# HELP test_metric_1_this_one_there_where Extra ones`, + `# TYPE test_metric_1_this_one_there_where counter`, + fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",os="windows"} %v`, 99+128), + fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",os="linux"} %v`, 100+128), + `# HELP test_metric_2_this_one_there_where Extra ones`, + `# TYPE test_metric_2_this_one_there_where counter`, + fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",os="windows"} %v`, 99+delta), + fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",os="linux"} %v`, 100+delta), } for _, w := range want { @@ -217,14 +217,14 @@ func TestPrometheusExporter_endToEndWithTimestamps(t *testing.T) { blob, _ := ioutil.ReadAll(res.Body) _ = res.Body.Close() want := []string{ - `# HELP test_metric_1_this_one_there_where_ Extra ones`, - `# TYPE test_metric_1_this_one_there_where_ counter`, - fmt.Sprintf(`test_metric_1_this_one_there_where_{arch="x86",code2="one2",foo2="bar2",os="windows"} %v %v`, 99+128, 1543160298100+128000), - fmt.Sprintf(`test_metric_1_this_one_there_where_{arch="x86",code2="one2",foo2="bar2",os="linux"} %v %v`, 100+128, 1543160298100), - `# HELP test_metric_2_this_one_there_where_ Extra ones`, - `# TYPE test_metric_2_this_one_there_where_ counter`, - fmt.Sprintf(`test_metric_2_this_one_there_where_{arch="x86",code2="one2",foo2="bar2",os="windows"} %v %v`, 99+delta, 1543160298100+delta*1000), - fmt.Sprintf(`test_metric_2_this_one_there_where_{arch="x86",code2="one2",foo2="bar2",os="linux"} %v %v`, 100+delta, 1543160298100), + `# HELP test_metric_1_this_one_there_where Extra ones`, + `# TYPE test_metric_1_this_one_there_where counter`, + fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code2="one2",foo2="bar2",os="windows"} %v %v`, 99+128, 1543160298100+128000), + fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code2="one2",foo2="bar2",os="linux"} %v %v`, 100+128, 1543160298100), + `# HELP test_metric_2_this_one_there_where Extra ones`, + `# TYPE test_metric_2_this_one_there_where counter`, + fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code2="one2",foo2="bar2",os="windows"} %v %v`, 99+delta, 1543160298100+delta*1000), + fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code2="one2",foo2="bar2",os="linux"} %v %v`, 100+delta, 1543160298100), } for _, w := range want { diff --git a/exporter/prometheusexporter/sanitize.go b/exporter/prometheusexporter/sanitize.go deleted file mode 100644 index f7af62c2246d..000000000000 --- a/exporter/prometheusexporter/sanitize.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// nolint:gocritic -package prometheusexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/prometheusexporter" - -import ( - "strings" - "unicode" -) - -// sanitize replaces non-alphanumeric characters with underscores in s. -// The code for sanitize is mostly copied from: -// https://github.com/census-instrumentation/opencensus-go/blob/950a67f393d867cfbe91414063b69e511f42fefb/internal/sanitize.go#L1-L50 -func sanitize(s string, skipSanitizeLabel bool) string { - if len(s) == 0 { - return s - } - - // Note: No length limit for label keys because Prometheus doesn't - // define a length limit, thus we should NOT be truncating label keys. - // See https://github.com/orijtech/prometheus-go-metrics-exporter/issues/4. - - s = strings.Map(sanitizeRune, s) - if unicode.IsDigit(rune(s[0])) { - s = "key_" + s - } - //replace labels starting with _ only when skipSanitizeLabel is disabled - if !skipSanitizeLabel && s[0] == '_' { - s = "key" + s - } - - if s[0] == '_' && s[1] == '_' { - s = "key" + s - } - return s -} - -// converts anything that is not a letter or digit to an underscore -func sanitizeRune(r rune) rune { - if unicode.IsLetter(r) || unicode.IsDigit(r) { - return r - } - // Everything else turns into an underscore - return '_' -} diff --git a/exporter/prometheusexporter/sanitize_test.go b/exporter/prometheusexporter/sanitize_test.go deleted file mode 100644 index 6132c078c20c..000000000000 --- a/exporter/prometheusexporter/sanitize_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// nolint:gocritic -package prometheusexporter - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestSanitize(t *testing.T) { - cfg := createDefaultConfig().(*Config) - require.Equal(t, "", sanitize("", cfg.skipSanitizeLabel), "") - require.Equal(t, "key_test", sanitize("_test", cfg.skipSanitizeLabel)) - require.Equal(t, "key_0test", sanitize("0test", cfg.skipSanitizeLabel)) - require.Equal(t, "test", sanitize("test", cfg.skipSanitizeLabel)) - require.Equal(t, "test__", sanitize("test_/", cfg.skipSanitizeLabel)) - require.Equal(t, "key__test", sanitize("__test", cfg.skipSanitizeLabel)) - //enable skipSanitizeLabel - cfg.skipSanitizeLabel = true - require.Equal(t, "_test", sanitize("_test", cfg.skipSanitizeLabel)) - require.Equal(t, "", sanitize("", cfg.skipSanitizeLabel), "") - require.Equal(t, "key_0test", sanitize("0test", cfg.skipSanitizeLabel)) - require.Equal(t, "test", sanitize("test", cfg.skipSanitizeLabel)) - require.Equal(t, "key__test", sanitize("__test", cfg.skipSanitizeLabel)) - -} diff --git a/exporter/prometheusremotewriteexporter/README.md b/exporter/prometheusremotewriteexporter/README.md index 3f0f4a7ef938..9f4f88d6b528 100644 --- a/exporter/prometheusremotewriteexporter/README.md +++ b/exporter/prometheusremotewriteexporter/README.md @@ -35,7 +35,7 @@ As a result, the following parameters are also required under `tls:`: The following settings can be optionally configured: - `external_labels`: map of labels names and values to be attached to each metric data point -- `headers`: additional headers attached to each HTTP request. +- `headers`: additional headers attached to each HTTP request. - *Note the following headers cannot be changed: `Content-Encoding`, `Content-Type`, `X-Prometheus-Remote-Write-Version`, and `User-Agent`.* - `namespace`: prefix attached to each exported metric name. - `remote_write_queue`: fine tuning for queueing and sending of the outgoing remote writes. @@ -63,7 +63,7 @@ exporters: endpoint: "https://my-cortex:7900/api/v1/push" external_labels: label_name1: label_value1 - label_name2: label_value2 + label_name2: label_value2 ``` ## Advanced Configuration @@ -73,3 +73,7 @@ Several helper files are leveraged to provide additional capabilities automatica - [HTTP settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/confighttp/README.md) - [TLS and mTLS settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/config/configtls/README.md) - [Retry and timeout settings](https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/exporterhelper/README.md), note that the exporter doesn't support `sending_queue` but provides `remote_write_queue`. + +## Metric names and labels normalization + +OpenTelemetry metric names and attributes are normalized to be compliant with Prometheus naming rules. [Details on this normalization process are described in the Prometheus translator module](../../pkg/translator/prometheus/). diff --git a/exporter/prometheusremotewriteexporter/config.go b/exporter/prometheusremotewriteexporter/config.go index ef2d1074a9b3..728f6a91ab5b 100644 --- a/exporter/prometheusremotewriteexporter/config.go +++ b/exporter/prometheusremotewriteexporter/config.go @@ -20,7 +20,6 @@ import ( "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.opentelemetry.io/collector/service/featuregate" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" ) @@ -30,7 +29,6 @@ type Config struct { config.ExporterSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct exporterhelper.TimeoutSettings `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct. exporterhelper.RetrySettings `mapstructure:"retry_on_failure"` - sanitizeLabel bool // prefix attached to each exported metric name // See: https://prometheus.io/docs/practices/naming/#metric-names @@ -67,16 +65,6 @@ type RemoteWriteQueue struct { NumConsumers int `mapstructure:"num_consumers"` } -var dropSanitizationGate = featuregate.Gate{ - ID: "exporter.prometheusremotewrite.PermissiveLabelSanitization", - Enabled: false, - Description: "Controls whether to change labels starting with '_' to 'key_'", -} - -func init() { - featuregate.GetRegistry().MustRegister(dropSanitizationGate) -} - // TODO(jbd): Add capacity, max_samples_per_send to QueueConfig. var _ config.Exporter = (*Config)(nil) diff --git a/exporter/prometheusremotewriteexporter/config_test.go b/exporter/prometheusremotewriteexporter/config_test.go index cbd24213e8d4..43acf230777a 100644 --- a/exporter/prometheusremotewriteexporter/config_test.go +++ b/exporter/prometheusremotewriteexporter/config_test.go @@ -31,8 +31,9 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" ) -// TestLoadConfig checks whether yaml configuration can be loaded correctly -func Test_loadConfig(t *testing.T) { +// From the default configurations -- checks if a correct exporter is instantiated +func TestLoadDefaultConfig(t *testing.T) { + factories, err := componenttest.NopFactories() assert.NoError(t, err) @@ -43,9 +44,21 @@ func Test_loadConfig(t *testing.T) { require.NoError(t, err) require.NotNil(t, cfg) - // From the default configurations -- checks if a correct exporter is instantiated - e0 := cfg.Exporters[(config.NewComponentID(typeStr))] + e0 := cfg.Exporters[config.NewComponentID(typeStr)] assert.Equal(t, e0, factory.CreateDefaultConfig()) +} + +// TestLoadConfig checks whether yaml configuration can be loaded correctly +func Test_loadConfigDefaultAutomaticRename(t *testing.T) { + factories, err := componenttest.NopFactories() + assert.NoError(t, err) + + factory := NewFactory() + factories.Exporters[typeStr] = factory + cfg, err := servicetest.LoadConfigAndValidate(filepath.Join("testdata", "config.yaml"), factories) + + require.NoError(t, err) + require.NotNil(t, cfg) // checks if the correct Config struct can be instantiated from testdata/config.yaml e1 := cfg.Exporters[config.NewComponentIDWithName(typeStr, "2")] @@ -65,7 +78,6 @@ func Test_loadConfig(t *testing.T) { NumConsumers: 10, }, Namespace: "test-space", - sanitizeLabel: false, ExternalLabels: map[string]string{"key1": "value1", "key2": "value2"}, HTTPClientSettings: confighttp.HTTPClientSettings{ Endpoint: "localhost:8888", diff --git a/exporter/prometheusremotewriteexporter/exporter.go b/exporter/prometheusremotewriteexporter/exporter.go index f56183d60c70..7134e4466b9e 100644 --- a/exporter/prometheusremotewriteexporter/exporter.go +++ b/exporter/prometheusremotewriteexporter/exporter.go @@ -36,6 +36,7 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" "go.uber.org/multierr" + prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite" ) @@ -148,23 +149,7 @@ func validateAndSanitizeExternalLabels(cfg *Config) (map[string]string, error) { if key == "" || value == "" { return nil, fmt.Errorf("prometheus remote write: external labels configuration contains an empty key or value") } - - // Sanitize label keys to meet Prometheus Requirements - // if sanitizeLabel is enabled, invoke sanitizeLabels else sanitize - if len(key) > 2 && key[:2] == "__" { - if cfg.sanitizeLabel { - key = "__" + sanitizeLabels(key[2:]) - } else { - key = "__" + sanitize(key[2:]) - } - } else { - if cfg.sanitizeLabel { - key = sanitizeLabels(key) - } else { - key = sanitize(key) - } - } - sanitizedLabels[key] = value + sanitizedLabels[prometheustranslator.NormalizeLabel(key)] = value } return sanitizedLabels, nil diff --git a/exporter/prometheusremotewriteexporter/exporter_test.go b/exporter/prometheusremotewriteexporter/exporter_test.go index 04cedc89937c..974f67a78871 100644 --- a/exporter/prometheusremotewriteexporter/exporter_test.go +++ b/exporter/prometheusremotewriteexporter/exporter_test.go @@ -51,7 +51,6 @@ func Test_NewPRWExporter(t *testing.T) { Namespace: "", ExternalLabels: map[string]string{}, HTTPClientSettings: confighttp.HTTPClientSettings{Endpoint: ""}, - sanitizeLabel: true, } buildInfo := component.BuildInfo{ Description: "OpenTelemetry Collector", @@ -679,11 +678,6 @@ func Test_validateAndSanitizeExternalLabels(t *testing.T) { map[string]string{"__key1_key__": "val1"}, false, }, - {"labels_that_start_with_underscore", - map[string]string{"_key_": "val1"}, - map[string]string{"_key_": "val1"}, - false, - }, {"labels_that_start_with_digit", map[string]string{"6key_": "val1"}, map[string]string{"key_6key_": "val1"}, @@ -721,11 +715,6 @@ func Test_validateAndSanitizeExternalLabels(t *testing.T) { map[string]string{"__key1_key__": "val1"}, false, }, - {"labels_that_start_with_underscore", - map[string]string{"_key_": "val1"}, - map[string]string{"key_key_": "val1"}, - false, - }, {"labels_that_start_with_digit", map[string]string{"6key_": "val1"}, map[string]string{"key_6key_": "val1"}, @@ -740,7 +729,6 @@ func Test_validateAndSanitizeExternalLabels(t *testing.T) { // run tests for _, tt := range tests { cfg := createDefaultConfig().(*Config) - cfg.sanitizeLabel = true cfg.ExternalLabels = tt.inputLabels t.Run(tt.name, func(t *testing.T) { newLabels, err := validateAndSanitizeExternalLabels(cfg) @@ -756,7 +744,6 @@ func Test_validateAndSanitizeExternalLabels(t *testing.T) { for _, tt := range testsWithoutSanitizelabel { cfg := createDefaultConfig().(*Config) //disable sanitizeLabel flag - cfg.sanitizeLabel = false cfg.ExternalLabels = tt.inputLabels t.Run(tt.name, func(t *testing.T) { newLabels, err := validateAndSanitizeExternalLabels(cfg) diff --git a/exporter/prometheusremotewriteexporter/factory.go b/exporter/prometheusremotewriteexporter/factory.go index ce8bbe4fac94..2d902abd19a5 100644 --- a/exporter/prometheusremotewriteexporter/factory.go +++ b/exporter/prometheusremotewriteexporter/factory.go @@ -23,7 +23,6 @@ import ( "go.opentelemetry.io/collector/config" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/exporter/exporterhelper" - "go.opentelemetry.io/collector/service/featuregate" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry" ) @@ -86,7 +85,6 @@ func createDefaultConfig() config.Exporter { Namespace: "", ExternalLabels: map[string]string{}, TimeoutSettings: exporterhelper.NewDefaultTimeoutSettings(), - sanitizeLabel: featuregate.GetRegistry().IsEnabled(dropSanitizationGate.ID), RetrySettings: exporterhelper.RetrySettings{ Enabled: true, InitialInterval: 50 * time.Millisecond, diff --git a/exporter/prometheusremotewriteexporter/go.mod b/exporter/prometheusremotewriteexporter/go.mod index a48a432d10d8..6ef3f29b85f8 100644 --- a/exporter/prometheusremotewriteexporter/go.mod +++ b/exporter/prometheusremotewriteexporter/go.mod @@ -9,6 +9,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.54.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.54.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite v0.54.0 github.com/prometheus/prometheus v0.36.2 github.com/stretchr/testify v1.7.4 @@ -62,4 +63,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/corei replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry => ../../pkg/resourcetotelemetry +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite diff --git a/exporter/prometheusremotewriteexporter/go.sum b/exporter/prometheusremotewriteexporter/go.sum index 98d60a4e031c..61496c3e8972 100644 --- a/exporter/prometheusremotewriteexporter/go.sum +++ b/exporter/prometheusremotewriteexporter/go.sum @@ -224,8 +224,8 @@ github.com/knadh/koanf v1.4.2/go.mod h1:4NCo0q4pmU398vF9vq2jStF9MWQZ8JEDcDMHlDCr github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -258,6 +258,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.53.0 h1:jQDPrr2ndsDm3G3fpkD6kyNdr4L5Qc3EgWaAwQ8kNFQ= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= diff --git a/exporter/prometheusremotewriteexporter/helper.go b/exporter/prometheusremotewriteexporter/helper.go index 7f28bddebe25..222b3a7b102a 100644 --- a/exporter/prometheusremotewriteexporter/helper.go +++ b/exporter/prometheusremotewriteexporter/helper.go @@ -17,16 +17,10 @@ package prometheusremotewriteexporter // import "github.com/open-telemetry/opent import ( "errors" "sort" - "strings" - "unicode" "github.com/prometheus/prometheus/prompb" ) -const ( - keyStr = "key" -) - // batchTimeSeries splits series into multiple batch write requests. func batchTimeSeries(tsMap map[string]*prompb.TimeSeries, maxBatchByteSize int) ([]*prompb.WriteRequest, error) { if len(tsMap) == 0 { @@ -81,48 +75,3 @@ func orderBySampleTimestamp(tsArray []prompb.TimeSeries) []prompb.TimeSeries { } return tsArray } - -// copied from prometheus-go-metric-exporter -// sanitize replaces non-alphanumeric characters with underscores in s. -func sanitize(s string) string { - if len(s) == 0 { - return s - } - - // Note: No length limit for label keys because Prometheus doesn't - // define a length limit, thus we should NOT be truncating label keys. - // See https://github.com/orijtech/prometheus-go-metrics-exporter/issues/4. - s = strings.Map(sanitizeRune, s) - if unicode.IsDigit(rune(s[0])) { - s = keyStr + "_" + s - } - if s[0] == '_' { - s = keyStr + s - } - return s -} - -func sanitizeLabels(s string) string { - if len(s) == 0 { - return s - } - - // Note: No length limit for label keys because Prometheus doesn't - // define a length limit, thus we should NOT be truncating label keys. - // labels that start with _ are not sanitized - s = strings.Map(sanitizeRune, s) - if unicode.IsDigit(rune(s[0])) { - s = keyStr + "_" + s - } - return s -} - -// copied from prometheus-go-metric-exporter -// sanitizeRune converts anything that is not a letter or digit to an underscore -func sanitizeRune(r rune) rune { - if unicode.IsLetter(r) || unicode.IsDigit(r) { - return r - } - // Everything else turns into an underscore - return '_' -} diff --git a/go.mod b/go.mod index c20414cf2212..a73c43fd022d 100644 --- a/go.mod +++ b/go.mod @@ -407,6 +407,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus v0.54.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.54.0 // indirect @@ -718,6 +719,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ./pkg/translator/opencensus +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ./pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ./pkg/translator/prometheusremotewrite replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx => ./pkg/translator/signalfx diff --git a/internal/common/go.mod b/internal/common/go.mod index 1572169b2da6..ec8dc4091d64 100644 --- a/internal/common/go.mod +++ b/internal/common/go.mod @@ -3,7 +3,8 @@ module github.com/open-telemetry/opentelemetry-collector-contrib/internal/common go 1.17 require ( - github.com/stretchr/testify v1.7.3 + github.com/stretchr/testify v1.7.4 + go.opentelemetry.io/collector v0.54.0 go.uber.org/zap v1.21.0 ) @@ -12,9 +13,8 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.uber.org/atomic v1.7.0 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/common/go.sum b/internal/common/go.sum index d15b8d6b0835..dbd376f86dfd 100644 --- a/internal/common/go.sum +++ b/internal/common/go.sum @@ -1,5 +1,5 @@ -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -21,11 +21,14 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.3 h1:dAm0YRdRQlWojc3CrCRgPBzG5f941d0zvAKu7qY4e+I= -github.com/stretchr/testify v1.7.3/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.opentelemetry.io/collector v0.54.0 h1:GGSLxp90IbdySxXdk1CA2aT8l/gZt+przVL43uQEYp4= +go.opentelemetry.io/collector v0.54.0/go.mod h1:FgNzyfb4sAGb5cqusB5znETJ8Pz4OQUBGbOeGIZ2rlQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -64,7 +67,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/common/testutil/testutil.go b/internal/common/testutil/testutil.go index d989e2f0cbac..6460a24d81af 100644 --- a/internal/common/testutil/testutil.go +++ b/internal/common/testutil/testutil.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/service/featuregate" ) type portpair struct { @@ -109,3 +110,13 @@ func createExclusionsList(t testing.TB, exclusionsText string) []portpair { } return exclusions } + +// Force the state of feature gate for a test +// usage: defer SetFeatureGateForTest("gateName", true)() +func SetFeatureGateForTest(gate string, enabled bool) func() { + originalValue := featuregate.GetRegistry().IsEnabled(gate) + featuregate.GetRegistry().Apply(map[string]bool{gate: enabled}) + return func() { + featuregate.GetRegistry().Apply(map[string]bool{gate: originalValue}) + } +} diff --git a/pkg/translator/prometheus/Makefile b/pkg/translator/prometheus/Makefile new file mode 100644 index 000000000000..bdd863a203be --- /dev/null +++ b/pkg/translator/prometheus/Makefile @@ -0,0 +1 @@ +include ../../../Makefile.Common diff --git a/pkg/translator/prometheus/README.md b/pkg/translator/prometheus/README.md new file mode 100644 index 000000000000..5512db53ce38 --- /dev/null +++ b/pkg/translator/prometheus/README.md @@ -0,0 +1,112 @@ +# Prometheus Normalization + +[OpenTelemetry's metric semantic convention](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/README.md) is not compatible with [Prometheus' own metrics naming convention](https://prometheus.io/docs/practices/naming/). This module provides centralized functions to convert OpenTelemetry metrics to Prometheus-compliant metrics. These functions are used by the exporters for Prometheus: + +* [prometheusexporter](../../../exporter/prometheusexporter/) +* [prometheusremotewriteexporter](../../../exporter/prometheusremotewriteexporter/) + +## Metric name + +### Full normalization + +> **Warning** +> +> This feature must be enabled with [feature gate](https://github.com/open-telemetry/opentelemetry-collector/tree/main/service/featuregate) `pkg.translator.prometheus.NormalizeName`. It is disabled by default (alpha stage). +> +> ```shell-session +> $ otelcol --config=config.yaml --feature-gates=pkg.translator.prometheus.NormalizeName +> ``` + +List of transformations performed on OpenTelemetry metrics names: + +| Case | Transformation | Example | +| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| Unsupported characters and extraneous underscores | Replace unsupported characters with underscores (`_`). Drop redundant, leading and trailing underscores. | `(lambda).function.executions(#)` ==> `lambda_function_executions` | +| Standard unit | Convert the unit from [Unified Code for Units of Measure](http://unitsofmeasure.org/ucum.html) to Prometheus standard and append | `system.filesystem.usage` with unit `By` ==> `system_filesystem_usage_bytes` | +| Non-standard unit (unit is surrounded with `{}`) | Drop the unit | `system.network.dropped` with unit `{packets}` ==> `system_network_dropped` | +| Non-standard unit (unit is **not** surrounded with `{}`) | Append the unit, if not already present, after sanitization (all non-alphanumeric chars are dropped) | `system.network.dropped` with unit `packets` ==> `system_network_dropped_packets` | +| Percentages (unit is `1`) | Append `_ratio` (for gauges only) | `system.memory.utilization` with unit `1` ==> `system_memory_utilization_ratio` | +| Percentages (unit is `%`) | Replace `%` with `percent` `_percent` | `storage.filesystem.utilization` with unit `%` ==> `storage_filesystem_utilization_percent` | +| Rates (unit contains `/`) | Replace `/` with `per` | `astro.light.speed` with unit `m/s` ==> `astro_light_speed_meters_per_second` | +| Dollars (unit is `$`) | Replace `$` with `dollars` | `crypto.dogecoin.value` with unit `$` ==> `crypto.dogecoin.value_dollars` | +| Counter | Append `_total` | `system.processes.created` ==> `system_processes_created_total` | +List of standard OpenTelemetry units that will be translated to [Prometheus standard base units](https://prometheus.io/docs/practices/naming/#base-units): + +| OpenTelemetry Unit | Corresponding Prometheus Unit | +| ------------------ | ----------------------------- | +| **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` | +| `B` | `bytes` | +| `KB` | `kilobytes` | +| `MB` | `megabytes` | +| `GB` | `gigabytes` | +| `TB` | `terabytes` | +| **SI Units** | | +| `m` | `meters` | +| `V` | `volts` | +| `A` | `amperes` | +| `J` | `joules` | +| `W` | `watts` | +| `g` | `grams` | +| **Misc.** | | +| `Cel` | `celsius` | +| `Hz` | `hertz` | +| `%` | `percent` | +| `$` | `dollars` | + +> **Note** +> Prometheus also recommends using base units (no kilobytes, or milliseconds, for example) but these functions will not attempt to convert non-base units to base units. + +### Simple normalization + +If feature `pkg.translator.prometheus.NormalizeName` is not enabled, a simple sanitization of the OpenTelemetry metric name is performed to ensure it follows Prometheus naming conventions: + +* Drop unsupported characters and replace with underscores (`_`) +* Remove redundant, leading and trailing underscores +* Ensure metric name doesn't start with a digit by prefixing with an underscore + +No processing of the unit is performed, and `_total` is not appended for *Counters*. + +## Labels + +OpenTelemetry *Attributes* are converted to Prometheus labels and normalized to follow the [Prometheus labels naming rules](https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). + +The following transformations are performed on OpenTelemetry *Attributes* to produce Prometheus labels: + +* Drop unsupported characters and replace with underscores (`_`) +* Prefix label with `key_` if it doesn't start with a letter, except if it's already prefixed with double-underscore (`__`) + +By default, labels that start with a simple underscore (`_`) are prefixed with `key`, which is strictly unnecessary to follow Prometheus labels naming rules. This behavior can be disabled with the feature `pkg.translator.prometheus.PermissiveLabelSanitization`, which must be activated with the feature gate option of the collector: + +```shell-session +$ otelcol --config=config.yaml --feature-gates=pkg.translator.prometheus.PermissiveLabelSanitization +``` + +Examples: + +| OpenTelemetry Attribute | Prometheus Label | +|---|---| +| `name` | `name` | +| `host.name` | `host_name` | +| `host_name` | `host_name` | +| `name (of the host)` | `name__of_the_host_` | +| `2 cents` | `key_2_cents` | +| `__name` | `__name` | +| `_name` | `key_name` | +| `_name` | `_name` (if `PermissiveLabelSanitization` is enabled) | diff --git a/pkg/translator/prometheus/go.mod b/pkg/translator/prometheus/go.mod new file mode 100644 index 000000000000..1dbc1ad90341 --- /dev/null +++ b/pkg/translator/prometheus/go.mod @@ -0,0 +1,30 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus + +go 1.17 + +require ( + github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.53.0 + github.com/stretchr/testify v1.7.4 + go.opentelemetry.io/collector v0.54.0 + go.opentelemetry.io/collector/pdata v0.54.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.8 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20220524023933-508584e28198 // indirect + google.golang.org/grpc v1.47.0 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => ../../../internal/coreinternal + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/common => ../../../internal/common diff --git a/pkg/translator/prometheus/go.sum b/pkg/translator/prometheus/go.sum new file mode 100644 index 000000000000..5538e886a739 --- /dev/null +++ b/pkg/translator/prometheus/go.sum @@ -0,0 +1,176 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.4 h1:wZRexSlwd7ZXfKINDLsO4r7WBt3gTKONc6K/VesHvHM= +github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/collector v0.54.0 h1:GGSLxp90IbdySxXdk1CA2aT8l/gZt+przVL43uQEYp4= +go.opentelemetry.io/collector v0.54.0/go.mod h1:FgNzyfb4sAGb5cqusB5znETJ8Pz4OQUBGbOeGIZ2rlQ= +go.opentelemetry.io/collector/pdata v0.54.0 h1:oo3HyHwdf4lJmDUN0yrOGKj2tiHIoXDutDd0HKR++/0= +go.opentelemetry.io/collector/pdata v0.54.0/go.mod h1:1nSelv/YqGwdHHaIKNW9ZOHSMqicDX7W4/7TjNCm6N8= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220524023933-508584e28198 h1:a1g7i05I2vUwq5eYrmxBJy6rPbw/yo7WzzwPJmcC0P4= +google.golang.org/genproto v0.0.0-20220524023933-508584e28198/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/translator/prometheus/normalize_label.go b/pkg/translator/prometheus/normalize_label.go new file mode 100644 index 000000000000..aff29369255b --- /dev/null +++ b/pkg/translator/prometheus/normalize_label.go @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" + +import ( + "strings" + "unicode" + + "go.opentelemetry.io/collector/service/featuregate" +) + +var dropSanitizationGate = featuregate.Gate{ + ID: "pkg.translator.prometheus.PermissiveLabelSanitization", + Enabled: false, + Description: "Controls whether to change labels starting with '_' to 'key_'", +} + +func init() { + featuregate.GetRegistry().MustRegister(dropSanitizationGate) +} + +// Normalizes the specified label to follow Prometheus label names standard +// +// See rules at https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels +// +// Labels that start with non-letter rune will be prefixed with "key_" +// +// Exception is made for double-underscores which are allowed +func NormalizeLabel(label string) string { + + // Trivial case + if len(label) == 0 { + return label + } + + // Replace all non-alphanumeric runes with underscores + label = strings.Map(sanitizeRune, label) + + // If label starts with a number, prepend with "key_" + if unicode.IsDigit(rune(label[0])) { + label = "key_" + label + } else if strings.HasPrefix(label, "_") && !strings.HasPrefix(label, "__") && !featuregate.GetRegistry().IsEnabled(dropSanitizationGate.ID) { + label = "key" + label + } + + return label +} + +// Return '_' for anything non-alphanumeric +func sanitizeRune(r rune) rune { + if unicode.IsLetter(r) || unicode.IsDigit(r) { + return r + } + return '_' +} diff --git a/pkg/translator/prometheus/normalize_label_test.go b/pkg/translator/prometheus/normalize_label_test.go new file mode 100644 index 000000000000..308bf2a00477 --- /dev/null +++ b/pkg/translator/prometheus/normalize_label_test.go @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// nolint:gocritic +package prometheus // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil" +) + +func TestSanitize(t *testing.T) { + + defer testutil.SetFeatureGateForTest(dropSanitizationGate.ID, false)() + + require.Equal(t, "", NormalizeLabel(""), "") + require.Equal(t, "key_test", NormalizeLabel("_test")) + require.Equal(t, "key_0test", NormalizeLabel("0test")) + require.Equal(t, "test", NormalizeLabel("test")) + require.Equal(t, "test__", NormalizeLabel("test_/")) + require.Equal(t, "__test", NormalizeLabel("__test")) +} + +func TestSanitizeDropSanitization(t *testing.T) { + + defer testutil.SetFeatureGateForTest(dropSanitizationGate.ID, true)() + + require.Equal(t, "", NormalizeLabel("")) + require.Equal(t, "_test", NormalizeLabel("_test")) + require.Equal(t, "key_0test", NormalizeLabel("0test")) + require.Equal(t, "test", NormalizeLabel("test")) + require.Equal(t, "__test", NormalizeLabel("__test")) +} diff --git a/pkg/translator/prometheus/normalize_name.go b/pkg/translator/prometheus/normalize_name.go new file mode 100644 index 000000000000..5589f3a6c37f --- /dev/null +++ b/pkg/translator/prometheus/normalize_name.go @@ -0,0 +1,237 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" + +import ( + "strings" + "unicode" + + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/service/featuregate" +) + +// The map to translate OTLP units to Prometheus units +// OTLP metrics use the c/s notation as specified at https://ucum.org/ucum.html +// (See also https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/README.md#instrument-units) +// Prometheus best practices for units: https://prometheus.io/docs/practices/naming/#base-units +// OpenMetrics specification for units: https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#units-and-base-units +var unitMap = map[string]string{ + + // 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", + "B": "bytes", + "KB": "kilobytes", + "MB": "megabytes", + "GB": "gigabytes", + "TB": "terabytes", + + // SI + "m": "meters", + "V": "volts", + "A": "amperes", + "J": "joules", + "W": "watts", + "g": "grams", + + // Misc + "Cel": "celsius", + "Hz": "hertz", + "1": "", + "%": "percent", + "$": "dollars", +} + +// The map that translates the "per" unit +// Example: s => per second (singular) +var perUnitMap = map[string]string{ + "s": "second", + "m": "minute", + "h": "hour", + "d": "day", + "w": "week", + "mo": "month", + "y": "year", +} + +var normalizeNameGate = featuregate.Gate{ + ID: "pkg.translator.prometheus.NormalizeName", + Enabled: false, + Description: "Controls whether metrics names are automatically normalized to follow Prometheus naming convention", +} + +func init() { + // Register the feature gates + featuregate.GetRegistry().MustRegister(normalizeNameGate) +} + +// Build a Prometheus-compliant metric name for the specified metric +// +// Metric name is prefixed with specified namespace and underscore (if any). +// Namespace is not cleaned up. Make sure specified namespace follows Prometheus +// naming convention. +// +// See rules at https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels +// and https://prometheus.io/docs/practices/naming/#metric-and-label-naming +func BuildPromCompliantName(metric pmetric.Metric, namespace string) string { + var metricName string + + // Full normalization following standard Prometheus naming conventions + if featuregate.GetRegistry().IsEnabled(normalizeNameGate.ID) { + return normalizeName(metric, namespace) + } + + // Simple case (no full normalization, no units, etc.), we simply trim out forbidden chars + metricName = CleanUpString(metric.Name()) + + // Namespace? + if namespace != "" { + return namespace + "_" + metricName + } + + // Metric name starts with a digit? Prefix it with an underscore + if metricName != "" && unicode.IsDigit(rune(metricName[0])) { + metricName = "_" + metricName + } + + return metricName +} + +// Build a normalized name for the specified metric +func normalizeName(metric pmetric.Metric, namespace string) string { + + // Split metric name in "tokens" (remove all non-alphanumeric) + nameTokens := strings.FieldsFunc( + metric.Name(), + func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsDigit(r) }, + ) + + // Split unit at the '/' if any + unitTokens := strings.SplitN(metric.Unit(), "/", 2) + + // Main unit + // Append if not blank, doesn't contain '{}', and is not present in metric name already + if len(unitTokens) > 0 { + mainUnitOtel := strings.TrimSpace(unitTokens[0]) + if mainUnitOtel != "" && !strings.ContainsAny(mainUnitOtel, "{}") { + mainUnitProm := CleanUpString(unitMapGetOrDefault(mainUnitOtel)) + if mainUnitProm != "" && !contains(nameTokens, mainUnitProm) { + nameTokens = append(nameTokens, mainUnitProm) + } + } + + // Per unit + // Append if not blank, doesn't contain '{}', and is not present in metric name already + if len(unitTokens) > 1 && unitTokens[1] != "" { + perUnitOtel := strings.TrimSpace(unitTokens[1]) + if perUnitOtel != "" && !strings.ContainsAny(perUnitOtel, "{}") { + perUnitProm := CleanUpString(perUnitMapGetOrDefault(perUnitOtel)) + if perUnitProm != "" && !contains(nameTokens, perUnitProm) { + nameTokens = append(append(nameTokens, "per"), perUnitProm) + } + } + } + + } + + // Append _total for Counters + if metric.DataType() == pmetric.MetricDataTypeSum && metric.Sum().IsMonotonic() { + nameTokens = append(removeItem(nameTokens, "total"), "total") + } + + // Append _ratio for metrics with unit "1" + // Some Otel receivers improperly use unit "1" for counters of objects + // See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aissue+some+metric+units+don%27t+follow+otel+semantic+conventions + // Until these issues have been fixed, we're appending `_ratio` for gauges ONLY + // Theoretically, counters could be ratios as well, but it's absurd (for mathematical reasons) + if metric.Unit() == "1" && metric.DataType() == pmetric.MetricDataTypeGauge { + nameTokens = append(removeItem(nameTokens, "ratio"), "ratio") + } + + // Namespace? + if namespace != "" { + nameTokens = append([]string{namespace}, nameTokens...) + } + + // Build the string from the tokens, separated with underscores + normalizedName := strings.Join(nameTokens, "_") + + // Metric name cannot start with a digit, so prefix it with "_" in this case + if normalizedName != "" && unicode.IsDigit(rune(normalizedName[0])) { + normalizedName = "_" + normalizedName + } + + return normalizedName +} + +// Clean up specified string so it's Prometheus compliant +func CleanUpString(s string) string { + return strings.Join(strings.FieldsFunc(s, func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsDigit(r) }), "_") +} + +// Retrieve the Prometheus "basic" unit corresponding to the specified "basic" unit +// Returns the specified unit if not found in unitMap +func unitMapGetOrDefault(unit string) string { + if promUnit, ok := unitMap[unit]; ok { + return promUnit + } + return unit +} + +// Retrieve the Prometheus "per" unit corresponding to the specified "per" unit +// Returns the specified unit if not found in perUnitMap +func perUnitMapGetOrDefault(perUnit string) string { + if promPerUnit, ok := perUnitMap[perUnit]; ok { + return promPerUnit + } + return perUnit +} + +// Returns whether the slice contains the specified value +func contains(slice []string, value string) bool { + for _, sliceEntry := range slice { + if sliceEntry == value { + return true + } + } + return false +} + +// Remove the specified value from the slice +func removeItem(slice []string, value string) []string { + newSlice := make([]string, 0, len(slice)) + for _, sliceEntry := range slice { + if sliceEntry != value { + newSlice = append(newSlice, sliceEntry) + } + } + return newSlice +} diff --git a/pkg/translator/prometheus/normalize_name_test.go b/pkg/translator/prometheus/normalize_name_test.go new file mode 100644 index 000000000000..883b6ade5cde --- /dev/null +++ b/pkg/translator/prometheus/normalize_name_test.go @@ -0,0 +1,208 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/testutil" +) + +func TestByte(t *testing.T) { + + require.Equal(t, "system_filesystem_usage_bytes", normalizeName(createGauge("system.filesystem.usage", "By"), "")) + +} + +func TestByteCounter(t *testing.T) { + + require.Equal(t, "system_io_bytes_total", normalizeName(createCounter("system.io", "By"), "")) + require.Equal(t, "network_transmitted_bytes_total", normalizeName(createCounter("network_transmitted_bytes_total", "By"), "")) + +} + +func TestWhiteSpaces(t *testing.T) { + + require.Equal(t, "system_filesystem_usage_bytes", normalizeName(createGauge("\t system.filesystem.usage ", " By\t"), "")) + +} + +func TestNonStandardUnit(t *testing.T) { + + require.Equal(t, "system_network_dropped", normalizeName(createGauge("system.network.dropped", "{packets}"), "")) + +} + +func TestNonStandardUnitCounter(t *testing.T) { + + require.Equal(t, "system_network_dropped_total", normalizeName(createCounter("system.network.dropped", "{packets}"), "")) + +} + +func TestBrokenUnit(t *testing.T) { + + require.Equal(t, "system_network_dropped_packets", normalizeName(createGauge("system.network.dropped", "packets"), "")) + require.Equal(t, "system_network_packets_dropped", normalizeName(createGauge("system.network.packets.dropped", "packets"), "")) + require.Equal(t, "system_network_packets", normalizeName(createGauge("system.network.packets", "packets"), "")) + +} + +func TestBrokenUnitCounter(t *testing.T) { + + require.Equal(t, "system_network_dropped_packets_total", normalizeName(createCounter("system.network.dropped", "packets"), "")) + require.Equal(t, "system_network_packets_dropped_total", normalizeName(createCounter("system.network.packets.dropped", "packets"), "")) + require.Equal(t, "system_network_packets_total", normalizeName(createCounter("system.network.packets", "packets"), "")) + +} + +func TestRatio(t *testing.T) { + + require.Equal(t, "hw_gpu_memory_utilization_ratio", normalizeName(createGauge("hw.gpu.memory.utilization", "1"), "")) + require.Equal(t, "hw_fan_speed_ratio", normalizeName(createGauge("hw.fan.speed_ratio", "1"), "")) + require.Equal(t, "objects_total", normalizeName(createCounter("objects", "1"), "")) + +} + +func TestHertz(t *testing.T) { + + require.Equal(t, "hw_cpu_speed_limit_hertz", normalizeName(createGauge("hw.cpu.speed_limit", "Hz"), "")) + +} + +func TestPer(t *testing.T) { + + require.Equal(t, "broken_metric_speed_km_per_hour", normalizeName(createGauge("broken.metric.speed", "km/h"), "")) + require.Equal(t, "astro_light_speed_limit_meters_per_second", normalizeName(createGauge("astro.light.speed_limit", "m/s"), "")) + +} + +func TestPercent(t *testing.T) { + + require.Equal(t, "broken_metric_success_ratio_percent", normalizeName(createGauge("broken.metric.success_ratio", "%"), "")) + require.Equal(t, "broken_metric_success_percent", normalizeName(createGauge("broken.metric.success_percent", "%"), "")) + +} + +func TestDollar(t *testing.T) { + + require.Equal(t, "crypto_bitcoin_value_dollars", normalizeName(createGauge("crypto.bitcoin.value", "$"), "")) + require.Equal(t, "crypto_bitcoin_value_dollars", normalizeName(createGauge("crypto.bitcoin.value.dollars", "$"), "")) + +} + +func TestEmpty(t *testing.T) { + + require.Equal(t, "test_metric_no_unit", normalizeName(createGauge("test.metric.no_unit", ""), "")) + require.Equal(t, "test_metric_spaces", normalizeName(createGauge("test.metric.spaces", " \t "), "")) + +} + +func TestUnsupportedRunes(t *testing.T) { + + require.Equal(t, "unsupported_metric_temperature_F", normalizeName(createGauge("unsupported.metric.temperature", "°F"), "")) + require.Equal(t, "unsupported_metric_weird", normalizeName(createGauge("unsupported.metric.weird", "+=.:,!* & #"), "")) + require.Equal(t, "unsupported_metric_redundant_test_per_C", normalizeName(createGauge("unsupported.metric.redundant", "__test $/°C"), "")) + +} + +func TestOtelReceivers(t *testing.T) { + + require.Equal(t, "active_directory_ds_replication_network_io_bytes_total", normalizeName(createCounter("active_directory.ds.replication.network.io", "By"), "")) + require.Equal(t, "active_directory_ds_replication_sync_object_pending_total", normalizeName(createCounter("active_directory.ds.replication.sync.object.pending", "{objects}"), "")) + require.Equal(t, "active_directory_ds_replication_object_rate_per_second", normalizeName(createGauge("active_directory.ds.replication.object.rate", "{objects}/s"), "")) + require.Equal(t, "active_directory_ds_name_cache_hit_rate_percent", normalizeName(createGauge("active_directory.ds.name_cache.hit_rate", "%"), "")) + require.Equal(t, "active_directory_ds_ldap_bind_last_successful_time_milliseconds", normalizeName(createGauge("active_directory.ds.ldap.bind.last_successful.time", "ms"), "")) + require.Equal(t, "apache_current_connections", normalizeName(createGauge("apache.current_connections", "connections"), "")) + require.Equal(t, "apache_workers_connections", normalizeName(createGauge("apache.workers", "connections"), "")) + require.Equal(t, "apache_requests_total", normalizeName(createCounter("apache.requests", "1"), "")) + require.Equal(t, "bigip_virtual_server_request_count_total", normalizeName(createCounter("bigip.virtual_server.request.count", "{requests}"), "")) + require.Equal(t, "system_cpu_utilization_ratio", normalizeName(createGauge("system.cpu.utilization", "1"), "")) + require.Equal(t, "system_disk_operation_time_seconds_total", normalizeName(createCounter("system.disk.operation_time", "s"), "")) + require.Equal(t, "system_cpu_load_average_15m_ratio", normalizeName(createGauge("system.cpu.load_average.15m", "1"), "")) + require.Equal(t, "memcached_operation_hit_ratio_percent", normalizeName(createGauge("memcached.operation_hit_ratio", "%"), "")) + require.Equal(t, "mongodbatlas_process_asserts_per_second", normalizeName(createGauge("mongodbatlas.process.asserts", "{assertions}/s"), "")) + require.Equal(t, "mongodbatlas_process_journaling_data_files_mebibytes", normalizeName(createGauge("mongodbatlas.process.journaling.data_files", "MiBy"), "")) + require.Equal(t, "mongodbatlas_process_network_io_bytes_per_second", normalizeName(createGauge("mongodbatlas.process.network.io", "By/s"), "")) + require.Equal(t, "mongodbatlas_process_oplog_rate_gibibytes_per_hour", normalizeName(createGauge("mongodbatlas.process.oplog.rate", "GiBy/h"), "")) + require.Equal(t, "mongodbatlas_process_db_query_targeting_scanned_per_returned", normalizeName(createGauge("mongodbatlas.process.db.query_targeting.scanned_per_returned", "{scanned}/{returned}"), "")) + require.Equal(t, "nginx_requests", normalizeName(createGauge("nginx.requests", "requests"), "")) + require.Equal(t, "nginx_connections_accepted", normalizeName(createGauge("nginx.connections_accepted", "connections"), "")) + require.Equal(t, "nsxt_node_memory_usage_kilobytes", normalizeName(createGauge("nsxt.node.memory.usage", "KBy"), "")) + require.Equal(t, "redis_latest_fork_microseconds", normalizeName(createGauge("redis.latest_fork", "us"), "")) + +} + +func TestNamespace(t *testing.T) { + require.Equal(t, "space_test", normalizeName(createGauge("test", ""), "space")) + require.Equal(t, "space_test", normalizeName(createGauge("#test", ""), "space")) +} + +func TestCleanUpString(t *testing.T) { + require.Equal(t, "", CleanUpString("")) + require.Equal(t, "a_b", CleanUpString("a b")) + require.Equal(t, "hello_world", CleanUpString("hello, world!")) + require.Equal(t, "hello_you_2", CleanUpString("hello you 2")) + require.Equal(t, "1000", CleanUpString("$1000")) + require.Equal(t, "", CleanUpString("*+$^=)")) +} + +func TestUnitMapGetOrDefault(t *testing.T) { + + require.Equal(t, "", unitMapGetOrDefault("")) + require.Equal(t, "seconds", unitMapGetOrDefault("s")) + require.Equal(t, "invalid", unitMapGetOrDefault("invalid")) + +} + +func TestPerUnitMapGetOrDefault(t *testing.T) { + + require.Equal(t, "", perUnitMapGetOrDefault("")) + require.Equal(t, "second", perUnitMapGetOrDefault("s")) + require.Equal(t, "invalid", perUnitMapGetOrDefault("invalid")) + +} + +func TestRemoveItem(t *testing.T) { + + require.Equal(t, []string{}, removeItem([]string{}, "test")) + require.Equal(t, []string{}, removeItem([]string{}, "")) + require.Equal(t, []string{"a", "b", "c"}, removeItem([]string{"a", "b", "c"}, "d")) + require.Equal(t, []string{"a", "b", "c"}, removeItem([]string{"a", "b", "c"}, "")) + require.Equal(t, []string{"a", "b"}, removeItem([]string{"a", "b", "c"}, "c")) + require.Equal(t, []string{"a", "c"}, removeItem([]string{"a", "b", "c"}, "b")) + require.Equal(t, []string{"b", "c"}, removeItem([]string{"a", "b", "c"}, "a")) + +} + +func TestBuildPromCompliantNameWithNormalize(t *testing.T) { + + defer testutil.SetFeatureGateForTest(normalizeNameGate.ID, 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", ""), "")) + +} + +func TestBuildPromCompliantNameWithoutNormalize(t *testing.T) { + + defer testutil.SetFeatureGateForTest(normalizeNameGate.ID, 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")) + require.Equal(t, "_3_14_digits", BuildPromCompliantName(createGauge("3.14 digits", "By"), "")) + +} diff --git a/pkg/translator/prometheus/testutils_test.go b/pkg/translator/prometheus/testutils_test.go new file mode 100644 index 000000000000..9fd78e8bc538 --- /dev/null +++ b/pkg/translator/prometheus/testutils_test.go @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// nolint:gocritic +package prometheus // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" + +import ( + "go.opentelemetry.io/collector/pdata/pmetric" +) + +var ilm pmetric.ScopeMetrics + +func init() { + + metrics := pmetric.NewMetrics() + resourceMetrics := metrics.ResourceMetrics().AppendEmpty() + ilm = resourceMetrics.ScopeMetrics().AppendEmpty() + +} + +// Returns a new Metric of type "Gauge" with specified name and unit +func createGauge(name string, unit string) pmetric.Metric { + gauge := ilm.Metrics().AppendEmpty() + gauge.SetDataType(pmetric.MetricDataTypeGauge) + gauge.SetName(name) + gauge.SetUnit(unit) + return gauge +} + +// Returns a new Metric of type Monotonic Sum with specified name and unit +func createCounter(name string, unit string) pmetric.Metric { + counter := ilm.Metrics().AppendEmpty() + counter.SetDataType(pmetric.MetricDataTypeSum) + counter.Sum().SetIsMonotonic(true) + counter.SetName(name) + counter.SetUnit(unit) + return counter +} diff --git a/pkg/translator/prometheusremotewrite/go.mod b/pkg/translator/prometheusremotewrite/go.mod index d2e52e93f1ef..2f7af4901d92 100644 --- a/pkg/translator/prometheusremotewrite/go.mod +++ b/pkg/translator/prometheusremotewrite/go.mod @@ -4,6 +4,7 @@ go 1.17 require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.54.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 github.com/prometheus/common v0.35.0 github.com/prometheus/prometheus v0.36.2 github.com/stretchr/testify v1.7.4 @@ -18,6 +19,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/text v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -32,3 +34,7 @@ require ( ) replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal => ../../../internal/coreinternal + +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../prometheus + +replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/common => ../../../internal/common diff --git a/pkg/translator/prometheusremotewrite/go.sum b/pkg/translator/prometheusremotewrite/go.sum index 865c3a9226a3..be5475b5614f 100644 --- a/pkg/translator/prometheusremotewrite/go.sum +++ b/pkg/translator/prometheusremotewrite/go.sum @@ -55,6 +55,7 @@ github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -154,11 +155,12 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= diff --git a/pkg/translator/prometheusremotewrite/helper.go b/pkg/translator/prometheusremotewrite/helper.go index 05df7dfc1cd5..e92b348d7192 100644 --- a/pkg/translator/prometheusremotewrite/helper.go +++ b/pkg/translator/prometheusremotewrite/helper.go @@ -22,7 +22,6 @@ import ( "strconv" "strings" "time" - "unicode" "unicode/utf8" "github.com/prometheus/common/model" @@ -32,12 +31,11 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" conventions "go.opentelemetry.io/collector/semconv/v1.6.1" - "go.opentelemetry.io/collector/service/featuregate" + + prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus" ) const ( - dropSanitization = "pkg.translator.prometheusremotewrite.PermissiveLabelSanitization" - nameStr = "__name__" sumStr = "_sum" countStr = "_count" @@ -45,7 +43,6 @@ const ( leStr = "le" quantileStr = "quantile" pInfStr = "+Inf" - keyStr = "key" // maxExemplarRunes is the maximum number of UTF-8 exemplar characters // according to the prometheus specification // https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars @@ -58,16 +55,6 @@ const ( targetMetricName = "target" ) -var dropSanitizationGate = featuregate.Gate{ - ID: dropSanitization, - Enabled: false, - Description: "Controls whether to change labels starting with '_' to 'key_'", -} - -func init() { - featuregate.GetRegistry().MustRegister(dropSanitizationGate) -} - type bucketBoundsData struct { sig string bound float64 @@ -184,7 +171,7 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, externa attributes.CopyTo(cloneAttributes) cloneAttributes.Sort() cloneAttributes.Range(func(key string, value pcommon.Value) bool { - var finalKey = sanitize(key) + var finalKey = prometheustranslator.NormalizeLabel(key) if existingLabel, alreadyExists := l[finalKey]; alreadyExists { existingLabel.Value = existingLabel.Value + ";" + value.AsString() l[finalKey] = existingLabel @@ -239,7 +226,7 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, externa // internal labels should be maintained name := extras[i] if !(len(name) > 4 && name[:2] == "__" && name[len(name)-2:] == "__") { - name = sanitize(name) + name = prometheustranslator.NormalizeLabel(name) } l[name] = prompb.Label{ Name: name, @@ -255,16 +242,6 @@ func createAttributes(resource pcommon.Resource, attributes pcommon.Map, externa return s } -// getPromMetricName creates a Prometheus metric name by attaching namespace prefix for Monotonic metrics. -func getPromMetricName(metric pmetric.Metric, ns string) string { - name := metric.Name() - if len(ns) > 0 { - name = ns + "_" + name - } - - return sanitize(name) -} - // validateMetrics returns a bool representing whether the metric has a valid type and temporality combination and a // matching metric type and field func validateMetrics(metric pmetric.Metric) bool { @@ -285,7 +262,7 @@ func validateMetrics(metric pmetric.Metric) bool { // to its corresponding time series in tsMap func addSingleNumberDataPoint(pt pmetric.NumberDataPoint, resource pcommon.Resource, metric pmetric.Metric, settings Settings, tsMap map[string]*prompb.TimeSeries) { // create parameters for addSample - name := getPromMetricName(metric, settings.Namespace) + name := prometheustranslator.BuildPromCompliantName(metric, settings.Namespace) labels := createAttributes(resource, pt.Attributes(), settings.ExternalLabels, nameStr, name) sample := &prompb.Sample{ // convert ns to ms @@ -308,7 +285,7 @@ func addSingleNumberDataPoint(pt pmetric.NumberDataPoint, resource pcommon.Resou func addSingleHistogramDataPoint(pt pmetric.HistogramDataPoint, resource pcommon.Resource, metric pmetric.Metric, settings Settings, tsMap map[string]*prompb.TimeSeries) { time := convertTimeStamp(pt.Timestamp()) // sum, count, and buckets of the histogram should append suffix to baseName - baseName := getPromMetricName(metric, settings.Namespace) + baseName := prometheustranslator.BuildPromCompliantName(metric, settings.Namespace) // treat sum as a sample in an individual TimeSeries sum := &prompb.Sample{ Value: pt.Sum(), @@ -472,7 +449,7 @@ func addSingleSummaryDataPoint(pt pmetric.SummaryDataPoint, resource pcommon.Res tsMap map[string]*prompb.TimeSeries) { time := convertTimeStamp(pt.Timestamp()) // sum and count of the summary should append suffix to baseName - baseName := getPromMetricName(metric, settings.Namespace) + baseName := prometheustranslator.BuildPromCompliantName(metric, settings.Namespace) // treat sum as a sample in an individual TimeSeries sum := &prompb.Sample{ Value: pt.Sum(), @@ -543,36 +520,6 @@ func addResourceTargetInfo(resource pcommon.Resource, settings Settings, timesta addSample(tsMap, sample, labels, infoType) } -// copied from prometheus-go-metric-exporter -// sanitize replaces non-alphanumeric characters with underscores in s. -func sanitize(s string) string { - if len(s) == 0 { - return s - } - - // Note: No length limit for label keys because Prometheus doesn't - // define a length limit, thus we should NOT be truncating label keys. - // See https://github.com/orijtech/prometheus-go-metrics-exporter/issues/4. - s = strings.Map(sanitizeRune, s) - if unicode.IsDigit(rune(s[0])) { - s = keyStr + "_" + s - } - if !featuregate.GetRegistry().IsEnabled(dropSanitizationGate.ID) && s[0] == '_' { - s = keyStr + s - } - return s -} - -// copied from prometheus-go-metric-exporter -// sanitizeRune converts anything that is not a letter or digit to an underscore -func sanitizeRune(r rune) rune { - if unicode.IsLetter(r) || unicode.IsDigit(r) { - return r - } - // Everything else turns into an underscore - return '_' -} - // convertTimeStamp converts OTLP timestamp in ns to timestamp in ms func convertTimeStamp(timestamp pcommon.Timestamp) int64 { return timestamp.AsTime().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)) diff --git a/pkg/translator/prometheusremotewrite/helper_test.go b/pkg/translator/prometheusremotewrite/helper_test.go index 4853428af94e..6aae880b2f03 100644 --- a/pkg/translator/prometheusremotewrite/helper_test.go +++ b/pkg/translator/prometheusremotewrite/helper_test.go @@ -25,21 +25,10 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" conventions "go.opentelemetry.io/collector/semconv/v1.6.1" - "go.opentelemetry.io/collector/service/featuregate" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/testdata" ) -// setSanitizeLabelFeatureGateForTest changes the dropSanitizationGate feature gate during a test. -// usage: defer setSanitizeLabelFeatureGateForTest(true)() -func setSanitizeLabelFeatureGateForTest(enabled bool) func() { - originalValue := featuregate.GetRegistry().IsEnabled(dropSanitization) - featuregate.GetRegistry().Apply(map[string]bool{dropSanitization: enabled}) - return func() { - featuregate.GetRegistry().Apply(map[string]bool{dropSanitization: originalValue}) - } -} - // Test_validateMetrics checks validateMetrics return true if a type and temporality combination is valid, false // otherwise. func Test_validateMetrics(t *testing.T) { @@ -302,7 +291,7 @@ func Test_createLabelSet(t *testing.T) { lbs3, exlbs1, []string{label31, value31, label32, value32}, - getPromLabels(label11, value11, label12, value12, keyStr+label51, value51, label41, value41, label31, value31, label32, value32), + getPromLabels(label11, value11, label12, value12, "key"+label51, value51, label41, value41, label31, value31, label32, value32), }, } // run tests @@ -313,84 +302,6 @@ func Test_createLabelSet(t *testing.T) { } } -func Test_createLabelSetDropSanitization(t *testing.T) { - defer setSanitizeLabelFeatureGateForTest(true)() - tests := []struct { - name string - resource pcommon.Resource - orig pcommon.Map - externalLabels map[string]string - extras []string - want []prompb.Label - }{ - { - "donot_sanitize_labels_starts_with_underscore", - getResource(map[string]pcommon.Value{}), - lbs3, - exlbs1, - []string{label31, value31, label32, value32}, - getPromLabels(label11, value11, label12, value12, label51, value51, label41, value41, label31, value31, label32, value32), - }, - } - // run tests - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.ElementsMatch(t, tt.want, createAttributes(tt.resource, tt.orig, tt.externalLabels, tt.extras...)) - }) - } -} - -// Tes_getPromMetricName checks if OTLP metric names are converted to Cortex metric names correctly. -// Test cases are empty namespace, monotonic metrics that require a total suffix, and metric names that contains -// invalid characters. -func Test_getPromMetricName(t *testing.T) { - tests := []struct { - name string - metric pmetric.Metric - ns string - want string - }{ - { - "empty_case", - invalidMetrics[empty], - ns1, - "test_ns_", - }, - { - "normal_case", - validMetrics1[validDoubleGauge], - ns1, - "test_ns_" + validDoubleGauge, - }, - { - "empty_namespace", - validMetrics1[validDoubleGauge], - "", - validDoubleGauge, - }, - { - // Ensure removed functionality stays removed. - // See https://github.com/open-telemetry/opentelemetry-collector/pull/2993 for context - "no_counter_suffix", - validMetrics1[validIntSum], - ns1, - "test_ns_" + validIntSum, - }, - { - "dirty_string", - validMetrics2[validIntGaugeDirty], - "7" + ns1, - "key_7test_ns__" + validIntGauge + "_", - }, - } - // run tests - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert.Equal(t, tt.want, getPromMetricName(tt.metric, tt.ns)) - }) - } -} - // Test_addExemplars checks addExemplars updates the map it receives correctly based on the exemplars and bucket bounds data it receives. func Test_addExemplars(t *testing.T) { type testCase struct { diff --git a/pkg/translator/prometheusremotewrite/testutils_test.go b/pkg/translator/prometheusremotewrite/testutils_test.go index c52f471c4399..9743d98e0432 100644 --- a/pkg/translator/prometheusremotewrite/testutils_test.go +++ b/pkg/translator/prometheusremotewrite/testutils_test.go @@ -68,7 +68,6 @@ var ( floatVal2 = 2.0 lbs1 = getAttributes(label11, value11, label12, value12) - lbs2 = getAttributes(label21, value21, label22, value22) lbs3 = getAttributes(label11, value11, label12, value12, label51, value51) lbs1Dirty = getAttributes(label11+dirty1, value11, dirty2+label12, value12) lbsColliding = getAttributes(colliding1, value11, colliding2, value12) @@ -81,7 +80,6 @@ var ( lb1Sig = "-" + label11 + "-" + value11 + "-" + label12 + "-" + value12 lb2Sig = "-" + label21 + "-" + value21 + "-" + label22 + "-" + value22 - ns1 = "test_ns" twoPointsSameTs = map[string]*prompb.TimeSeries{ "Gauge" + "-" + label11 + "-" + value11 + "-" + label12 + "-" + value12: getTimeSeries(getPromLabels(label11, value11, label12, value12), @@ -123,10 +121,6 @@ var ( validSummary = "valid_Summary" suffixedCounter = "valid_IntSum_total" - validIntGaugeDirty = "*valid_IntGauge$" - - unmatchedBoundBucketHist = "unmatchedBoundBucketHist" - // valid metrics as input should not return error validMetrics1 = map[string]pmetric.Metric{ validIntGauge: getIntGaugeMetric(validIntGauge, lbs1, intVal1, time1), @@ -137,18 +131,6 @@ var ( validHistogram: getHistogramMetric(validHistogram, lbs1, time1, floatVal1, uint64(intVal1), bounds, buckets), validSummary: getSummaryMetric(validSummary, lbs1, time1, floatVal1, uint64(intVal1), quantiles), } - validMetrics2 = map[string]pmetric.Metric{ - validIntGauge: getIntGaugeMetric(validIntGauge, lbs2, intVal2, time2), - validDoubleGauge: getDoubleGaugeMetric(validDoubleGauge, lbs2, floatVal2, time2), - validIntSum: getIntSumMetric(validIntSum, lbs2, intVal2, time2), - validSum: getSumMetric(validSum, lbs2, floatVal2, time2), - validHistogram: getHistogramMetric(validHistogram, lbs2, time2, floatVal2, uint64(intVal2), bounds, buckets), - validSummary: getSummaryMetric(validSummary, lbs2, time2, floatVal2, uint64(intVal2), quantiles), - validIntGaugeDirty: getIntGaugeMetric(validIntGaugeDirty, lbs1, intVal1, time1), - unmatchedBoundBucketHist: getHistogramMetric(unmatchedBoundBucketHist, pcommon.NewMap(), 0, 0, 0, - pcommon.NewImmutableFloat64Slice([]float64{0.1, 0.2, 0.3}), - pcommon.NewImmutableUInt64Slice([]uint64{1, 2})), - } empty = "empty" diff --git a/pkg/translator/signalfx/go.mod b/pkg/translator/signalfx/go.mod index 1b6207eea608..ff731b883285 100644 --- a/pkg/translator/signalfx/go.mod +++ b/pkg/translator/signalfx/go.mod @@ -16,7 +16,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/kr/pretty v0.2.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.uber.org/atomic v1.7.0 // indirect + go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect golang.org/x/text v0.3.3 // indirect diff --git a/pkg/translator/signalfx/go.sum b/pkg/translator/signalfx/go.sum index 12e00e25c5ff..9448e6701084 100644 --- a/pkg/translator/signalfx/go.sum +++ b/pkg/translator/signalfx/go.sum @@ -75,8 +75,9 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec go.opentelemetry.io/collector/pdata v0.54.0 h1:oo3HyHwdf4lJmDUN0yrOGKj2tiHIoXDutDd0HKR++/0= go.opentelemetry.io/collector/pdata v0.54.0/go.mod h1:1nSelv/YqGwdHHaIKNW9ZOHSMqicDX7W4/7TjNCm6N8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/processor/spanmetricsprocessor/go.mod b/processor/spanmetricsprocessor/go.mod index bc0dd244fff6..a7e292a57a28 100644 --- a/processor/spanmetricsprocessor/go.mod +++ b/processor/spanmetricsprocessor/go.mod @@ -54,6 +54,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.54.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect @@ -113,6 +114,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ../../pkg/translator/opencensus +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver => ../../receiver/jaegerreceiver diff --git a/receiver/prometheusexecreceiver/go.mod b/receiver/prometheusexecreceiver/go.mod index fd88bdf1db85..0d6a869a2bb9 100644 --- a/receiver/prometheusexecreceiver/go.mod +++ b/receiver/prometheusexecreceiver/go.mod @@ -84,6 +84,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/knadh/koanf v1.4.2 // indirect github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b // indirect + github.com/kr/pretty v0.2.1 // indirect github.com/linode/linodego v1.5.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.12 // indirect @@ -155,6 +156,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourceto replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ../../pkg/translator/opencensus +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver => ../../receiver/prometheusreceiver diff --git a/receiver/prometheusexecreceiver/go.sum b/receiver/prometheusexecreceiver/go.sum index 8840e9a81dcd..15ed66399c12 100644 --- a/receiver/prometheusexecreceiver/go.sum +++ b/receiver/prometheusexecreceiver/go.sum @@ -450,8 +450,9 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= diff --git a/receiver/prometheusreceiver/go.mod b/receiver/prometheusreceiver/go.mod index a1526d9493cf..b9367a08160c 100644 --- a/receiver/prometheusreceiver/go.mod +++ b/receiver/prometheusreceiver/go.mod @@ -114,6 +114,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.54.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite v0.54.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect @@ -185,4 +186,6 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourceto replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ../../pkg/translator/opencensus +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite diff --git a/receiver/prometheusreceiver/go.sum b/receiver/prometheusreceiver/go.sum index aa9bfb2d9d57..9cc8a95cee71 100644 --- a/receiver/prometheusreceiver/go.sum +++ b/receiver/prometheusreceiver/go.sum @@ -461,8 +461,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -550,6 +550,7 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.53.0 h1:jQDPrr2ndsDm3G3fpkD6kyNdr4L5Qc3EgWaAwQ8kNFQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= diff --git a/receiver/simpleprometheusreceiver/go.mod b/receiver/simpleprometheusreceiver/go.mod index a711e6e7a708..cd19158a9ef8 100644 --- a/receiver/simpleprometheusreceiver/go.mod +++ b/receiver/simpleprometheusreceiver/go.mod @@ -155,6 +155,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourceto replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ../../pkg/translator/opencensus +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../../pkg/translator/prometheusremotewrite replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver => ../../receiver/prometheusreceiver diff --git a/testbed/go.mod b/testbed/go.mod index cd9155704658..f147f94cd414 100644 --- a/testbed/go.mod +++ b/testbed/go.mod @@ -148,6 +148,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus v0.54.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx v0.54.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/zipkin v0.54.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -258,6 +259,8 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experiment replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus => ../pkg/translator/opencensus +replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus => ../pkg/translator/prometheus + replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite => ../pkg/translator/prometheusremotewrite replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx => ../pkg/translator/signalfx diff --git a/versions.yaml b/versions.yaml index 48dc143b0333..43f15b0d14dd 100644 --- a/versions.yaml +++ b/versions.yaml @@ -111,6 +111,7 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/stanza - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/opencensus + - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/signalfx - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/winperfcounters