Skip to content

Commit 2243431

Browse files
authored
Signed-off-by: Ziqi Zhao <[email protected]> (#3469)
prevent conflict metric info
1 parent e97704c commit 2243431

20 files changed

+660
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
5353
- Asynchronous callbacks are only called if they are registered with at least one instrument that does not use drop aggragation. (#3408)
5454
- Do not report empty partial-success responses in the `go.opentelemetry.io/otel/exporters/otlp` exporters. (#3438, #3432)
5555
- Handle partial success responses in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` exporters. (#3162, #3440)
56+
- Prevent duplicate Prometheus description, unit, and type. (#3469)
5657
- Prevents panic when using incorrect `attribute.Value.As[Type]Slice()`. (#3489)
5758

5859
## Removed

exporters/prometheus/exporter.go

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package prometheus // import "go.opentelemetry.io/otel/exporters/prometheus"
1616

1717
import (
1818
"context"
19+
"errors"
1920
"fmt"
2021
"sort"
2122
"strings"
@@ -24,9 +25,12 @@ import (
2425
"unicode/utf8"
2526

2627
"github.com/prometheus/client_golang/prometheus"
28+
dto "github.com/prometheus/client_model/go"
29+
"google.golang.org/protobuf/proto"
2730

2831
"go.opentelemetry.io/otel"
2932
"go.opentelemetry.io/otel/attribute"
33+
"go.opentelemetry.io/otel/internal/global"
3034
"go.opentelemetry.io/otel/metric/unit"
3135
"go.opentelemetry.io/otel/sdk/instrumentation"
3236
"go.opentelemetry.io/otel/sdk/metric"
@@ -62,6 +66,7 @@ type collector struct {
6266
disableScopeInfo bool
6367
createTargetInfoOnce sync.Once
6468
scopeInfos map[instrumentation.Scope]prometheus.Metric
69+
metricFamilies map[string]*dto.MetricFamily
6570
}
6671

6772
// prometheus counters MUST have a _total suffix:
@@ -83,6 +88,7 @@ func New(opts ...Option) (*Exporter, error) {
8388
withoutUnits: cfg.withoutUnits,
8489
disableScopeInfo: cfg.disableScopeInfo,
8590
scopeInfos: make(map[instrumentation.Scope]prometheus.Metric),
91+
metricFamilies: make(map[string]*dto.MetricFamily),
8692
}
8793

8894
if err := cfg.registerer.Register(collector); err != nil {
@@ -149,22 +155,30 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
149155
for _, m := range scopeMetrics.Metrics {
150156
switch v := m.Data.(type) {
151157
case metricdata.Histogram:
152-
addHistogramMetric(ch, v, m, keys, values, c.getName(m))
158+
addHistogramMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
153159
case metricdata.Sum[int64]:
154-
addSumMetric(ch, v, m, keys, values, c.getName(m))
160+
addSumMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
155161
case metricdata.Sum[float64]:
156-
addSumMetric(ch, v, m, keys, values, c.getName(m))
162+
addSumMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
157163
case metricdata.Gauge[int64]:
158-
addGaugeMetric(ch, v, m, keys, values, c.getName(m))
164+
addGaugeMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
159165
case metricdata.Gauge[float64]:
160-
addGaugeMetric(ch, v, m, keys, values, c.getName(m))
166+
addGaugeMetric(ch, v, m, keys, values, c.getName(m), c.metricFamilies)
161167
}
162168
}
163169
}
164170
}
165171

166-
func addHistogramMetric(ch chan<- prometheus.Metric, histogram metricdata.Histogram, m metricdata.Metrics, ks, vs [2]string, name string) {
172+
func addHistogramMetric(ch chan<- prometheus.Metric, histogram metricdata.Histogram, m metricdata.Metrics, ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily) {
167173
// TODO(https://github.com/open-telemetry/opentelemetry-go/issues/3163): support exemplars
174+
drop, help := validateMetrics(name, m.Description, dto.MetricType_HISTOGRAM.Enum(), mfs)
175+
if drop {
176+
return
177+
}
178+
if help != "" {
179+
m.Description = help
180+
}
181+
168182
for _, dp := range histogram.DataPoints {
169183
keys, values := getAttrs(dp.Attributes, ks, vs)
170184

@@ -185,15 +199,26 @@ func addHistogramMetric(ch chan<- prometheus.Metric, histogram metricdata.Histog
185199
}
186200
}
187201

188-
func addSumMetric[N int64 | float64](ch chan<- prometheus.Metric, sum metricdata.Sum[N], m metricdata.Metrics, ks, vs [2]string, name string) {
202+
func addSumMetric[N int64 | float64](ch chan<- prometheus.Metric, sum metricdata.Sum[N], m metricdata.Metrics, ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily) {
189203
valueType := prometheus.CounterValue
204+
metricType := dto.MetricType_COUNTER
190205
if !sum.IsMonotonic {
191206
valueType = prometheus.GaugeValue
207+
metricType = dto.MetricType_GAUGE
192208
}
193209
if sum.IsMonotonic {
194210
// Add _total suffix for counters
195211
name += counterSuffix
196212
}
213+
214+
drop, help := validateMetrics(name, m.Description, metricType.Enum(), mfs)
215+
if drop {
216+
return
217+
}
218+
if help != "" {
219+
m.Description = help
220+
}
221+
197222
for _, dp := range sum.DataPoints {
198223
keys, values := getAttrs(dp.Attributes, ks, vs)
199224

@@ -207,7 +232,15 @@ func addSumMetric[N int64 | float64](ch chan<- prometheus.Metric, sum metricdata
207232
}
208233
}
209234

210-
func addGaugeMetric[N int64 | float64](ch chan<- prometheus.Metric, gauge metricdata.Gauge[N], m metricdata.Metrics, ks, vs [2]string, name string) {
235+
func addGaugeMetric[N int64 | float64](ch chan<- prometheus.Metric, gauge metricdata.Gauge[N], m metricdata.Metrics, ks, vs [2]string, name string, mfs map[string]*dto.MetricFamily) {
236+
drop, help := validateMetrics(name, m.Description, dto.MetricType_GAUGE.Enum(), mfs)
237+
if drop {
238+
return
239+
}
240+
if help != "" {
241+
m.Description = help
242+
}
243+
211244
for _, dp := range gauge.DataPoints {
212245
keys, values := getAttrs(dp.Attributes, ks, vs)
213246

@@ -344,3 +377,36 @@ func sanitizeName(n string) string {
344377

345378
return b.String()
346379
}
380+
381+
func validateMetrics(name, description string, metricType *dto.MetricType, mfs map[string]*dto.MetricFamily) (drop bool, help string) {
382+
emf, exist := mfs[name]
383+
if !exist {
384+
mfs[name] = &dto.MetricFamily{
385+
Name: proto.String(name),
386+
Help: proto.String(description),
387+
Type: metricType,
388+
}
389+
return false, ""
390+
}
391+
if emf.GetType() != *metricType {
392+
global.Error(
393+
errors.New("instrument type conflict"),
394+
"Using existing type definition.",
395+
"instrument", name,
396+
"existing", emf.GetType(),
397+
"dropped", *metricType,
398+
)
399+
return true, ""
400+
}
401+
if emf.GetHelp() != description {
402+
global.Info(
403+
"Instrument description conflict, using existing",
404+
"instrument", name,
405+
"existing", emf.GetHelp(),
406+
"dropped", description,
407+
)
408+
return false, emf.GetHelp()
409+
}
410+
411+
return false, ""
412+
}

0 commit comments

Comments
 (0)