diff --git a/bridge/opencensus/go.mod b/bridge/opencensus/go.mod index 0afe0807a803..840c8a53255d 100644 --- a/bridge/opencensus/go.mod +++ b/bridge/opencensus/go.mod @@ -3,32 +3,25 @@ module go.opentelemetry.io/otel/bridge/opencensus go 1.19 require ( - github.com/prometheus/client_golang v1.16.0 github.com/stretchr/testify v1.8.4 go.opencensus.io v0.24.0 go.opentelemetry.io/otel v1.16.0 - go.opentelemetry.io/otel/exporters/prometheus v0.0.0-00010101000000-000000000000 - 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/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect golang.org/x/sys v0.10.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/bridge/opencensus/go.sum b/bridge/opencensus/go.sum index e8a70b03ab11..6e70b53855cb 100644 --- a/bridge/opencensus/go.sum +++ b/bridge/opencensus/go.sum @@ -1,12 +1,9 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -32,34 +29,28 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -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.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -85,7 +76,6 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -120,12 +110,9 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/opencensus/metric.go b/bridge/opencensus/metric.go index 62a52696fba7..0d3053514c21 100644 --- a/bridge/opencensus/metric.go +++ b/bridge/opencensus/metric.go @@ -22,117 +22,87 @@ import ( "go.opentelemetry.io/otel" internal "go.opentelemetry.io/otel/bridge/opencensus/internal/ocmetric" - "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric" - "go.opentelemetry.io/otel/sdk/metric/aggregation" "go.opentelemetry.io/otel/sdk/metric/metricdata" ) const scopeName = "go.opentelemetry.io/otel/bridge/opencensus" -// exporter wraps an OpenTelemetry exporter and adds OpenCensus metrics to it. -type exporter struct { - manager *metricproducer.Manager - base metric.Exporter -} - // NewMetricExporter returns an OpenTelemetry exporter that adds metrics from OpenCensus // before exporting to the base OpenTelemetry exporter. -func NewMetricExporter(base metric.Exporter) metric.Exporter { +// +// Usage: +// +// reader := metric.NewPeriodicReader(opencensus.NewMetricExporter(exporter)) +func NewMetricExporter(base metric.Exporter) *exporter { return &exporter{ - base: base, - manager: metricproducer.GlobalManager(), - } -} - -func (e *exporter) Export(ctx context.Context, sdkMetrics *metricdata.ResourceMetrics) error { - producers := e.manager.GetAll() - data := []*ocmetricdata.Metric{} - for _, ocProducer := range producers { - data = append(data, ocProducer.Read()...) - } - otelmetrics, err := internal.ConvertMetrics(data) - if err != nil { - otel.Handle(err) - } - if len(otelmetrics) > 0 { - // add metrics from OpenCensus to our exported batch of metrics under - // its own scope. - sdkMetrics.ScopeMetrics = append( - sdkMetrics.ScopeMetrics, - metricdata.ScopeMetrics{ - Scope: instrumentation.Scope{ - Name: scopeName, - }, - Metrics: otelmetrics, - }) + Exporter: base, } - return e.base.Export(ctx, sdkMetrics) -} - -func (e *exporter) Temporality(kind metric.InstrumentKind) metricdata.Temporality { - return e.base.Temporality(kind) - } -func (e *exporter) Aggregation(kind metric.InstrumentKind) aggregation.Aggregation { - return e.base.Aggregation(kind) - -} - -func (e *exporter) ForceFlush(ctx context.Context) error { - return e.base.ForceFlush(ctx) - +// exporter wraps an OpenTelemetry exporter and adds OpenCensus metrics to it. +type exporter struct { + metric.Exporter } -func (e *exporter) Shutdown(ctx context.Context) error { - return e.base.Shutdown(ctx) +// Export exports a batch of metrics from the OpenTelemetry SDK, and adds +// metrics from OpenCensus prior to exporting. +func (e *exporter) Export(ctx context.Context, sdkMetrics *metricdata.ResourceMetrics) error { + appendOpenCensusMetrics(sdkMetrics) + return e.Exporter.Export(ctx, sdkMetrics) } -func NewBridgedPrometheusExporter(opts ...prometheus.Option) (*prometheus.Exporter, error) { - exporter, err := prometheus.New(opts...) - if err != nil { - return nil, err - } - exporter.Reader = &PrometheusExporter{ - Reader: metric.NewManualReader(), - manager: metricproducer.GlobalManager(), +// NewMetricReader wraps an existing metric reader, but overrides the Collect +// function to insert metrics from OpenCensus. +// +// Usage: +// +// exporter := prometheus.NewExporter() +// exporter.Reader = opencensus.NewMetricReader() +func NewMetricReader(opts ...metric.ManualReaderOption) *reader { + return &reader{ + Reader: metric.NewManualReader(opts...), } - return exporter, nil } -type PrometheusExporter struct { - manager *metricproducer.Manager +type reader struct { metric.Reader } // Override the collect function with one that inserts metrics from OpenCensus. -func (p *PrometheusExporter) Collect(ctx context.Context, sdkMetrics *metricdata.ResourceMetrics) error { - err := p.Reader.Collect(ctx, sdkMetrics) +func (r *reader) Collect(ctx context.Context, sdkMetrics *metricdata.ResourceMetrics) error { + // Collect metrics from the OTel SDK + err := r.Reader.Collect(ctx, sdkMetrics) if err != nil { return err } - producers := p.manager.GetAll() + appendOpenCensusMetrics(sdkMetrics) + return nil +} + +// appendOpenCensusMetrics gets metrics from the OpenCensus manager, and +// appends them to ResourceMetrics from the OpenTelemetry SDK. +func appendOpenCensusMetrics(sdkMetrics *metricdata.ResourceMetrics) { + // Collect metrics from OpenCensus + producers := metricproducer.GlobalManager().GetAll() data := []*ocmetricdata.Metric{} for _, ocProducer := range producers { data = append(data, ocProducer.Read()...) } - otelmetrics, err := internal.ConvertMetrics(data) + convertedMetrics, err := internal.ConvertMetrics(data) if err != nil { otel.Handle(err) } - if len(otelmetrics) > 0 { - // add metrics from OpenCensus to our exported batch of metrics under - // its own scope. + // Insert metrics from OpenCensus into the metrics from the OTel SDK + if len(convertedMetrics) > 0 { sdkMetrics.ScopeMetrics = append( sdkMetrics.ScopeMetrics, metricdata.ScopeMetrics{ Scope: instrumentation.Scope{ Name: scopeName, }, - Metrics: otelmetrics, + Metrics: convertedMetrics, }) } - return nil } diff --git a/bridge/opencensus/metric_test.go b/bridge/opencensus/metric_test.go index df164e58cb2c..5d4313021a5c 100644 --- a/bridge/opencensus/metric_test.go +++ b/bridge/opencensus/metric_test.go @@ -17,20 +17,15 @@ package opencensus // import "go.opentelemetry.io/otel/bridge/opencensus" import ( "context" "fmt" - "os" "testing" "time" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" ocmetricdata "go.opencensus.io/metric/metricdata" "go.opencensus.io/metric/metricproducer" ocresource "go.opencensus.io/resource" "go.opentelemetry.io/otel/attribute" - prometheusexporter "go.opentelemetry.io/otel/exporters/prometheus" - otelmetric "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" @@ -38,27 +33,22 @@ import ( "go.opentelemetry.io/otel/sdk/resource" ) -type fakeOCProducer struct { - metrics []*ocmetricdata.Metric -} +var now = time.Now() -func (f *fakeOCProducer) Read() []*ocmetricdata.Metric { - return f.metrics +type testCase struct { + desc string + input []*ocmetricdata.Metric + inputMetrics *metricdata.ResourceMetrics + inputErr error + expected *metricdata.ResourceMetrics + expectErr bool } -func TestPushMetricsExporter(t *testing.T) { - now := time.Now() - for _, tc := range []struct { - desc string - input []*ocmetricdata.Metric - inputMetrics *metricdata.ResourceMetrics - exportErr error - expected *metricdata.ResourceMetrics - expectErr bool - }{ +func testCases() []testCase { + return []testCase{ { desc: "export error", - exportErr: fmt.Errorf("failed to export"), + inputErr: fmt.Errorf("failed to export"), inputMetrics: &metricdata.ResourceMetrics{}, input: []*ocmetricdata.Metric{ { @@ -137,12 +127,16 @@ func TestPushMetricsExporter(t *testing.T) { }, }, }, - } { + } +} + +func TestMetricExporter(t *testing.T) { + for _, tc := range testCases() { t.Run(tc.desc, func(t *testing.T) { fakeProducer := &fakeOCProducer{metrics: tc.input} metricproducer.GlobalManager().AddProducer(fakeProducer) defer metricproducer.GlobalManager().DeleteProducer(fakeProducer) - fake := &fakeExporter{err: tc.exportErr} + fake := &fakeExporter{err: tc.inputErr} exporter := NewMetricExporter(fake) err := exporter.Export(context.Background(), tc.inputMetrics) if tc.expectErr { @@ -160,6 +154,41 @@ func TestPushMetricsExporter(t *testing.T) { } } +func TestMetricReader(t *testing.T) { + for _, tc := range testCases() { + t.Run(tc.desc, func(t *testing.T) { + fakeProducer := &fakeOCProducer{metrics: tc.input} + metricproducer.GlobalManager().AddProducer(fakeProducer) + defer metricproducer.GlobalManager().DeleteProducer(fakeProducer) + reader := NewMetricReader() + // replace the SDK reader so we can inject an error + reader.Reader = &fakeSDKReader{err: tc.inputErr, data: tc.inputMetrics} + got := &metricdata.ResourceMetrics{} + err := reader.Collect(context.Background(), got) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + if tc.expected != nil { + require.NotNil(t, got) + metricdatatest.AssertEqual(t, *tc.expected, *got) + } else { + require.Nil(t, got.Resource) + require.Nil(t, got.ScopeMetrics) + } + }) + } +} + +type fakeOCProducer struct { + metrics []*ocmetricdata.Metric +} + +func (f *fakeOCProducer) Read() []*ocmetricdata.Metric { + return f.metrics +} + type fakeExporter struct { metric.Exporter data *metricdata.ResourceMetrics @@ -173,112 +202,15 @@ func (f *fakeExporter) Export(ctx context.Context, data *metricdata.ResourceMetr return f.err } -func TestPrometheusMetricsExporter(t *testing.T) { - now := time.Now() - for _, tc := range []struct { - desc string - input []*ocmetricdata.Metric - recordMetrics func(ctx context.Context, meter otelmetric.Meter) - expected *metricdata.ResourceMetrics - expectedFile string - inputMetrics *metricdata.ResourceMetrics - }{ - { - desc: "success", - input: []*ocmetricdata.Metric{ - { - Descriptor: ocmetricdata.Descriptor{ - Name: "foo.com/my-gauge", - Description: "a gauge opencensus metric", - Type: ocmetricdata.TypeGaugeInt64, - }, - TimeSeries: []*ocmetricdata.TimeSeries{ - { - StartTime: now, - Points: []ocmetricdata.Point{ - {Value: int64(123), Time: now}, - }, - }, - }, - }, - }, - expected: &metricdata.ResourceMetrics{ - Resource: resource.NewSchemaless( - attribute.String("R1", "V1"), - attribute.String("R2", "V2"), - ), - ScopeMetrics: []metricdata.ScopeMetrics{ - { - Scope: instrumentation.Scope{ - Name: scopeName, - }, - Metrics: []metricdata.Metrics{ - { - Name: "foo.com/my-gauge", - Description: "a gauge opencensus metric", - Unit: "", - Data: metricdata.Gauge[int64]{ - DataPoints: []metricdata.DataPoint[int64]{ - { - Attributes: attribute.NewSet(), - StartTime: now, - Time: now, - Value: 123, - }, - }, - }, - }, - }, - }, - }, - }, - recordMetrics: func(ctx context.Context, meter otelmetric.Meter) { - opt := otelmetric.WithAttributes( - attribute.Key("A").String("B"), - ) - counter, err := meter.Float64Counter( - "foo", - otelmetric.WithDescription("a simple counter"), - otelmetric.WithUnit("ms"), - ) - require.NoError(t, err) - counter.Add(ctx, 5, opt) - counter.Add(ctx, 10.3, opt) - counter.Add(ctx, 9, opt) - }, - expectedFile: "testdata/counter.txt", - }, - } { - t.Run(tc.desc, func(t *testing.T) { - ctx := context.Background() - fakeProducer := &fakeOCProducer{metrics: tc.input} - metricproducer.GlobalManager().AddProducer(fakeProducer) - defer metricproducer.GlobalManager().DeleteProducer(fakeProducer) - registry := prometheus.NewRegistry() - exporter, err := NewBridgedPrometheusExporter(prometheusexporter.WithRegisterer(registry)) - require.NoError(t, err) - provider := metric.NewMeterProvider( - metric.WithReader(exporter), - metric.WithResource(resource.NewSchemaless( - attribute.String("R1", "V1"), - attribute.String("R2", "V2"), - )), - ) - got := &metricdata.ResourceMetrics{} - err = exporter.Collect(ctx, got) - require.NoError(t, err) - metricdatatest.AssertEqual(t, *tc.expected, *got) - - meter := provider.Meter("testmeter") - - tc.recordMetrics(ctx, meter) - - file, err := os.Open(tc.expectedFile) - require.NoError(t, err) - t.Cleanup(func() { require.NoError(t, file.Close()) }) +type fakeSDKReader struct { + metric.Reader + data *metricdata.ResourceMetrics + err error +} - err = testutil.GatherAndCompare(registry, file) - require.NoError(t, err) - }) +func (f *fakeSDKReader) Collect(ctx context.Context, data *metricdata.ResourceMetrics) error { + if f.err == nil { + *data = *f.data } + return f.err } diff --git a/example/opencensus/go.mod b/example/opencensus/go.mod index 80903db6ca93..460ee5fd4c5b 100644 --- a/example/opencensus/go.mod +++ b/example/opencensus/go.mod @@ -12,6 +12,7 @@ require ( go.opencensus.io v0.24.0 go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel/bridge/opencensus v0.39.0 + go.opentelemetry.io/otel/exporters/prometheus v0.39.0 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.39.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.16.0 go.opentelemetry.io/otel/sdk v1.16.0 @@ -30,7 +31,6 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.39.0 // 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 diff --git a/example/opencensus/main.go b/example/opencensus/main.go index 244646378c45..38e7254431c1 100644 --- a/example/opencensus/main.go +++ b/example/opencensus/main.go @@ -30,6 +30,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/bridge/opencensus" + "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/sdk/metric" @@ -58,10 +59,14 @@ func main() { } metricsExporter, err := stdoutmetric.New() if err != nil { - log.Fatal(fmt.Errorf("error creating metric exporter: %w", err)) + log.Fatal(fmt.Errorf("error creating stdout metric exporter: %w", err)) } tracing(traceExporter) - if err := monitoring(metricsExporter); err != nil { + pullExporter, err := prometheus.New() + if err != nil { + log.Fatal(fmt.Errorf("error creating prometheus metric exporter: %w", err)) + } + if err := monitoring(metricsExporter, pullExporter); err != nil { log.Fatal(err) } } @@ -101,12 +106,17 @@ func tracing(otExporter sdktrace.SpanExporter) { // monitoring demonstrates creating an IntervalReader using the OpenTelemetry // exporter to send metrics to the exporter by using either an OpenCensus // registry or an OpenCensus view. -func monitoring(exporter metric.Exporter) error { +func monitoring(pushExporter metric.Exporter, pullExporter *prometheus.Exporter) error { log.Println("Adding the OpenCensus metric Producer to an OpenTelemetry Reader to export OpenCensus metrics using the OpenTelemetry stdout exporter.") - // Wrap our exporter with the opencensus bridge to add metrics from OpenCensus to the output. - exporter = opencensus.NewMetricExporter(exporter) - reader := metric.NewPeriodicReader(exporter) - metric.NewMeterProvider(metric.WithReader(reader)) + // Wrap our push exporter with the opencensus bridge to add metrics from OpenCensus to the output. + pushExporter = opencensus.NewMetricExporter(pushExporter) + reader := metric.NewPeriodicReader(pushExporter) + + // Override the pull exporter's Reader with the OpenCensus reader bridge. + pullExporter.Reader = opencensus.NewMetricReader() + + // Make a MeterProvider with push and pull bridged exporters. + metric.NewMeterProvider(metric.WithReader(reader), metric.WithReader(pullExporter)) log.Println("Registering a gauge metric using an OpenCensus registry.") r := ocmetric.NewRegistry()