Browse Source

utf8: enable utf-8 support by default

This change causes Prometheus to allow all UTF-8 characters in metric and label names.
This means that names that were previously invalid and would have been previously rejected will be allowed through.

Signed-off-by: Owen Williams <owen.williams@grafana.com>
pull/14705/head
Owen Williams 3 months ago
parent
commit
88bb05c3e8
  1. 5
      cmd/prometheus/main.go
  2. 5
      cmd/prometheus/main_test.go
  3. 5
      cmd/promtool/main.go
  4. 6
      cmd/promtool/main_test.go
  5. 6
      config/config.go
  6. 23
      config/config_test.go
  7. 2
      config/testdata/jobname_dup.bad.yml
  8. 2
      config/testdata/lowercase.bad.yml
  9. 2
      config/testdata/scrape_config_global_validation_mode.yml
  10. 4
      config/testdata/scrape_config_local_global_validation_mode.yml
  11. 2
      config/testdata/scrape_config_local_validation_mode.yml
  12. 14
      docs/configuration/configuration.md
  13. 7
      docs/feature_flags.md
  14. 5
      scrape/manager_test.go
  15. 12
      scrape/scrape.go
  16. 2
      scrape/scrape_test.go

5
cmd/prometheus/main.go

@ -103,6 +103,8 @@ var (
)
func init() {
// This can be removed when the default validation scheme in common is updated.
model.NameValidationScheme = model.UTF8Validation
prometheus.MustRegister(versioncollector.NewCollector(strings.ReplaceAll(appName, "-", "_")))
var err error
@ -237,9 +239,6 @@ func (c *flagConfig) setFeatureListOptions(logger log.Logger) error {
case "promql-delayed-name-removal":
c.promqlEnableDelayedNameRemoval = true
level.Info(logger).Log("msg", "Experimental PromQL delayed name removal enabled.")
case "utf8-names":
model.NameValidationScheme = model.UTF8Validation
level.Info(logger).Log("msg", "Experimental UTF-8 support enabled")
case "":
continue
default:

5
cmd/prometheus/main_test.go

@ -42,6 +42,11 @@ import (
"github.com/prometheus/prometheus/rules"
)
func init() {
// This can be removed when the default validation scheme in common is updated.
model.NameValidationScheme = model.UTF8Validation
}
const startupTime = 10 * time.Second
var (

5
cmd/promtool/main.go

@ -62,6 +62,11 @@ import (
"github.com/prometheus/prometheus/util/documentcli"
)
func init() {
// This can be removed when the default validation scheme in common is updated.
model.NameValidationScheme = model.UTF8Validation
}
const (
successExitCode = 0
failureExitCode = 1

6
cmd/promtool/main_test.go

@ -31,12 +31,18 @@ import (
"testing"
"time"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/rulefmt"
)
func init() {
// This can be removed when the default validation scheme in common is updated.
model.NameValidationScheme = model.UTF8Validation
}
var promtoolPath = os.Args[0]
func TestMain(m *testing.M) {

6
config/config.go

@ -774,10 +774,10 @@ func (c *ScrapeConfig) Validate(globalConfig GlobalConfig) error {
}
switch globalConfig.MetricNameValidationScheme {
case "", LegacyValidationConfig:
case UTF8ValidationConfig:
case LegacyValidationConfig:
case "", UTF8ValidationConfig:
if model.NameValidationScheme != model.UTF8Validation {
return fmt.Errorf("utf8 name validation requested but feature not enabled via --enable-feature=utf8-names")
panic("utf8 name validation requested but model.NameValidationScheme is not set to UTF8")
}
default:
return fmt.Errorf("unknown name validation method specified, must be either 'legacy' or 'utf8', got %s", globalConfig.MetricNameValidationScheme)

23
config/config_test.go

@ -62,6 +62,11 @@ import (
"github.com/prometheus/prometheus/util/testutil"
)
func init() {
// This can be removed when the default validation scheme in common is updated.
model.NameValidationScheme = model.UTF8Validation
}
func mustParseURL(u string) *config.URL {
parsed, err := url.Parse(u)
if err != nil {
@ -2042,6 +2047,10 @@ var expectedErrors = []struct {
}
func TestBadConfigs(t *testing.T) {
model.NameValidationScheme = model.LegacyValidation
defer func() {
model.NameValidationScheme = model.UTF8Validation
}()
for _, ee := range expectedErrors {
_, err := LoadFile("testdata/"+ee.filename, false, false, log.NewNopLogger())
require.Error(t, err, "%s", ee.filename)
@ -2051,6 +2060,10 @@ func TestBadConfigs(t *testing.T) {
}
func TestBadStaticConfigsJSON(t *testing.T) {
model.NameValidationScheme = model.LegacyValidation
defer func() {
model.NameValidationScheme = model.UTF8Validation
}()
content, err := os.ReadFile("testdata/static_config.bad.json")
require.NoError(t, err)
var tg targetgroup.Group
@ -2059,6 +2072,10 @@ func TestBadStaticConfigsJSON(t *testing.T) {
}
func TestBadStaticConfigsYML(t *testing.T) {
model.NameValidationScheme = model.LegacyValidation
defer func() {
model.NameValidationScheme = model.UTF8Validation
}()
content, err := os.ReadFile("testdata/static_config.bad.yml")
require.NoError(t, err)
var tg targetgroup.Group
@ -2323,17 +2340,17 @@ func TestScrapeConfigNameValidationSettings(t *testing.T) {
{
name: "global setting implies local settings",
inputFile: "scrape_config_global_validation_mode",
expectScheme: "utf8",
expectScheme: "legacy",
},
{
name: "local setting",
inputFile: "scrape_config_local_validation_mode",
expectScheme: "utf8",
expectScheme: "legacy",
},
{
name: "local setting overrides global setting",
inputFile: "scrape_config_local_global_validation_mode",
expectScheme: "legacy",
expectScheme: "utf8",
},
}

2
config/testdata/jobname_dup.bad.yml vendored

@ -1,4 +1,6 @@
# Two scrape configs with the same job names are not allowed.
global:
metric_name_validation_scheme: legacy
scrape_configs:
- job_name: prometheus
- job_name: service-x

2
config/testdata/lowercase.bad.yml vendored

@ -1,3 +1,5 @@
global:
metric_name_validation_scheme: legacy
scrape_configs:
- job_name: prometheus
relabel_configs:

2
config/testdata/scrape_config_global_validation_mode.yml vendored

@ -1,4 +1,4 @@
global:
metric_name_validation_scheme: utf8
metric_name_validation_scheme: legacy
scrape_configs:
- job_name: prometheus

4
config/testdata/scrape_config_local_global_validation_mode.yml vendored

@ -1,5 +1,5 @@
global:
metric_name_validation_scheme: utf8
metric_name_validation_scheme: legacy
scrape_configs:
- job_name: prometheus
metric_name_validation_scheme: legacy
metric_name_validation_scheme: utf8

2
config/testdata/scrape_config_local_validation_mode.yml vendored

@ -1,3 +1,3 @@
scrape_configs:
- job_name: prometheus
metric_name_validation_scheme: utf8
metric_name_validation_scheme: legacy

14
docs/configuration/configuration.md

@ -122,9 +122,9 @@ global:
[ keep_dropped_targets: <int> | default = 0 ]
# Specifies the validation scheme for metric and label names. Either blank or
# "legacy" for letters, numbers, colons, and underscores; or "utf8" for full
# UTF-8 support.
[ metric_name_validation_scheme <string> | default "legacy" ]
# "utf8" for for full UTF-8 support, or "legacy" for letters, numbers, colons,
# and underscores.
[ metric_name_validation_scheme <string> | default "utf8" ]
runtime:
# Configure the Go garbage collector GOGC parameter
@ -477,10 +477,10 @@ metric_relabel_configs:
# that will be kept in memory. 0 means no limit.
[ keep_dropped_targets: <int> | default = 0 ]
# Specifies the validation scheme for metric and label names. Either blank or
# "legacy" for letters, numbers, colons, and underscores; or "utf8" for full
# UTF-8 support.
[ metric_name_validation_scheme <string> | default "legacy" ]
# Specifies the validation scheme for metric and label names. Either blank or
# "utf8" for full UTF-8 support, or "legacy" for letters, numbers, colons, and
# underscores.
[ metric_name_validation_scheme <string> | default "utf8" ]
# Limit on total number of positive and negative buckets allowed in a single
# native histogram. The resolution of a histogram with more buckets will be

7
docs/feature_flags.md

@ -250,10 +250,3 @@ When enabled, Prometheus will change the way in which the `__name__` label is re
This allows optionally preserving the `__name__` label via the `label_replace` and `label_join` functions, and helps prevent the "vector cannot contain metrics with the same labelset" error, which can happen when applying a regex-matcher to the `__name__` label.
## UTF-8 Name Support
`--enable-feature=utf8-names`
When enabled, changes the metric and label name validation scheme inside Prometheus to allow the full UTF-8 character set.
By itself, this flag does not enable the request of UTF-8 names via content negotiation.
Users will also have to set `metric_name_validation_scheme` in scrape configs to enable the feature either on the global config or on a per-scrape config basis.

5
scrape/manager_test.go

@ -45,6 +45,11 @@ import (
"github.com/prometheus/prometheus/util/testutil"
)
func init() {
// This can be removed when the default validation scheme in common is updated.
model.NameValidationScheme = model.UTF8Validation
}
func TestPopulateLabels(t *testing.T) {
cases := []struct {
in labels.Labels

12
scrape/scrape.go

@ -305,9 +305,9 @@ func (sp *scrapePool) restartLoops(reuseCache bool) {
mrc = sp.config.MetricRelabelConfigs
)
validationScheme := model.LegacyValidation
if sp.config.MetricNameValidationScheme == config.UTF8ValidationConfig {
validationScheme = model.UTF8Validation
validationScheme := model.UTF8Validation
if sp.config.MetricNameValidationScheme == config.LegacyValidationConfig {
validationScheme = model.LegacyValidation
}
sp.targetMtx.Lock()
@ -460,9 +460,9 @@ func (sp *scrapePool) sync(targets []*Target) {
scrapeClassicHistograms = sp.config.ScrapeClassicHistograms
)
validationScheme := model.LegacyValidation
if sp.config.MetricNameValidationScheme == config.UTF8ValidationConfig {
validationScheme = model.UTF8Validation
validationScheme := model.UTF8Validation
if sp.config.MetricNameValidationScheme == config.LegacyValidationConfig {
validationScheme = model.LegacyValidation
}
sp.targetMtx.Lock()

2
scrape/scrape_test.go

@ -1040,6 +1040,7 @@ func TestScrapeLoopSeriesAdded(t *testing.T) {
}
func TestScrapeLoopFailWithInvalidLabelsAfterRelabel(t *testing.T) {
model.NameValidationScheme = model.LegacyValidation
s := teststorage.New(t)
defer s.Close()
ctx, cancel := context.WithCancel(context.Background())
@ -3768,6 +3769,7 @@ func testNativeHistogramMaxSchemaSet(t *testing.T, minBucketFactor string, expec
// Create a scrape loop with the HTTP server as the target.
configStr := fmt.Sprintf(`
global:
metric_name_validation_scheme: legacy
scrape_interval: 1s
scrape_timeout: 1s
scrape_configs:

Loading…
Cancel
Save