Merge branch 'release-3.7' into krajo/merge-release-3071-to-main

This commit is contained in:
György Krajcsovits 2025-10-17 10:45:55 +02:00
commit ea398c15e8
No known key found for this signature in database
GPG Key ID: 47A8F9CE80FD7C7F
11 changed files with 195 additions and 28 deletions

View File

@ -2,6 +2,10 @@
## main / unreleased
## 3.7.1 / 2025-10-16
* [BUGFIX] OTLP: Prefix `key_` to label name when translating an OTel attribute name starting with a single underscore, and keep multiple consecutive underscores in label name when translating an OTel attribute name. This reverts the breaking changes introduced in 3.7.0. #17344
## 3.7.0 / 2025-10-15
* [CHANGE] Remote-write: the following metrics are deprecated:

View File

@ -1 +1 @@
3.7.0
3.7.1

View File

@ -88,7 +88,11 @@ func (c *PrometheusConverter) createAttributes(resource pcommon.Resource, attrib
c.scratchBuilder.Sort()
sortedLabels := c.scratchBuilder.Labels()
labelNamer := otlptranslator.LabelNamer{UTF8Allowed: settings.AllowUTF8}
labelNamer := otlptranslator.LabelNamer{
UTF8Allowed: settings.AllowUTF8,
UnderscoreLabelSanitization: settings.LabelNameUnderscoreSanitization,
PreserveMultipleUnderscores: settings.LabelNamePreserveMultipleUnderscores,
}
if settings.AllowUTF8 {
// UTF8 is allowed, so conflicts aren't possible.
@ -118,7 +122,7 @@ func (c *PrometheusConverter) createAttributes(resource pcommon.Resource, attrib
}
}
err := settings.PromoteResourceAttributes.addPromotedAttributes(c.builder, resourceAttrs, settings.AllowUTF8)
err := settings.PromoteResourceAttributes.addPromotedAttributes(c.builder, resourceAttrs, labelNamer)
if err != nil {
return labels.EmptyLabels(), err
}

View File

@ -67,15 +67,35 @@ func TestCreateAttributes(t *testing.T) {
attrs.PutStr("metric-attr", "metric value")
attrs.PutStr("metric-attr-other", "metric value other")
// Setup resources with underscores for sanitization tests
resourceAttrsWithUnderscores := map[string]string{
"service.name": "service name",
"service.instance.id": "service ID",
"_private": "private value",
"__reserved__": "reserved value",
"label___multi": "multi value",
}
resourceWithUnderscores := pcommon.NewResource()
for k, v := range resourceAttrsWithUnderscores {
resourceWithUnderscores.Attributes().PutStr(k, v)
}
attrsWithUnderscores := pcommon.NewMap()
attrsWithUnderscores.PutStr("_metric_private", "private metric")
attrsWithUnderscores.PutStr("metric___multi", "multi metric")
testCases := []struct {
name string
scope scope
promoteAllResourceAttributes bool
promoteResourceAttributes []string
promoteScope bool
ignoreResourceAttributes []string
ignoreAttrs []string
expectedLabels labels.Labels
name string
resource pcommon.Resource
attrs pcommon.Map
scope scope
promoteAllResourceAttributes bool
promoteResourceAttributes []string
promoteScope bool
ignoreResourceAttributes []string
ignoreAttrs []string
labelNameUnderscoreLabelSanitization bool
labelNamePreserveMultipleUnderscores bool
expectedLabels labels.Labels
}{
{
name: "Successful conversion without resource attribute promotion and without scope promotion",
@ -251,6 +271,121 @@ func TestCreateAttributes(t *testing.T) {
"otel_scope_attr2", "value2",
),
},
// Label sanitization test cases
{
name: "Underscore sanitization enabled - prepends key_ to labels starting with single _",
resource: resourceWithUnderscores,
attrs: attrsWithUnderscores,
promoteResourceAttributes: []string{"_private"},
labelNameUnderscoreLabelSanitization: true,
labelNamePreserveMultipleUnderscores: true,
expectedLabels: labels.FromStrings(
"__name__", "test_metric",
"instance", "service ID",
"job", "service name",
"key_private", "private value",
"key_metric_private", "private metric",
"metric___multi", "multi metric",
),
},
{
name: "Underscore sanitization disabled - keeps labels with _ as-is",
resource: resourceWithUnderscores,
attrs: attrsWithUnderscores,
promoteResourceAttributes: []string{"_private"},
labelNameUnderscoreLabelSanitization: false,
labelNamePreserveMultipleUnderscores: true,
expectedLabels: labels.FromStrings(
"__name__", "test_metric",
"instance", "service ID",
"job", "service name",
"_private", "private value",
"_metric_private", "private metric",
"metric___multi", "multi metric",
),
},
{
name: "Multiple underscores preserved - keeps consecutive underscores",
resource: resourceWithUnderscores,
attrs: attrsWithUnderscores,
promoteResourceAttributes: []string{"label___multi"},
labelNameUnderscoreLabelSanitization: false,
labelNamePreserveMultipleUnderscores: true,
expectedLabels: labels.FromStrings(
"__name__", "test_metric",
"instance", "service ID",
"job", "service name",
"label___multi", "multi value",
"_metric_private", "private metric",
"metric___multi", "multi metric",
),
},
{
name: "Multiple underscores collapsed - collapses to single underscore",
resource: resourceWithUnderscores,
attrs: attrsWithUnderscores,
promoteResourceAttributes: []string{"label___multi"},
labelNameUnderscoreLabelSanitization: false,
labelNamePreserveMultipleUnderscores: false,
expectedLabels: labels.FromStrings(
"__name__", "test_metric",
"instance", "service ID",
"job", "service name",
"label_multi", "multi value",
"_metric_private", "private metric",
"metric_multi", "multi metric",
),
},
{
name: "Both sanitization options enabled",
resource: resourceWithUnderscores,
attrs: attrsWithUnderscores,
promoteResourceAttributes: []string{"_private", "label___multi"},
labelNameUnderscoreLabelSanitization: true,
labelNamePreserveMultipleUnderscores: true,
expectedLabels: labels.FromStrings(
"__name__", "test_metric",
"instance", "service ID",
"job", "service name",
"key_private", "private value",
"label___multi", "multi value",
"key_metric_private", "private metric",
"metric___multi", "multi metric",
),
},
{
name: "Both sanitization options disabled",
resource: resourceWithUnderscores,
attrs: attrsWithUnderscores,
promoteResourceAttributes: []string{"_private", "label___multi"},
labelNameUnderscoreLabelSanitization: false,
labelNamePreserveMultipleUnderscores: false,
expectedLabels: labels.FromStrings(
"__name__", "test_metric",
"instance", "service ID",
"job", "service name",
"_private", "private value",
"label_multi", "multi value",
"_metric_private", "private metric",
"metric_multi", "multi metric",
),
},
{
name: "Reserved labels (starting with __) are never modified",
resource: resourceWithUnderscores,
attrs: attrsWithUnderscores,
promoteResourceAttributes: []string{"__reserved__"},
labelNameUnderscoreLabelSanitization: true,
labelNamePreserveMultipleUnderscores: false,
expectedLabels: labels.FromStrings(
"__name__", "test_metric",
"instance", "service ID",
"job", "service name",
"__reserved__", "reserved value",
"key_metric_private", "private metric",
"metric_multi", "multi metric",
),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
@ -261,9 +396,24 @@ func TestCreateAttributes(t *testing.T) {
PromoteResourceAttributes: tc.promoteResourceAttributes,
IgnoreResourceAttributes: tc.ignoreResourceAttributes,
}),
PromoteScopeMetadata: tc.promoteScope,
PromoteScopeMetadata: tc.promoteScope,
LabelNameUnderscoreSanitization: tc.labelNameUnderscoreLabelSanitization,
LabelNamePreserveMultipleUnderscores: tc.labelNamePreserveMultipleUnderscores,
}
lbls, err := c.createAttributes(resource, attrs, tc.scope, settings, tc.ignoreAttrs, false, Metadata{}, model.MetricNameLabel, "test_metric")
// Use test case specific resource/attrs if provided, otherwise use defaults
// Check if tc.resource is initialized (non-zero) by trying to get its attributes
testResource := resource
testAttrs := attrs
// For pcommon types, we can check if they're non-zero by seeing if they have attributes
// Since zero-initialized Resource is not valid, we use a simple heuristic:
// if the struct has been explicitly set in the test case, use it
if tc.resource != (pcommon.Resource{}) {
testResource = tc.resource
}
if tc.attrs != (pcommon.Map{}) {
testAttrs = tc.attrs
}
lbls, err := c.createAttributes(testResource, testAttrs, tc.scope, settings, tc.ignoreAttrs, false, Metadata{}, model.MetricNameLabel, "test_metric")
require.NoError(t, err)
testutil.RequireEqual(t, lbls, tc.expectedLabels)

View File

@ -54,6 +54,12 @@ type Settings struct {
// PromoteScopeMetadata controls whether to promote OTel scope metadata to metric labels.
PromoteScopeMetadata bool
EnableTypeAndUnitLabels bool
// LabelNameUnderscoreSanitization controls whether to enable prepending of 'key' to labels
// starting with '_'. Reserved labels starting with `__` are not modified.
LabelNameUnderscoreSanitization bool
// LabelNamePreserveMultipleUnderscores enables preserving of multiple
// consecutive underscores in label names when AllowUTF8 is false.
LabelNamePreserveMultipleUnderscores bool
}
// PrometheusConverter converts from OTel write format to Prometheus remote write format.
@ -305,12 +311,11 @@ func NewPromoteResourceAttributes(otlpCfg config.OTLPConfig) *PromoteResourceAtt
}
// addPromotedAttributes adds labels for promoted resourceAttributes to the builder.
func (s *PromoteResourceAttributes) addPromotedAttributes(builder *labels.Builder, resourceAttributes pcommon.Map, allowUTF8 bool) error {
func (s *PromoteResourceAttributes) addPromotedAttributes(builder *labels.Builder, resourceAttributes pcommon.Map, labelNamer otlptranslator.LabelNamer) error {
if s == nil {
return nil
}
labelNamer := otlptranslator.LabelNamer{UTF8Allowed: allowUTF8}
if s.promoteAll {
var err error
resourceAttributes.Range(func(name string, value pcommon.Value) bool {

View File

@ -680,6 +680,10 @@ func (rw *rwExporter) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) er
AllowDeltaTemporality: rw.allowDeltaTemporality,
LookbackDelta: rw.lookbackDelta,
EnableTypeAndUnitLabels: rw.enableTypeAndUnitLabels,
// For backwards compatibility.
LabelNameUnderscoreSanitization: true,
// For backwards compatibility.
LabelNamePreserveMultipleUnderscores: true,
})
defer func() {

View File

@ -1,7 +1,7 @@
{
"name": "@prometheus-io/mantine-ui",
"private": true,
"version": "0.307.0",
"version": "0.307.1",
"type": "module",
"scripts": {
"start": "vite",
@ -28,7 +28,7 @@
"@microsoft/fetch-event-source": "^2.0.1",
"@nexucis/fuzzy": "^0.5.1",
"@nexucis/kvsearch": "^0.9.1",
"@prometheus-io/codemirror-promql": "0.307.0",
"@prometheus-io/codemirror-promql": "0.307.1",
"@reduxjs/toolkit": "^2.9.0",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.2",

View File

@ -1,6 +1,6 @@
{
"name": "@prometheus-io/codemirror-promql",
"version": "0.307.0",
"version": "0.307.1",
"description": "a CodeMirror mode for the PromQL language",
"types": "dist/esm/index.d.ts",
"module": "dist/esm/index.js",
@ -29,7 +29,7 @@
},
"homepage": "https://github.com/prometheus/prometheus/blob/main/web/ui/module/codemirror-promql/README.md",
"dependencies": {
"@prometheus-io/lezer-promql": "0.307.0",
"@prometheus-io/lezer-promql": "0.307.1",
"lru-cache": "^11.2.2"
},
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@prometheus-io/lezer-promql",
"version": "0.307.0",
"version": "0.307.1",
"description": "lezer-based PromQL grammar",
"main": "dist/index.cjs",
"type": "module",

View File

@ -1,12 +1,12 @@
{
"name": "prometheus-io",
"version": "0.307.0",
"version": "0.307.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "prometheus-io",
"version": "0.307.0",
"version": "0.307.1",
"workspaces": [
"mantine-ui",
"module/*"
@ -24,7 +24,7 @@
},
"mantine-ui": {
"name": "@prometheus-io/mantine-ui",
"version": "0.307.0",
"version": "0.307.1",
"dependencies": {
"@codemirror/autocomplete": "^6.19.0",
"@codemirror/language": "^6.11.3",
@ -42,7 +42,7 @@
"@microsoft/fetch-event-source": "^2.0.1",
"@nexucis/fuzzy": "^0.5.1",
"@nexucis/kvsearch": "^0.9.1",
"@prometheus-io/codemirror-promql": "0.307.0",
"@prometheus-io/codemirror-promql": "0.307.1",
"@reduxjs/toolkit": "^2.9.0",
"@tabler/icons-react": "^3.35.0",
"@tanstack/react-query": "^5.90.2",
@ -87,10 +87,10 @@
},
"module/codemirror-promql": {
"name": "@prometheus-io/codemirror-promql",
"version": "0.307.0",
"version": "0.307.1",
"license": "Apache-2.0",
"dependencies": {
"@prometheus-io/lezer-promql": "0.307.0",
"@prometheus-io/lezer-promql": "0.307.1",
"lru-cache": "^11.2.2"
},
"devDependencies": {
@ -120,7 +120,7 @@
},
"module/lezer-promql": {
"name": "@prometheus-io/lezer-promql",
"version": "0.307.0",
"version": "0.307.1",
"license": "Apache-2.0",
"devDependencies": {
"@lezer/generator": "^1.8.0",

View File

@ -1,7 +1,7 @@
{
"name": "prometheus-io",
"description": "Monorepo for the Prometheus UI",
"version": "0.307.0",
"version": "0.307.1",
"private": true,
"scripts": {
"build": "bash build_ui.sh --all",