Skip to content

Commit e5a689f

Browse files
committed
feat(exporters/prometheus): add explicit option to set naming scheme
1 parent 787518f commit e5a689f

File tree

2 files changed

+38
-22
lines changed

2 files changed

+38
-22
lines changed

exporters/prometheus/config.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type config struct {
2525
disableScopeInfo bool
2626
namespace string
2727
resourceAttributesFilter attribute.Filter
28+
validationScheme model.ValidationScheme
2829
}
2930

3031
var logDeprecatedLegacyScheme = sync.OnceFunc(func() {
@@ -137,9 +138,9 @@ func WithoutScopeInfo() Option {
137138
// WithNamespace configures the Exporter to prefix metric with the given namespace.
138139
// Metadata metrics such as target_info are not prefixed since these
139140
// have special behavior based on their name.
140-
func WithNamespace(ns string) Option {
141+
func WithNamespace(ns string, validationScheme model.ValidationScheme) Option {
141142
return optionFunc(func(cfg config) config {
142-
if model.NameValidationScheme != model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
143+
if validationScheme != model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
143144
logDeprecatedLegacyScheme()
144145
// Only sanitize if prometheus does not support UTF-8.
145146
ns = model.EscapeName(ns, model.NameEscapingScheme)
@@ -165,3 +166,12 @@ func WithResourceAsConstantLabels(resourceFilter attribute.Filter) Option {
165166
return cfg
166167
})
167168
}
169+
170+
// WithValidationScheme configures the Exporter to validate label and metric names
171+
// according to this scheme. Defaults to UTF8Validation.
172+
func WithValidationScheme(scheme model.ValidationScheme) Option {
173+
return optionFunc(func(cfg config) config {
174+
cfg.validationScheme = scheme
175+
return cfg
176+
})
177+
}

exporters/prometheus/exporter.go

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ type collector struct {
9393
targetInfo prometheus.Metric
9494
metricFamilies map[string]*dto.MetricFamily
9595
resourceKeyVals keyVals
96+
validationScheme model.ValidationScheme
9697
}
9798

9899
// prometheus counters MUST have a _total suffix by default:
@@ -117,6 +118,7 @@ func New(opts ...Option) (*Exporter, error) {
117118
metricFamilies: make(map[string]*dto.MetricFamily),
118119
namespace: cfg.namespace,
119120
resourceAttributesFilter: cfg.resourceAttributesFilter,
121+
validationScheme: cfg.validationScheme,
120122
}
121123

122124
if err := cfg.registerer.Register(collector); err != nil {
@@ -164,7 +166,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
164166
defer c.mu.Unlock()
165167

166168
if c.targetInfo == nil && !c.disableTargetInfo {
167-
targetInfo, err := createInfoMetric(targetInfoMetricName, targetInfoDescription, metrics.Resource)
169+
targetInfo, err := createInfoMetric(targetInfoMetricName, targetInfoDescription, metrics.Resource, c.validationScheme)
168170
if err != nil {
169171
// If the target info metric is invalid, disable sending it.
170172
c.disableTargetInfo = true
@@ -195,7 +197,7 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
195197
kv.keys = append(kv.keys, scopeNameLabel, scopeVersionLabel, scopeSchemaLabel)
196198
kv.vals = append(kv.vals, scopeMetrics.Scope.Name, scopeMetrics.Scope.Version, scopeMetrics.Scope.SchemaURL)
197199

198-
attrKeys, attrVals := getAttrs(scopeMetrics.Scope.Attributes)
200+
attrKeys, attrVals := getAttrs(scopeMetrics.Scope.Attributes, c.validationScheme)
199201
for i := range attrKeys {
200202
attrKeys[i] = scopeLabelPrefix + attrKeys[i]
201203
}
@@ -224,21 +226,21 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
224226

225227
switch v := m.Data.(type) {
226228
case metricdata.Histogram[int64]:
227-
addHistogramMetric(ch, v, m, name, kv)
229+
addHistogramMetric(ch, v, m, name, kv, c.validationScheme)
228230
case metricdata.Histogram[float64]:
229-
addHistogramMetric(ch, v, m, name, kv)
231+
addHistogramMetric(ch, v, m, name, kv, c.validationScheme)
230232
case metricdata.ExponentialHistogram[int64]:
231-
addExponentialHistogramMetric(ch, v, m, name, kv)
233+
addExponentialHistogramMetric(ch, v, m, name, kv, c.validationScheme)
232234
case metricdata.ExponentialHistogram[float64]:
233-
addExponentialHistogramMetric(ch, v, m, name, kv)
235+
addExponentialHistogramMetric(ch, v, m, name, kv, c.validationScheme)
234236
case metricdata.Sum[int64]:
235-
addSumMetric(ch, v, m, name, kv)
237+
addSumMetric(ch, v, m, name, kv, c.validationScheme)
236238
case metricdata.Sum[float64]:
237-
addSumMetric(ch, v, m, name, kv)
239+
addSumMetric(ch, v, m, name, kv, c.validationScheme)
238240
case metricdata.Gauge[int64]:
239-
addGaugeMetric(ch, v, m, name, kv)
241+
addGaugeMetric(ch, v, m, name, kv, c.validationScheme)
240242
case metricdata.Gauge[float64]:
241-
addGaugeMetric(ch, v, m, name, kv)
243+
addGaugeMetric(ch, v, m, name, kv, c.validationScheme)
242244
}
243245
}
244246
}
@@ -303,9 +305,10 @@ func addExponentialHistogramMetric[N int64 | float64](
303305
m metricdata.Metrics,
304306
name string,
305307
kv keyVals,
308+
validationScheme model.ValidationScheme,
306309
) {
307310
for _, dp := range histogram.DataPoints {
308-
keys, values := getAttrs(dp.Attributes)
311+
keys, values := getAttrs(dp.Attributes, validationScheme)
309312
keys = append(keys, kv.keys...)
310313
values = append(values, kv.vals...)
311314

@@ -377,9 +380,10 @@ func addHistogramMetric[N int64 | float64](
377380
m metricdata.Metrics,
378381
name string,
379382
kv keyVals,
383+
validationScheme model.ValidationScheme,
380384
) {
381385
for _, dp := range histogram.DataPoints {
382-
keys, values := getAttrs(dp.Attributes)
386+
keys, values := getAttrs(dp.Attributes, validationScheme)
383387
keys = append(keys, kv.keys...)
384388
values = append(values, kv.vals...)
385389

@@ -407,14 +411,15 @@ func addSumMetric[N int64 | float64](
407411
m metricdata.Metrics,
408412
name string,
409413
kv keyVals,
414+
validationScheme model.ValidationScheme,
410415
) {
411416
valueType := prometheus.CounterValue
412417
if !sum.IsMonotonic {
413418
valueType = prometheus.GaugeValue
414419
}
415420

416421
for _, dp := range sum.DataPoints {
417-
keys, values := getAttrs(dp.Attributes)
422+
keys, values := getAttrs(dp.Attributes, validationScheme)
418423
keys = append(keys, kv.keys...)
419424
values = append(values, kv.vals...)
420425

@@ -439,9 +444,10 @@ func addGaugeMetric[N int64 | float64](
439444
m metricdata.Metrics,
440445
name string,
441446
kv keyVals,
447+
validationScheme model.ValidationScheme,
442448
) {
443449
for _, dp := range gauge.DataPoints {
444-
keys, values := getAttrs(dp.Attributes)
450+
keys, values := getAttrs(dp.Attributes, validationScheme)
445451
keys = append(keys, kv.keys...)
446452
values = append(values, kv.vals...)
447453

@@ -457,12 +463,12 @@ func addGaugeMetric[N int64 | float64](
457463

458464
// getAttrs converts the attribute.Set to two lists of matching Prometheus-style
459465
// keys and values.
460-
func getAttrs(attrs attribute.Set) ([]string, []string) {
466+
func getAttrs(attrs attribute.Set, validationScheme model.ValidationScheme) ([]string, []string) {
461467
keys := make([]string, 0, attrs.Len())
462468
values := make([]string, 0, attrs.Len())
463469
itr := attrs.Iter()
464470

465-
if model.NameValidationScheme == model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
471+
if validationScheme == model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
466472
// Do not perform sanitization if prometheus supports UTF-8.
467473
for itr.Next() {
468474
kv := itr.Attribute()
@@ -492,8 +498,8 @@ func getAttrs(attrs attribute.Set) ([]string, []string) {
492498
return keys, values
493499
}
494500

495-
func createInfoMetric(name, description string, res *resource.Resource) (prometheus.Metric, error) {
496-
keys, values := getAttrs(*res.Set())
501+
func createInfoMetric(name, description string, res *resource.Resource, validationScheme model.ValidationScheme) (prometheus.Metric, error) {
502+
keys, values := getAttrs(*res.Set(), validationScheme)
497503
desc := prometheus.NewDesc(name, description, keys, nil)
498504
return prometheus.NewConstMetric(desc, prometheus.GaugeValue, float64(1), values...)
499505
}
@@ -544,7 +550,7 @@ var unitSuffixes = map[string]string{
544550
// getName returns the sanitized name, prefixed with the namespace and suffixed with unit.
545551
func (c *collector) getName(m metricdata.Metrics, typ *dto.MetricType) string {
546552
name := m.Name
547-
if model.NameValidationScheme != model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
553+
if c.validationScheme != model.UTF8Validation { // nolint:staticcheck // We need this check to keep supporting the legacy scheme.
548554
// Only sanitize if prometheus does not support UTF-8.
549555
logDeprecatedLegacyScheme()
550556
name = model.EscapeName(name, model.NameEscapingScheme)
@@ -606,7 +612,7 @@ func (c *collector) createResourceAttributes(res *resource.Resource) {
606612
defer c.mu.Unlock()
607613

608614
resourceAttrs, _ := res.Set().Filter(c.resourceAttributesFilter)
609-
resourceKeys, resourceValues := getAttrs(resourceAttrs)
615+
resourceKeys, resourceValues := getAttrs(resourceAttrs, c.validationScheme)
610616
c.resourceKeyVals = keyVals{keys: resourceKeys, vals: resourceValues}
611617
}
612618

0 commit comments

Comments
 (0)