Skip to content

Commit

Permalink
refactor prometheus exporter to slightly improve performance
Browse files Browse the repository at this point in the history
  • Loading branch information
dashpole committed Oct 18, 2022
1 parent 1cbd4c2 commit 6814790
Showing 1 changed file with 23 additions and 81 deletions.
104 changes: 23 additions & 81 deletions exporters/prometheus/exporter.go
Expand Up @@ -69,13 +69,6 @@ func New(opts ...Option) (*Exporter, error) {

// Describe implements prometheus.Collector.
func (c *collector) Describe(ch chan<- *prometheus.Desc) {
metrics, err := c.reader.Collect(context.TODO())
if err != nil {
otel.Handle(err)
}
for _, metricData := range getMetricData(metrics) {
ch <- metricData.description
}
}

// Collect implements prometheus.Collector.
Expand All @@ -87,64 +80,26 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {

// TODO(#3166): convert otel resource to target_info
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#resource-attributes-1
for _, metricData := range getMetricData(metrics) {
if metricData.valueType == prometheus.UntypedValue {
m, err := prometheus.NewConstHistogram(metricData.description, metricData.histogramCount, metricData.histogramSum, metricData.histogramBuckets, metricData.attributeValues...)
if err != nil {
otel.Handle(err)
continue
}
ch <- m
} else {
m, err := prometheus.NewConstMetric(metricData.description, metricData.valueType, metricData.value, metricData.attributeValues...)
if err != nil {
otel.Handle(err)
continue
}
ch <- m
}
}
}

// metricData holds the metadata as well as values for individual data points.
type metricData struct {
// name should include the unit as a suffix (before _total on counters)
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#metric-metadata-1
name string
description *prometheus.Desc
attributeValues []string
valueType prometheus.ValueType
value float64
histogramCount uint64
histogramSum float64
histogramBuckets map[float64]uint64
}

func getMetricData(metrics metricdata.ResourceMetrics) []*metricData {
allMetrics := make([]*metricData, 0)
for _, scopeMetrics := range metrics.ScopeMetrics {
for _, m := range scopeMetrics.Metrics {
switch v := m.Data.(type) {
case metricdata.Histogram:
allMetrics = append(allMetrics, getHistogramMetricData(v, m)...)
addHistogramMetric(ch, v, m)
case metricdata.Sum[int64]:
allMetrics = append(allMetrics, getSumMetricData(v, m)...)
addSumMetric(ch, v, m)
case metricdata.Sum[float64]:
allMetrics = append(allMetrics, getSumMetricData(v, m)...)
addSumMetric(ch, v, m)
case metricdata.Gauge[int64]:
allMetrics = append(allMetrics, getGaugeMetricData(v, m)...)
addGaugeMetric(ch, v, m)
case metricdata.Gauge[float64]:
allMetrics = append(allMetrics, getGaugeMetricData(v, m)...)
addGaugeMetric(ch, v, m)
}
}
}

return allMetrics
}

func getHistogramMetricData(histogram metricdata.Histogram, m metricdata.Metrics) []*metricData {
func addHistogramMetric(ch chan<- prometheus.Metric, histogram metricdata.Histogram, m metricdata.Metrics) {
// TODO(https://github.com/open-telemetry/opentelemetry-go/issues/3163): support exemplars
dataPoints := make([]*metricData, 0, len(histogram.DataPoints))
for _, dp := range histogram.DataPoints {
keys, values := getAttrs(dp.Attributes)
desc := prometheus.NewDesc(sanitizeName(m.Name), m.Description, keys, nil)
Expand All @@ -155,52 +110,39 @@ func getHistogramMetricData(histogram metricdata.Histogram, m metricdata.Metrics
cumulativeCount += dp.BucketCounts[i]
buckets[bound] = cumulativeCount
}
md := &metricData{
name: m.Name,
description: desc,
attributeValues: values,
valueType: prometheus.UntypedValue,
histogramCount: dp.Count,
histogramSum: dp.Sum,
histogramBuckets: buckets,
m, err := prometheus.NewConstHistogram(desc, dp.Count, dp.Sum, buckets, values...)
if err != nil {
otel.Handle(err)
continue
}
dataPoints = append(dataPoints, md)
ch <- m
}
return dataPoints
}

func getSumMetricData[N int64 | float64](sum metricdata.Sum[N], m metricdata.Metrics) []*metricData {
dataPoints := make([]*metricData, 0, len(sum.DataPoints))
func addSumMetric[N int64 | float64](ch chan<- prometheus.Metric, sum metricdata.Sum[N], m metricdata.Metrics) {
for _, dp := range sum.DataPoints {
keys, values := getAttrs(dp.Attributes)
desc := prometheus.NewDesc(sanitizeName(m.Name), m.Description, keys, nil)
md := &metricData{
name: m.Name,
description: desc,
attributeValues: values,
valueType: prometheus.CounterValue,
value: float64(dp.Value),
m, err := prometheus.NewConstMetric(desc, prometheus.CounterValue, float64(dp.Value), values...)
if err != nil {
otel.Handle(err)
continue
}
dataPoints = append(dataPoints, md)
ch <- m
}
return dataPoints
}

func getGaugeMetricData[N int64 | float64](gauge metricdata.Gauge[N], m metricdata.Metrics) []*metricData {
dataPoints := make([]*metricData, 0, len(gauge.DataPoints))
func addGaugeMetric[N int64 | float64](ch chan<- prometheus.Metric, gauge metricdata.Gauge[N], m metricdata.Metrics) {
for _, dp := range gauge.DataPoints {
keys, values := getAttrs(dp.Attributes)
desc := prometheus.NewDesc(sanitizeName(m.Name), m.Description, keys, nil)
md := &metricData{
name: m.Name,
description: desc,
attributeValues: values,
valueType: prometheus.GaugeValue,
value: float64(dp.Value),
m, err := prometheus.NewConstMetric(desc, prometheus.GaugeValue, float64(dp.Value), values...)
if err != nil {
otel.Handle(err)
continue
}
dataPoints = append(dataPoints, md)
ch <- m
}
return dataPoints
}

// getAttrs parses the attribute.Set to two lists of matching Prometheus-style
Expand Down

0 comments on commit 6814790

Please sign in to comment.