From 06bb8682fbff79401c9586d0b2782a3305bf3e23 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Sat, 22 Jul 2023 14:47:11 +0000 Subject: [PATCH] Add prometheus bridge --- .github/dependabot.yml | 18 +++ CHANGELOG.md | 1 + bridge/prometheus/config.go | 58 +++++++ bridge/prometheus/config_test.go | 60 ++++++++ bridge/prometheus/doc.go | 16 ++ bridge/prometheus/go.mod | 40 +++++ bridge/prometheus/go.sum | 46 ++++++ bridge/prometheus/producer.go | 231 ++++++++++++++++++++++++++++ bridge/prometheus/producer_test.go | 238 +++++++++++++++++++++++++++++ example/prometheusbridge/go.mod | 42 +++++ example/prometheusbridge/go.sum | 37 +++++ example/prometheusbridge/main.go | 60 ++++++++ go.mod | 14 ++ go.sum | 39 ++++- versions.yaml | 2 + 15 files changed, 901 insertions(+), 1 deletion(-) create mode 100644 bridge/prometheus/config.go create mode 100644 bridge/prometheus/config_test.go create mode 100644 bridge/prometheus/doc.go create mode 100644 bridge/prometheus/go.mod create mode 100644 bridge/prometheus/go.sum create mode 100644 bridge/prometheus/producer.go create mode 100644 bridge/prometheus/producer_test.go create mode 100644 example/prometheusbridge/go.mod create mode 100644 example/prometheusbridge/go.sum create mode 100644 example/prometheusbridge/main.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 08b13a0543f2..98085e978b25 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -64,6 +64,15 @@ updates: schedule: interval: weekly day: sunday + - package-ecosystem: gomod + directory: /bridge/prometheus + labels: + - dependencies + - go + - Skip Changelog + schedule: + interval: weekly + day: sunday - package-ecosystem: gomod directory: /example/fib labels: @@ -127,6 +136,15 @@ updates: schedule: interval: weekly day: sunday + - package-ecosystem: gomod + directory: /example/prometheusbridge + labels: + - dependencies + - go + - Skip Changelog + schedule: + interval: weekly + day: sunday - package-ecosystem: gomod directory: /example/view labels: diff --git a/CHANGELOG.md b/CHANGELOG.md index b0924627aff7..bc50482bdb1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - OTLP Metrics Exporter now supports the `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` environment variable. (#4287) - Add `WithoutCounterSuffixes` option in `go.opentelemetry.io/otel/exporters/prometheus` to disable addition of `_total` suffixes. (#4306) - Add info and debug logging to the metric SDK. (#4315) +- Add `go.opentelemetry.io/otel/bridge/prometheus` which can bridge metrics defined using the prometheus client library to OpenTelemetry. (#4351) ### Changed diff --git a/bridge/prometheus/config.go b/bridge/prometheus/config.go new file mode 100644 index 000000000000..7907be8322db --- /dev/null +++ b/bridge/prometheus/config.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus // import "go.opentelemetry.io/otel/exporters/prometheus" + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// config contains options for the exporter. +type config struct { + gatherers []prometheus.Gatherer +} + +// newConfig creates a validated config configured with options. +func newConfig(opts ...Option) config { + cfg := config{} + for _, opt := range opts { + cfg = opt.apply(cfg) + } + + if len(cfg.gatherers) == 0 { + cfg.gatherers = []prometheus.Gatherer{prometheus.DefaultGatherer} + } + + return cfg +} + +// Option sets exporter option values. +type Option interface { + apply(config) config +} + +type optionFunc func(config) config + +func (fn optionFunc) apply(cfg config) config { + return fn(cfg) +} + +// WithGatherer configures which prometheus Gatherer the Bridge will gather +// from. If no registerer is used the prometheus DefaultGatherer is used. +func WithGatherer(gatherer prometheus.Gatherer) Option { + return optionFunc(func(cfg config) config { + cfg.gatherers = append(cfg.gatherers, gatherer) + return cfg + }) +} diff --git a/bridge/prometheus/config_test.go b/bridge/prometheus/config_test.go new file mode 100644 index 000000000000..7d8e9e4fa0b4 --- /dev/null +++ b/bridge/prometheus/config_test.go @@ -0,0 +1,60 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus // import "go.opentelemetry.io/otel/bridge/prometheus" + +import ( + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/assert" +) + +func TestNewConfig(t *testing.T) { + otherRegistry := prometheus.NewRegistry() + + testCases := []struct { + name string + options []Option + wantConfig config + }{ + { + name: "Default", + options: nil, + wantConfig: config{ + gatherers: []prometheus.Gatherer{prometheus.DefaultGatherer}, + }, + }, + { + name: "With a different gatherer", + options: []Option{WithGatherer(otherRegistry)}, + wantConfig: config{ + gatherers: []prometheus.Gatherer{otherRegistry}, + }, + }, + { + name: "Multiple gatherers", + options: []Option{WithGatherer(otherRegistry), WithGatherer(prometheus.DefaultGatherer)}, + wantConfig: config{ + gatherers: []prometheus.Gatherer{otherRegistry, prometheus.DefaultGatherer}, + }, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + cfg := newConfig(tt.options...) + assert.Equal(t, tt.wantConfig, cfg) + }) + } +} diff --git a/bridge/prometheus/doc.go b/bridge/prometheus/doc.go new file mode 100644 index 000000000000..bca1d5e8a001 --- /dev/null +++ b/bridge/prometheus/doc.go @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package prometheus provides a bridge from Prometheus to OpenTelemetry. +package prometheus // import "go.opentelemetry.io/otel/bridge/prometheus" diff --git a/bridge/prometheus/go.mod b/bridge/prometheus/go.mod new file mode 100644 index 000000000000..730ce01f87a9 --- /dev/null +++ b/bridge/prometheus/go.mod @@ -0,0 +1,40 @@ +module go.opentelemetry.io/otel/exporters/prometheus + +go 1.19 + +require ( + github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_model v0.4.1-0.20230719122841-95a0733b38a0 + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/otel v1.16.0 + go.opentelemetry.io/otel/sdk v1.16.0 + go.opentelemetry.io/otel/sdk/metric v0.39.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.10.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace go.opentelemetry.io/otel => ../.. + +replace go.opentelemetry.io/otel/sdk => ../../sdk + +replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric + +replace go.opentelemetry.io/otel/trace => ../../trace + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/bridge/prometheus/go.sum b/bridge/prometheus/go.sum new file mode 100644 index 000000000000..16a6705f8b40 --- /dev/null +++ b/bridge/prometheus/go.sum @@ -0,0 +1,46 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.1-0.20230719122841-95a0733b38a0 h1:0+/NUBQSANv2NUdj4G9gW+2gy10v89mZB5WMa9/fWPs= +github.com/prometheus/client_model v0.4.1-0.20230719122841-95a0733b38a0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/bridge/prometheus/producer.go b/bridge/prometheus/producer.go new file mode 100644 index 000000000000..071ba3cc2d0c --- /dev/null +++ b/bridge/prometheus/producer.go @@ -0,0 +1,231 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus // import "go.opentelemetry.io/otel/bridge/prometheus" + +import ( + "context" + "errors" + "fmt" + "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/otel/bridge/prometheus" + traceIDLabel = "trace_id" + spanIDLabel = "span_id" +) + +var errUnsupportedType = errors.New("unsupported metric type") + +type producer struct { + gatherers []prometheus.Gatherer +} + +// 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) { + 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) + 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) ([]metricdata.Metrics, error) { + var errs multierr + otelMetrics := make([]metricdata.Metrics, 0) + for _, pm := range promMetrics { + newMetric := metricdata.Metrics{ + Name: pm.GetName(), + Description: pm.GetHelp(), + } + switch pm.GetType() { + case dto.MetricType_GAUGE: + newMetric.Data = convertGauge(pm.GetMetric()) + case dto.MetricType_COUNTER: + newMetric.Data = convertCounter(pm.GetMetric()) + case dto.MetricType_HISTOGRAM: + newMetric.Data = convertHistogram(pm.GetMetric()) + default: + // MetricType_GAUGE_HISTOGRAM, MetricType_SUMMARY, 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 convertGauge(metrics []*dto.Metric) metricdata.Gauge[float64] { + otelGauge := metricdata.Gauge[float64]{ + DataPoints: make([]metricdata.DataPoint[float64], len(metrics)), + } + for i, m := range metrics { + otelGauge.DataPoints[i] = metricdata.DataPoint[float64]{ + Attributes: convertLabels(m.GetLabel()), + Time: time.UnixMilli(m.GetTimestampMs()), + Value: m.GetGauge().GetValue(), + } + } + return otelGauge +} + +func convertCounter(metrics []*dto.Metric) metricdata.Sum[float64] { + otelCounter := metricdata.Sum[float64]{ + DataPoints: make([]metricdata.DataPoint[float64], len(metrics)), + Temporality: metricdata.CumulativeTemporality, + IsMonotonic: true, + } + for i, m := range metrics { + otelCounter.DataPoints[i] = metricdata.DataPoint[float64]{ + Attributes: convertLabels(m.GetLabel()), + StartTime: m.GetCounter().GetCreatedTimestamp().AsTime(), + Time: time.UnixMilli(m.GetTimestampMs()), + Value: m.GetCounter().GetValue(), + Exemplars: []metricdata.Exemplar[float64]{convertExemplar(m.GetCounter().GetExemplar())}, + } + } + return otelCounter + +} + +func convertHistogram(metrics []*dto.Metric) 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()) + otelHistogram.DataPoints[i] = metricdata.HistogramDataPoint[float64]{ + Attributes: convertLabels(m.GetLabel()), + StartTime: m.GetHistogram().GetCreatedTimestamp().AsTime(), + Time: time.UnixMilli(m.GetTimestampMs()), + Count: m.GetHistogram().GetSampleCount(), + Sum: m.GetHistogram().GetSampleSum(), + Bounds: bounds, + BucketCounts: bucketCounts, + Exemplars: exemplars, + } + } + return otelHistogram + +} + +func convertBuckets(buckets []*dto.Bucket) ([]float64, []uint64, []metricdata.Exemplar[float64]) { + bounds := make([]float64, len(buckets)-1) + bucketCounts := make([]uint64, len(buckets)) + exemplars := make([]metricdata.Exemplar[float64], 0) + for i, bucket := range buckets { + // The last bound is the +Inf bound, which is implied in OTel, but is + // explicit in Prometheus. Skip the last boundary, and assume it is the + // +Inf bound. + if i < len(bounds) { + bounds[i] = bucket.GetUpperBound() + } + bucketCounts[i] = bucket.GetCumulativeCount() + if bucket.GetExemplar() != nil { + exemplars = append(exemplars, convertExemplar(bucket.GetExemplar())) + } + } + return bounds, bucketCounts, exemplars +} + +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() { + if label.GetName() == traceIDLabel { + traceID = []byte(label.GetValue()) + } else if label.GetName() == spanIDLabel { + spanID = []byte(label.GetValue()) + } else { + 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") +} diff --git a/bridge/prometheus/producer_test.go b/bridge/prometheus/producer_test.go new file mode 100644 index 000000000000..aee1dc0d6b77 --- /dev/null +++ b/bridge/prometheus/producer_test.go @@ -0,0 +1,238 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package prometheus // import "go.opentelemetry.io/otel/bridge/prometheus" + +import ( + "context" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" +) + +const ( + traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736" + spanIDStr = "00f067aa0ba902b7" +) + +func TestProduce(t *testing.T) { + testCases := []struct { + name string + testFn func(*prometheus.Registry) + expected []metricdata.ScopeMetrics + wantErr error + }{ + { + name: "no metrics registered", + testFn: func(*prometheus.Registry) {}, + }, + { + name: "gauge", + testFn: func(reg *prometheus.Registry) { + metric := prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "test_gauge_metric", + Help: "A gauge metric for testing", + }) + reg.MustRegister(metric) + metric.Set(123.4) + }, + expected: []metricdata.ScopeMetrics{{ + Scope: instrumentation.Scope{ + Name: scopeName, + }, + Metrics: []metricdata.Metrics{ + { + Name: "test_gauge_metric", + Description: "A gauge metric for testing", + Data: metricdata.Gauge[float64]{ + DataPoints: []metricdata.DataPoint[float64]{ + { + Attributes: attribute.NewSet(), + Value: 123.4, + }, + }, + }, + }, + }, + }}, + }, + { + name: "counter", + testFn: func(reg *prometheus.Registry) { + metric := prometheus.NewCounter(prometheus.CounterOpts{ + Name: "test_counter_metric", + Help: "A counter metric for testing", + }) + reg.MustRegister(metric) + metric.(prometheus.ExemplarAdder).AddWithExemplar( + 245.3, prometheus.Labels{ + "trace_id": traceIDStr, + "span_id": spanIDStr, + "other_attribute": "abcd", + }, + ) + }, + expected: []metricdata.ScopeMetrics{{ + Scope: instrumentation.Scope{ + Name: scopeName, + }, + Metrics: []metricdata.Metrics{ + { + Name: "test_counter_metric", + Description: "A counter metric for testing", + Data: metricdata.Sum[float64]{ + Temporality: metricdata.CumulativeTemporality, + IsMonotonic: true, + DataPoints: []metricdata.DataPoint[float64]{ + { + Attributes: attribute.NewSet(), + Value: 245.3, + Exemplars: []metricdata.Exemplar[float64]{ + { + Value: 245.3, + TraceID: []byte(traceIDStr), + SpanID: []byte(spanIDStr), + FilteredAttributes: []attribute.KeyValue{attribute.String("other_attribute", "abcd")}, + }, + }, + }, + }, + }, + }, + }, + }}, + }, + { + name: "summary dropped", + testFn: func(reg *prometheus.Registry) { + metric := prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "test_summary_metric", + Help: "A summary metric for testing", + }) + reg.MustRegister(metric) + metric.Observe(15.0) + }, + wantErr: errUnsupportedType, + }, + { + name: "histogram", + testFn: func(reg *prometheus.Registry) { + metric := prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "test_histogram_metric", + Help: "A histogram metric for testing", + }) + reg.MustRegister(metric) + metric.(prometheus.ExemplarObserver).ObserveWithExemplar( + 578.3, prometheus.Labels{ + "trace_id": traceIDStr, + "span_id": spanIDStr, + "other_attribute": "efgh", + }, + ) + }, + expected: []metricdata.ScopeMetrics{{ + Scope: instrumentation.Scope{ + Name: scopeName, + }, + Metrics: []metricdata.Metrics{ + { + Name: "test_histogram_metric", + Description: "A histogram metric for testing", + Data: metricdata.Histogram[float64]{ + Temporality: metricdata.CumulativeTemporality, + DataPoints: []metricdata.HistogramDataPoint[float64]{ + { + Count: 1, + Sum: 578.3, + Bounds: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + BucketCounts: []uint64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + Attributes: attribute.NewSet(), + Exemplars: []metricdata.Exemplar[float64]{ + { + Value: 578.3, + TraceID: []byte(traceIDStr), + SpanID: []byte(spanIDStr), + FilteredAttributes: []attribute.KeyValue{ + attribute.String("other_attribute", "efgh"), + }, + }, + }, + }, + }, + }, + }, + }, + }}, + }, + { + name: "partial success", + testFn: func(reg *prometheus.Registry) { + metric := prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "test_gauge_metric", + Help: "A gauge metric for testing", + }) + reg.MustRegister(metric) + metric.Set(123.4) + unsupportedMetric := prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "test_summary_metric", + Help: "A summary metric for testing", + }) + reg.MustRegister(unsupportedMetric) + unsupportedMetric.Observe(15.0) + }, + expected: []metricdata.ScopeMetrics{{ + Scope: instrumentation.Scope{ + Name: scopeName, + }, + Metrics: []metricdata.Metrics{ + { + Name: "test_gauge_metric", + Description: "A gauge metric for testing", + Data: metricdata.Gauge[float64]{ + DataPoints: []metricdata.DataPoint[float64]{ + { + Attributes: attribute.NewSet(), + Value: 123.4, + }, + }, + }, + }, + }, + }}, + wantErr: errUnsupportedType, + }, + } + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + reg := prometheus.NewRegistry() + tt.testFn(reg) + p := NewMetricProducer(WithGatherer(reg)) + output, err := p.Produce(context.Background()) + if tt.wantErr == nil { + assert.Nil(t, err) + } + require.Equal(t, len(output), len(tt.expected)) + for i := range output { + metricdatatest.AssertEqual(t, tt.expected[i], output[i], metricdatatest.IgnoreTimestamp()) + } + }) + } +} diff --git a/example/prometheusbridge/go.mod b/example/prometheusbridge/go.mod new file mode 100644 index 000000000000..8c89d9249658 --- /dev/null +++ b/example/prometheusbridge/go.mod @@ -0,0 +1,42 @@ +module go.opentelemetry.io/opentelemetry-go/example/prometheusbridge + +go 1.19 + +require ( + github.com/prometheus/client_golang v1.16.0 + go.opentelemetry.io/otel/bridge/prometheus v0.39.0 + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.39.0 + go.opentelemetry.io/otel/sdk/metric v0.39.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/prometheus/client_model v0.4.1-0.20230719122841-95a0733b38a0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + golang.org/x/sys v0.10.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect +) + +replace go.opentelemetry.io/otel => ../.. + +replace go.opentelemetry.io/otel/sdk => ../../sdk + +replace go.opentelemetry.io/otel/sdk/metric => ../../sdk/metric + +replace go.opentelemetry.io/otel/bridge/prometheus => ../../bridge/prometheus + +replace go.opentelemetry.io/otel/exporters/stdout/stdoutmetric => ../../exporters/stdout/stdoutmetric + +replace go.opentelemetry.io/otel/trace => ../../trace + +replace go.opentelemetry.io/otel/metric => ../../metric diff --git a/example/prometheusbridge/go.sum b/example/prometheusbridge/go.sum new file mode 100644 index 000000000000..2ad15c2d056d --- /dev/null +++ b/example/prometheusbridge/go.sum @@ -0,0 +1,37 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.1-0.20230719122841-95a0733b38a0 h1:0+/NUBQSANv2NUdj4G9gW+2gy10v89mZB5WMa9/fWPs= +github.com/prometheus/client_model v0.4.1-0.20230719122841-95a0733b38a0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/example/prometheusbridge/main.go b/example/prometheusbridge/main.go new file mode 100644 index 000000000000..fb923d27aea1 --- /dev/null +++ b/example/prometheusbridge/main.go @@ -0,0 +1,60 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "log" + "math/rand" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + + prombridge "go.opentelemetry.io/otel/bridge/prometheus" + "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" + "go.opentelemetry.io/otel/sdk/metric" +) + +// promHistogram is a histogram defined using the prometheus client library. +var promHistogram = promauto.NewHistogram(prometheus.HistogramOpts{ + Name: "random_numbers", + Help: "A histogram of normally distributed random numbers.", + Buckets: prometheus.LinearBuckets(-3, .1, 61), +}) + +func main() { + // Create an OpenTelemetry exporter. Use the stdout exporter for our + // example, but you could use OTLP or another exporter instead. + otelExporter, err := stdoutmetric.New() + if err != nil { + log.Fatal(fmt.Errorf("error creating metric exporter: %w", err)) + } + // Construct a reader which periodically exports to our exporter. + reader := metric.NewPeriodicReader(otelExporter) + // Register the Prometheus metric Producer to add metrics from the + // Prometheus DefaultGatherer to the output. + reader.RegisterProducer(prombridge.NewMetricProducer()) + // Create an OTel MeterProvider that periodically reads from Prometheus, + // but don't use it to create any meters or instruments. We will use + // Prometheus instruments in this example instead. + metric.NewMeterProvider(metric.WithReader(reader)) + // Make observations using our Prometheus histogram. + for { + promHistogram.Observe(rand.NormFloat64()) + } + // We should see our histogram in the output, as well as the metrics + // registered by prometheus by default, which includes go runtime metrics + // and process metrics. +} diff --git a/go.mod b/go.mod index d6483834b99c..280cdb12fcb8 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,28 @@ require ( github.com/go-logr/logr v1.2.4 github.com/go-logr/stdr v1.2.2 github.com/google/go-cmp v0.5.9 + github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_model v0.4.0 github.com/stretchr/testify v1.8.4 go.opentelemetry.io/otel/metric v1.16.0 + go.opentelemetry.io/otel/sdk v1.16.0 + go.opentelemetry.io/otel/sdk/metric v0.39.0 go.opentelemetry.io/otel/trace v1.16.0 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect + golang.org/x/sys v0.8.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d58b1a3b04c3..d8f88b0d0ccc 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,8 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -5,13 +10,45 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/sdk/metric v0.39.0 h1:Kun8i1eYf48kHH83RucG93ffz0zGV1sh46FAScOTuDI= +go.opentelemetry.io/otel/sdk/metric v0.39.0/go.mod h1:piDIRgjcK7u0HCL5pCA4e74qpK/jk3NiUoAHATVAmiI= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/versions.yaml b/versions.yaml index 9dc47532bc2e..19a8ddcaec39 100644 --- a/versions.yaml +++ b/versions.yaml @@ -49,6 +49,8 @@ module-sets: - go.opentelemetry.io/otel/bridge/opencensus - go.opentelemetry.io/otel/bridge/opencensus/test - go.opentelemetry.io/otel/example/view + - go.opentelemetry.io/otel/bridge/prometheus + - go.opentelemetry.io/otel/example/prometheus experimental-schema: version: v0.0.4 modules: