diff --git a/exporters/autoexport/metrics.go b/exporters/autoexport/metrics.go index 18aa5a63ac5..6e158dcbd72 100644 --- a/exporters/autoexport/metrics.go +++ b/exporters/autoexport/metrics.go @@ -136,10 +136,22 @@ func init() { }) RegisterMetricReader("prometheus", func(ctx context.Context) (metric.Reader, error) { // create an isolated registry instead of using the global registry -- - // the user might not want to mix OTel with non-OTel metrics + // the user might not want to mix OTel with non-OTel metrics. + // Those that want to comingle metrics from global registry can use + // OTEL_METRICS_PRODUCERS=prometheus reg := prometheus.NewRegistry() - reader, err := promexporter.New(promexporter.WithRegisterer(reg)) + exporterOpts := []promexporter.Option{promexporter.WithRegisterer(reg)} + + producer, err := metricsProducers.create(ctx) + if err != nil { + return nil, err + } + if producer != nil { + exporterOpts = append(exporterOpts, promexporter.WithProducer(producer)) + } + + reader, err := promexporter.New(exporterOpts...) if err != nil { return nil, err } diff --git a/exporters/autoexport/metrics_test.go b/exporters/autoexport/metrics_test.go index 93f41bbe86f..64d819c29f9 100644 --- a/exporters/autoexport/metrics_test.go +++ b/exporters/autoexport/metrics_test.go @@ -11,6 +11,7 @@ import ( "net/http/httptest" "reflect" "runtime/debug" + "strings" "testing" "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" @@ -122,7 +123,7 @@ func TestMetricExporterPrometheusInvalidPort(t *testing.T) { assert.ErrorContains(t, err, "binding") } -func TestMetricProducerPrometheus(t *testing.T) { +func TestMetricProducerPrometheusWithOTLPExporter(t *testing.T) { assertNoOtelHandleErrors(t) requestWaitChan := make(chan struct{}) @@ -150,12 +151,44 @@ func TestMetricProducerPrometheus(t *testing.T) { assert.IsType(t, &metric.PeriodicReader{}, r) // Register it with a meter provider to ensure it is used. - metric.NewMeterProvider(metric.WithReader(r)) + mp := metric.NewMeterProvider(metric.WithReader(r)) // Shutdown actually makes an export call. assert.NoError(t, r.Shutdown(context.Background())) <-requestWaitChan ts.Close() + assert.NoError(t, mp.Shutdown(context.Background())) + goleak.VerifyNone(t) +} + +func TestMetricProducerPrometheusWithPrometheusExporter(t *testing.T) { + assertNoOtelHandleErrors(t) + + t.Setenv("OTEL_METRICS_EXPORTER", "prometheus") + t.Setenv("OTEL_EXPORTER_PROMETHEUS_PORT", "0") + t.Setenv("OTEL_METRICS_PRODUCERS", "prometheus") + + r, err := NewMetricReader(context.Background()) + assert.NoError(t, err) + + // pull-based exporters like Prometheus need to be registered + mp := metric.NewMeterProvider(metric.WithReader(r)) + + rws, ok := r.(readerWithServer) + if !ok { + t.Errorf("expected readerWithServer but got %v", r) + } + + resp, err := http.Get(fmt.Sprintf("http://%s/metrics", rws.addr)) + assert.NoError(t, err) + body, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + + // By default there are two metrics exporter. target_info and promhttp_metric_handler_errors_total. + // But by including the prometheus producer we should have more. + assert.Greater(t, strings.Count(string(body), "# HELP"), 2) + + assert.NoError(t, mp.Shutdown(context.Background())) goleak.VerifyNone(t) }