diff --git a/exporter/prometheusexporter/README.md b/exporter/prometheusexporter/README.md
index f8d755af7eb..c556d733ce7 100644
--- a/exporter/prometheusexporter/README.md
+++ b/exporter/prometheusexporter/README.md
@@ -17,6 +17,8 @@ The following settings can be optionally configured:
 - `send_timestamps` (default = `false`): if true, sends the timestamp of the underlying
   metric sample in the response.
 - `metric_expiration` (default = `5m`): defines how long metrics are exposed without updates
+- `resource_to_telemetry_conversion`
+  - `enabled` (default = false): If `enabled` is `true`, all the resource attributes will be converted to metric labels by default.
 
 Example:
 
@@ -30,4 +32,6 @@ exporters:
       "another label": spaced value
     send_timestamps: true
     metric_expiration: 180m
+    resource_to_telemetry_conversion:
+      enabled: true
 ```
diff --git a/exporter/prometheusexporter/config.go b/exporter/prometheusexporter/config.go
index ac0415bdeb8..a361fba1948 100644
--- a/exporter/prometheusexporter/config.go
+++ b/exporter/prometheusexporter/config.go
@@ -20,6 +20,7 @@ import (
 	"github.com/prometheus/client_golang/prometheus"
 
 	"go.opentelemetry.io/collector/config"
+	"go.opentelemetry.io/collector/exporter/exporterhelper"
 )
 
 // Config defines configuration for Prometheus exporter.
@@ -40,6 +41,9 @@ type Config struct {
 
 	// MetricExpiration defines how long metrics are kept without updates
 	MetricExpiration time.Duration `mapstructure:"metric_expiration"`
+
+	// ResourceToTelemetrySettings defines configuration for converting resource attributes to metric labels.
+	exporterhelper.ResourceToTelemetrySettings `mapstructure:"resource_to_telemetry_conversion"`
 }
 
 var _ config.Exporter = (*Config)(nil)
diff --git a/exporter/prometheusexporter/factory.go b/exporter/prometheusexporter/factory.go
index faafb2a9087..9e1f70769ea 100644
--- a/exporter/prometheusexporter/factory.go
+++ b/exporter/prometheusexporter/factory.go
@@ -52,5 +52,30 @@ func createMetricsExporter(
 ) (component.MetricsExporter, error) {
 	pcfg := cfg.(*Config)
 
-	return newPrometheusExporter(pcfg, params.Logger)
+	prometheus, err := newPrometheusExporter(pcfg, params.Logger)
+	if err != nil {
+		return nil, err
+	}
+
+	exporter, err := exporterhelper.NewMetricsExporter(
+		cfg,
+		params.Logger,
+		prometheus.ConsumeMetrics,
+		exporterhelper.WithStart(prometheus.Start),
+		exporterhelper.WithShutdown(prometheus.Shutdown),
+		exporterhelper.WithResourceToTelemetryConversion(pcfg.ResourceToTelemetrySettings),
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	return &wrapMetricsExpoter{
+		MetricsExporter: exporter,
+		exporter:        prometheus,
+	}, nil
+}
+
+type wrapMetricsExpoter struct {
+	component.MetricsExporter
+	exporter *prometheusExporter
 }
diff --git a/exporter/prometheusexporter/factory_test.go b/exporter/prometheusexporter/factory_test.go
index 22b2fb988fb..35d3e193cb1 100644
--- a/exporter/prometheusexporter/factory_test.go
+++ b/exporter/prometheusexporter/factory_test.go
@@ -43,3 +43,19 @@ func TestCreateMetricsExporter(t *testing.T) {
 	require.Equal(t, errBlankPrometheusAddress, err)
 	require.Nil(t, exp)
 }
+
+func TestCreateMetricsExporterExportHelperError(t *testing.T) {
+	cfg, ok := createDefaultConfig().(*Config)
+	require.True(t, ok)
+
+	cfg.Endpoint = "http://localhost:8889"
+
+	// Should give us an exporterhelper.errNilLogger
+	exp, err := createMetricsExporter(
+		context.Background(),
+		component.ExporterCreateParams{Logger: nil},
+		cfg)
+
+	assert.Nil(t, exp)
+	assert.Error(t, err)
+}
diff --git a/exporter/prometheusexporter/prometheus_test.go b/exporter/prometheusexporter/prometheus_test.go
index 4644effd0fc..b3bec5cd6ec 100644
--- a/exporter/prometheusexporter/prometheus_test.go
+++ b/exporter/prometheusexporter/prometheus_test.go
@@ -32,6 +32,8 @@ import (
 	"go.opentelemetry.io/collector/component"
 	"go.opentelemetry.io/collector/component/componenttest"
 	"go.opentelemetry.io/collector/config"
+	"go.opentelemetry.io/collector/exporter/exporterhelper"
+	"go.opentelemetry.io/collector/internal/testdata"
 	"go.opentelemetry.io/collector/translator/internaldata"
 )
 
@@ -162,7 +164,7 @@ func TestPrometheusExporter_endToEnd(t *testing.T) {
 	}
 
 	// Expired metrics should be removed during first scrape
-	exp.(*prometheusExporter).collector.accumulator.(*lastValueAccumulator).metricExpiration = 1 * time.Millisecond
+	exp.(*wrapMetricsExpoter).exporter.collector.accumulator.(*lastValueAccumulator).metricExpiration = 1 * time.Millisecond
 	time.Sleep(10 * time.Millisecond)
 
 	res, err := http.Get("http://localhost:7777/metrics")
@@ -240,7 +242,7 @@ func TestPrometheusExporter_endToEndWithTimestamps(t *testing.T) {
 	}
 
 	// Expired metrics should be removed during first scrape
-	exp.(*prometheusExporter).collector.accumulator.(*lastValueAccumulator).metricExpiration = 1 * time.Millisecond
+	exp.(*wrapMetricsExpoter).exporter.collector.accumulator.(*lastValueAccumulator).metricExpiration = 1 * time.Millisecond
 	time.Sleep(10 * time.Millisecond)
 
 	res, err := http.Get("http://localhost:7777/metrics")
@@ -254,6 +256,65 @@ func TestPrometheusExporter_endToEndWithTimestamps(t *testing.T) {
 	require.Emptyf(t, string(blob), "Metrics did not expire")
 }
 
+func TestPrometheusExporter_endToEndWithResource(t *testing.T) {
+	cfg := &Config{
+		ExporterSettings: config.NewExporterSettings(typeStr),
+		Namespace:        "test",
+		ConstLabels: map[string]string{
+			"foo2":  "bar2",
+			"code2": "one2",
+		},
+		Endpoint:         ":7777",
+		SendTimestamps:   true,
+		MetricExpiration: 120 * time.Minute,
+		ResourceToTelemetrySettings: exporterhelper.ResourceToTelemetrySettings{
+			Enabled: true,
+		},
+	}
+
+	factory := NewFactory()
+	creationParams := component.ExporterCreateParams{Logger: zap.NewNop()}
+	exp, err := factory.CreateMetricsExporter(context.Background(), creationParams, cfg)
+	assert.NoError(t, err)
+
+	t.Cleanup(func() {
+		require.NoError(t, exp.Shutdown(context.Background()))
+		// trigger a get so that the server cleans up our keepalive socket
+		http.Get("http://localhost:7777/metrics")
+	})
+
+	assert.NotNil(t, exp)
+	require.NoError(t, exp.Start(context.Background(), componenttest.NewNopHost()))
+
+	md := testdata.GenerateMetricsOneMetric()
+	assert.NotNil(t, md)
+
+	assert.NoError(t, exp.ConsumeMetrics(context.Background(), md))
+
+	rsp, err := http.Get("http://localhost:7777/metrics")
+	require.NoError(t, err, "Failed to perform a scrape")
+
+	if g, w := rsp.StatusCode, 200; g != w {
+		t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
+	}
+
+	blob, _ := ioutil.ReadAll(rsp.Body)
+	_ = rsp.Body.Close()
+
+	want := []string{
+		`# HELP test_counter_int`,
+		`# TYPE test_counter_int counter`,
+		`test_counter_int{code2="one2",foo2="bar2",label_1="label-value-1",resource_attr="resource-attr-val-1"} 123 1581452773000`,
+		`test_counter_int{code2="one2",foo2="bar2",label_2="label-value-2",resource_attr="resource-attr-val-1"} 456 1581452773000`,
+	}
+
+	for _, w := range want {
+		if !strings.Contains(string(blob), w) {
+			t.Errorf("Missing %v from response:\n%v", w, string(blob))
+		}
+	}
+}
+
 func metricBuilder(delta int64, prefix string) []*metricspb.Metric {
 	return []*metricspb.Metric{
 		{