summaryrefslogtreecommitdiff
path: root/vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go')
-rw-r--r--vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go404
1 files changed, 0 insertions, 404 deletions
diff --git a/vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go b/vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go
deleted file mode 100644
index 4212198f0..000000000
--- a/vendor/go.opentelemetry.io/contrib/bridges/prometheus/producer.go
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright The OpenTelemetry Authors
-// SPDX-License-Identifier: Apache-2.0
-
-package prometheus // import "go.opentelemetry.io/contrib/bridges/prometheus"
-
-import (
- "context"
- "errors"
- "fmt"
- "math"
- "strings"
- "time"
-
- "github.com/prometheus/client_golang/prometheus"
- dto "github.com/prometheus/client_model/go"
- "go.opentelemetry.io/otel"
- "go.opentelemetry.io/otel/attribute"
- "go.opentelemetry.io/otel/sdk/instrumentation"
- "go.opentelemetry.io/otel/sdk/metric"
- "go.opentelemetry.io/otel/sdk/metric/metricdata"
-)
-
-const (
- scopeName = "go.opentelemetry.io/contrib/bridges/prometheus"
- traceIDLabel = "trace_id"
- spanIDLabel = "span_id"
-)
-
-var (
- errUnsupportedType = errors.New("unsupported metric type")
- processStartTime = time.Now()
-)
-
-type producer struct {
- gatherers prometheus.Gatherers
-}
-
-// NewMetricProducer returns a metric.Producer that fetches metrics from
-// Prometheus. This can be used to allow Prometheus instrumentation to be
-// added to an OpenTelemetry export pipeline.
-func NewMetricProducer(opts ...Option) metric.Producer {
- cfg := newConfig(opts...)
- return &producer{
- gatherers: cfg.gatherers,
- }
-}
-
-func (p *producer) Produce(context.Context) ([]metricdata.ScopeMetrics, error) {
- now := time.Now()
- var errs multierr
- otelMetrics := make([]metricdata.Metrics, 0)
- for _, gatherer := range p.gatherers {
- promMetrics, err := gatherer.Gather()
- if err != nil {
- errs = append(errs, err)
- continue
- }
- m, err := convertPrometheusMetricsInto(promMetrics, now)
- otelMetrics = append(otelMetrics, m...)
- if err != nil {
- errs = append(errs, err)
- }
- }
- if errs.errOrNil() != nil {
- otel.Handle(errs.errOrNil())
- }
- if len(otelMetrics) == 0 {
- return nil, nil
- }
- return []metricdata.ScopeMetrics{{
- Scope: instrumentation.Scope{
- Name: scopeName,
- },
- Metrics: otelMetrics,
- }}, nil
-}
-
-func convertPrometheusMetricsInto(promMetrics []*dto.MetricFamily, now time.Time) ([]metricdata.Metrics, error) {
- var errs multierr
- otelMetrics := make([]metricdata.Metrics, 0)
- for _, pm := range promMetrics {
- if len(pm.GetMetric()) == 0 {
- // This shouldn't ever happen
- continue
- }
- newMetric := metricdata.Metrics{
- Name: pm.GetName(),
- Description: pm.GetHelp(),
- }
- switch pm.GetType() {
- case dto.MetricType_GAUGE:
- newMetric.Data = convertGauge(pm.GetMetric(), now)
- case dto.MetricType_COUNTER:
- newMetric.Data = convertCounter(pm.GetMetric(), now)
- case dto.MetricType_SUMMARY:
- newMetric.Data = convertSummary(pm.GetMetric(), now)
- case dto.MetricType_HISTOGRAM:
- if isExponentialHistogram(pm.GetMetric()[0].GetHistogram()) {
- newMetric.Data = convertExponentialHistogram(pm.GetMetric(), now)
- } else {
- newMetric.Data = convertHistogram(pm.GetMetric(), now)
- }
- default:
- // MetricType_GAUGE_HISTOGRAM, MetricType_UNTYPED
- errs = append(errs, fmt.Errorf("%w: %v for metric %v", errUnsupportedType, pm.GetType(), pm.GetName()))
- continue
- }
- otelMetrics = append(otelMetrics, newMetric)
- }
- return otelMetrics, errs.errOrNil()
-}
-
-func isExponentialHistogram(hist *dto.Histogram) bool {
- // The prometheus go client ensures at least one of these is non-zero
- // so it can be distinguished from a fixed-bucket histogram.
- // https://github.com/prometheus/client_golang/blob/7ac90362b02729a65109b33d172bafb65d7dab50/prometheus/histogram.go#L818
- return hist.GetZeroThreshold() > 0 ||
- hist.GetZeroCount() > 0 ||
- len(hist.GetPositiveSpan()) > 0 ||
- len(hist.GetNegativeSpan()) > 0
-}
-
-func convertGauge(metrics []*dto.Metric, now time.Time) metricdata.Gauge[float64] {
- otelGauge := metricdata.Gauge[float64]{
- DataPoints: make([]metricdata.DataPoint[float64], len(metrics)),
- }
- for i, m := range metrics {
- dp := metricdata.DataPoint[float64]{
- Attributes: convertLabels(m.GetLabel()),
- Time: now,
- Value: m.GetGauge().GetValue(),
- }
- if m.GetTimestampMs() != 0 {
- dp.Time = time.UnixMilli(m.GetTimestampMs())
- }
- otelGauge.DataPoints[i] = dp
- }
- return otelGauge
-}
-
-func convertCounter(metrics []*dto.Metric, now time.Time) metricdata.Sum[float64] {
- otelCounter := metricdata.Sum[float64]{
- DataPoints: make([]metricdata.DataPoint[float64], len(metrics)),
- Temporality: metricdata.CumulativeTemporality,
- IsMonotonic: true,
- }
- for i, m := range metrics {
- dp := metricdata.DataPoint[float64]{
- Attributes: convertLabels(m.GetLabel()),
- StartTime: processStartTime,
- Time: now,
- Value: m.GetCounter().GetValue(),
- }
- if ex := m.GetCounter().GetExemplar(); ex != nil {
- dp.Exemplars = []metricdata.Exemplar[float64]{convertExemplar(ex)}
- }
- createdTs := m.GetCounter().GetCreatedTimestamp()
- if createdTs.IsValid() {
- dp.StartTime = createdTs.AsTime()
- }
- if m.GetTimestampMs() != 0 {
- dp.Time = time.UnixMilli(m.GetTimestampMs())
- }
- otelCounter.DataPoints[i] = dp
- }
- return otelCounter
-}
-
-func convertExponentialHistogram(metrics []*dto.Metric, now time.Time) metricdata.ExponentialHistogram[float64] {
- otelExpHistogram := metricdata.ExponentialHistogram[float64]{
- DataPoints: make([]metricdata.ExponentialHistogramDataPoint[float64], len(metrics)),
- Temporality: metricdata.CumulativeTemporality,
- }
- for i, m := range metrics {
- dp := metricdata.ExponentialHistogramDataPoint[float64]{
- Attributes: convertLabels(m.GetLabel()),
- StartTime: processStartTime,
- Time: now,
- Count: m.GetHistogram().GetSampleCount(),
- Sum: m.GetHistogram().GetSampleSum(),
- Scale: m.GetHistogram().GetSchema(),
- ZeroCount: m.GetHistogram().GetZeroCount(),
- ZeroThreshold: m.GetHistogram().GetZeroThreshold(),
- PositiveBucket: convertExponentialBuckets(
- m.GetHistogram().GetPositiveSpan(),
- m.GetHistogram().GetPositiveDelta(),
- ),
- NegativeBucket: convertExponentialBuckets(
- m.GetHistogram().GetNegativeSpan(),
- m.GetHistogram().GetNegativeDelta(),
- ),
- // TODO: Support exemplars
- }
- createdTs := m.GetHistogram().GetCreatedTimestamp()
- if createdTs.IsValid() {
- dp.StartTime = createdTs.AsTime()
- }
- if t := m.GetTimestampMs(); t != 0 {
- dp.Time = time.UnixMilli(t)
- }
- otelExpHistogram.DataPoints[i] = dp
- }
- return otelExpHistogram
-}
-
-func convertExponentialBuckets(bucketSpans []*dto.BucketSpan, deltas []int64) metricdata.ExponentialBucket {
- if len(bucketSpans) == 0 {
- return metricdata.ExponentialBucket{}
- }
- // Prometheus Native Histograms buckets are indexed by upper boundary
- // while Exponential Histograms are indexed by lower boundary, the result
- // being that the Offset fields are different-by-one.
- initialOffset := bucketSpans[0].GetOffset() - 1
- // We will have one bucket count for each delta, and zeros for the offsets
- // after the initial offset.
- lenCounts := len(deltas)
- for i, bs := range bucketSpans {
- if i != 0 {
- lenCounts += int(bs.GetOffset())
- }
- }
- counts := make([]uint64, lenCounts)
- deltaIndex := 0
- countIndex := int32(0)
- count := int64(0)
- for i, bs := range bucketSpans {
- // Do not insert zeroes if this is the first bucketSpan, since those
- // zeroes are accounted for in the Offset field.
- if i != 0 {
- // Increase the count index by the Offset to insert Offset zeroes
- countIndex += bs.GetOffset()
- }
- for range bs.GetLength() {
- // Convert deltas to the cumulative number of observations
- count += deltas[deltaIndex]
- deltaIndex++
- // count should always be positive after accounting for deltas
- if count > 0 {
- counts[countIndex] = uint64(count)
- }
- countIndex++
- }
- }
- return metricdata.ExponentialBucket{
- Offset: initialOffset,
- Counts: counts,
- }
-}
-
-func convertHistogram(metrics []*dto.Metric, now time.Time) metricdata.Histogram[float64] {
- otelHistogram := metricdata.Histogram[float64]{
- DataPoints: make([]metricdata.HistogramDataPoint[float64], len(metrics)),
- Temporality: metricdata.CumulativeTemporality,
- }
- for i, m := range metrics {
- bounds, bucketCounts, exemplars := convertBuckets(m.GetHistogram().GetBucket(), m.GetHistogram().GetSampleCount())
- dp := metricdata.HistogramDataPoint[float64]{
- Attributes: convertLabels(m.GetLabel()),
- StartTime: processStartTime,
- Time: now,
- Count: m.GetHistogram().GetSampleCount(),
- Sum: m.GetHistogram().GetSampleSum(),
- Bounds: bounds,
- BucketCounts: bucketCounts,
- Exemplars: exemplars,
- }
- createdTs := m.GetHistogram().GetCreatedTimestamp()
- if createdTs.IsValid() {
- dp.StartTime = createdTs.AsTime()
- }
- if m.GetTimestampMs() != 0 {
- dp.Time = time.UnixMilli(m.GetTimestampMs())
- }
- otelHistogram.DataPoints[i] = dp
- }
- return otelHistogram
-}
-
-func convertBuckets(buckets []*dto.Bucket, sampleCount uint64) ([]float64, []uint64, []metricdata.Exemplar[float64]) {
- if len(buckets) == 0 {
- // This should never happen
- return nil, nil, nil
- }
- // buckets will only include the +Inf bucket if there is an exemplar for it
- // https://github.com/prometheus/client_golang/blob/d038ab96c0c7b9cd217a39072febd610bcdf1fd8/prometheus/metric.go#L189
- // we need to handle the case where it is present, or where it is missing.
- hasInf := math.IsInf(buckets[len(buckets)-1].GetUpperBound(), +1)
- var bounds []float64
- var bucketCounts []uint64
- if hasInf {
- bounds = make([]float64, len(buckets)-1)
- bucketCounts = make([]uint64, len(buckets))
- } else {
- bounds = make([]float64, len(buckets))
- bucketCounts = make([]uint64, len(buckets)+1)
- }
- exemplars := make([]metricdata.Exemplar[float64], 0)
- var previousCount uint64
- for i, bucket := range buckets {
- // The last bound may be the +Inf bucket, which is implied in OTel, but
- // is explicit in Prometheus. Skip the last boundary if it is the +Inf
- // bound.
- if bound := bucket.GetUpperBound(); !math.IsInf(bound, +1) {
- bounds[i] = bound
- }
- previousCount, bucketCounts[i] = bucket.GetCumulativeCount(), bucket.GetCumulativeCount()-previousCount
- if ex := bucket.GetExemplar(); ex != nil {
- exemplars = append(exemplars, convertExemplar(ex))
- }
- }
- if !hasInf {
- // The Inf bucket was missing, so set the last bucket counts to the
- // overall count
- bucketCounts[len(bucketCounts)-1] = sampleCount - previousCount
- }
- return bounds, bucketCounts, exemplars
-}
-
-func convertSummary(metrics []*dto.Metric, now time.Time) metricdata.Summary {
- otelSummary := metricdata.Summary{
- DataPoints: make([]metricdata.SummaryDataPoint, len(metrics)),
- }
- for i, m := range metrics {
- dp := metricdata.SummaryDataPoint{
- Attributes: convertLabels(m.GetLabel()),
- StartTime: processStartTime,
- Time: now,
- Count: m.GetSummary().GetSampleCount(),
- Sum: m.GetSummary().GetSampleSum(),
- QuantileValues: convertQuantiles(m.GetSummary().GetQuantile()),
- }
- createdTs := m.GetSummary().GetCreatedTimestamp()
- if createdTs.IsValid() {
- dp.StartTime = createdTs.AsTime()
- }
- if t := m.GetTimestampMs(); t != 0 {
- dp.Time = time.UnixMilli(t)
- }
- otelSummary.DataPoints[i] = dp
- }
- return otelSummary
-}
-
-func convertQuantiles(quantiles []*dto.Quantile) []metricdata.QuantileValue {
- otelQuantiles := make([]metricdata.QuantileValue, len(quantiles))
- for i, quantile := range quantiles {
- dp := metricdata.QuantileValue{
- Quantile: quantile.GetQuantile(),
- Value: quantile.GetValue(),
- }
- otelQuantiles[i] = dp
- }
- return otelQuantiles
-}
-
-func convertLabels(labels []*dto.LabelPair) attribute.Set {
- kvs := make([]attribute.KeyValue, len(labels))
- for i, l := range labels {
- kvs[i] = attribute.String(l.GetName(), l.GetValue())
- }
- return attribute.NewSet(kvs...)
-}
-
-func convertExemplar(exemplar *dto.Exemplar) metricdata.Exemplar[float64] {
- attrs := make([]attribute.KeyValue, 0)
- var traceID, spanID []byte
- // find the trace ID and span ID in attributes, if it exists
- for _, label := range exemplar.GetLabel() {
- switch label.GetName() {
- case traceIDLabel:
- traceID = []byte(label.GetValue())
- case spanIDLabel:
- spanID = []byte(label.GetValue())
- default:
- attrs = append(attrs, attribute.String(label.GetName(), label.GetValue()))
- }
- }
- return metricdata.Exemplar[float64]{
- Value: exemplar.GetValue(),
- Time: exemplar.GetTimestamp().AsTime(),
- TraceID: traceID,
- SpanID: spanID,
- FilteredAttributes: attrs,
- }
-}
-
-type multierr []error
-
-func (e multierr) errOrNil() error {
- if len(e) == 0 {
- return nil
- } else if len(e) == 1 {
- return e[0]
- }
- return e
-}
-
-func (e multierr) Error() string {
- es := make([]string, len(e))
- for i, err := range e {
- es[i] = fmt.Sprintf("* %s", err)
- }
- return strings.Join(es, "\n\t")
-}