From d62a2e72d9e40ae37641b2f6b5dfe022b39b7a0b Mon Sep 17 00:00:00 2001 From: Yang Song Date: Thu, 18 Apr 2019 19:44:27 -0700 Subject: [PATCH] Exporter/Prometheus: Allow to set custom namespace. (#1850) * Exporter/Prometheus: Allow to set custom namespace. * Fix wording. --- CHANGELOG.md | 1 + .../prometheus/PrometheusExportUtils.java | 24 +++++--- .../prometheus/PrometheusStatsCollector.java | 27 +++++--- .../PrometheusStatsConfiguration.java | 23 ++++++- .../prometheus/PrometheusExportUtilsTest.java | 61 ++++++++++++++++--- .../PrometheusStatsCollectorTest.java | 27 ++++++-- 6 files changed, 133 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e6065798c..0163e3d678 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Add HTTP text format serializer to Tag propagation component. - Support constant labels in Gauge APIs. - Add an option to allow users to override the default "opencensus_task" metric label in Stackdriver Stats Exporter. +- Allow setting custom namespace in Prometheus exporter. ## 0.20.0 - 2019-03-28 - Add OpenCensus Java OC-Agent Trace Exporter. diff --git a/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtils.java b/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtils.java index afe188a2a9..b4588a5000 100644 --- a/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtils.java +++ b/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtils.java @@ -55,10 +55,10 @@ *

Each OpenCensus {@link Metric} will be converted to a Prometheus {@link MetricFamilySamples}, * and each {@code Point} of the {@link Metric} will be converted to Prometheus {@link Sample}s. * - *

{@link io.opencensus.metrics.export.Value.ValueDouble}, {@link + *

{@code io.opencensus.metrics.export.Value.ValueDouble}, {@code * io.opencensus.metrics.export.Value.ValueLong} will be converted to a single {@link Sample}. - * {@link io.opencensus.metrics.export.Value.ValueSummary} will be converted to two {@link Sample}s - * sum and count. {@link io.opencensus.metrics.export.Value.ValueDistribution} will be converted to + * {@code io.opencensus.metrics.export.Value.ValueSummary} will be converted to two {@code Sample}s + * sum and count. {@code io.opencensus.metrics.export.Value.ValueDistribution} will be converted to * a list of {@link Sample}s that have the sum, count and histogram buckets. * *

{@link LabelKey} and {@link LabelValue} will be converted to Prometheus {@code LabelName} and @@ -76,9 +76,9 @@ final class PrometheusExportUtils { @VisibleForTesting static final String LABEL_NAME_QUANTILE = "quantile"; // Converts a Metric to a Prometheus MetricFamilySamples. - static MetricFamilySamples createMetricFamilySamples(Metric metric) { + static MetricFamilySamples createMetricFamilySamples(Metric metric, String namespace) { MetricDescriptor metricDescriptor = metric.getMetricDescriptor(); - String name = Collector.sanitizeMetricName(metricDescriptor.getName()); + String name = getNamespacedName(metricDescriptor.getName(), namespace); Type type = getType(metricDescriptor.getType()); List labelNames = convertToLabelNames(metricDescriptor.getLabelKeys()); List samples = Lists.newArrayList(); @@ -94,8 +94,8 @@ static MetricFamilySamples createMetricFamilySamples(Metric metric) { // Converts a MetricDescriptor to a Prometheus MetricFamilySamples. // Used only for Prometheus metric registry, should not contain any actual samples. static MetricFamilySamples createDescribableMetricFamilySamples( - MetricDescriptor metricDescriptor) { - String name = Collector.sanitizeMetricName(metricDescriptor.getName()); + MetricDescriptor metricDescriptor, String namespace) { + String name = getNamespacedName(metricDescriptor.getName(), namespace); Type type = getType(metricDescriptor.getType()); List labelNames = convertToLabelNames(metricDescriptor.getLabelKeys()); @@ -116,6 +116,16 @@ static MetricFamilySamples createDescribableMetricFamilySamples( name, type, metricDescriptor.getDescription(), Collections.emptyList()); } + private static String getNamespacedName(String metricName, String namespace) { + if (!namespace.isEmpty()) { + if (!namespace.endsWith("/") && !namespace.endsWith("_")) { + namespace += '_'; + } + metricName = namespace + metricName; + } + return Collector.sanitizeMetricName(metricName); + } + @VisibleForTesting static Type getType(MetricDescriptor.Type type) { if (type == MetricDescriptor.Type.CUMULATIVE_INT64 diff --git a/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollector.java b/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollector.java index 391675a63c..3d2b680076 100644 --- a/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollector.java +++ b/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollector.java @@ -52,6 +52,7 @@ public final class PrometheusStatsCollector extends Collector implements Collect private static final String EXPORT_METRICS_TO_PROMETHEUS = "ExportMetricsToPrometheus"; private final MetricReader collectMetricReader; private final MetricReader describeMetricReader; + private final String namespace; /** * Creates a {@link PrometheusStatsCollector} and registers it to Prometheus {@link @@ -68,7 +69,7 @@ public final class PrometheusStatsCollector extends Collector implements Collect * @since 0.12 */ public static void createAndRegister() { - new PrometheusStatsCollector(Metrics.getExportComponent().getMetricProducerManager()) + new PrometheusStatsCollector(Metrics.getExportComponent().getMetricProducerManager(), "") .register(); } @@ -88,12 +89,18 @@ public static void createAndRegister(PrometheusStatsConfiguration configuration) if (registry == null) { registry = CollectorRegistry.defaultRegistry; } - new PrometheusStatsCollector(Metrics.getExportComponent().getMetricProducerManager()) + new PrometheusStatsCollector( + Metrics.getExportComponent().getMetricProducerManager(), configuration.getNamespace()) .register(registry); } private static final class ExportMetricExporter extends MetricExporter { private final ArrayList samples = new ArrayList<>(); + private final String namespace; + + private ExportMetricExporter(String namespace) { + this.namespace = namespace; + } @Override public void export(Collection metrics) { @@ -111,7 +118,7 @@ public void export(Collection metrics) { continue; } try { - samples.add(PrometheusExportUtils.createMetricFamilySamples(metric)); + samples.add(PrometheusExportUtils.createMetricFamilySamples(metric, namespace)); } catch (Throwable e) { logger.log(Level.WARNING, "Exception thrown when collecting metric samples.", e); tracer @@ -127,13 +134,18 @@ public void export(Collection metrics) { @Override public List collect() { - ExportMetricExporter exportMetricExporter = new ExportMetricExporter(); + ExportMetricExporter exportMetricExporter = new ExportMetricExporter(namespace); collectMetricReader.readAndExport(exportMetricExporter); return exportMetricExporter.samples; } private static final class DescribeMetricExporter extends MetricExporter { private final ArrayList samples = new ArrayList<>(); + private final String namespace; + + private DescribeMetricExporter(String namespace) { + this.namespace = namespace; + } @Override public void export(Collection metrics) { @@ -142,7 +154,7 @@ public void export(Collection metrics) { try { samples.add( PrometheusExportUtils.createDescribableMetricFamilySamples( - metric.getMetricDescriptor())); + metric.getMetricDescriptor(), namespace)); } catch (Throwable e) { logger.log(Level.WARNING, "Exception thrown when describing metrics.", e); tracer @@ -158,13 +170,13 @@ public void export(Collection metrics) { @Override public List describe() { - DescribeMetricExporter describeMetricExporter = new DescribeMetricExporter(); + DescribeMetricExporter describeMetricExporter = new DescribeMetricExporter(namespace); describeMetricReader.readAndExport(describeMetricExporter); return describeMetricExporter.samples; } @VisibleForTesting - PrometheusStatsCollector(MetricProducerManager metricProducerManager) { + PrometheusStatsCollector(MetricProducerManager metricProducerManager, String namespace) { this.collectMetricReader = MetricReader.create( MetricReader.Options.builder() @@ -177,6 +189,7 @@ public List describe() { .setMetricProducerManager(metricProducerManager) .setSpanName(DESCRIBE_METRICS_FOR_PROMETHEUS) .build()); + this.namespace = namespace; } private static String exceptionMessage(Throwable e) { diff --git a/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsConfiguration.java b/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsConfiguration.java index 3e8b95ed31..b609a88f27 100644 --- a/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsConfiguration.java +++ b/exporters/stats/prometheus/src/main/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsConfiguration.java @@ -18,7 +18,6 @@ import com.google.auto.value.AutoValue; import io.prometheus.client.CollectorRegistry; -import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; /** @@ -38,9 +37,16 @@ public abstract class PrometheusStatsConfiguration { * @return the Prometheus {@code CollectorRegistry}. * @since 0.13 */ - @Nullable public abstract CollectorRegistry getRegistry(); + /** + * Returns the namespace used for Prometheus metrics. + * + * @return the namespace. + * @since 0.21 + */ + public abstract String getNamespace(); + /** * Returns a new {@link Builder}. * @@ -48,7 +54,9 @@ public abstract class PrometheusStatsConfiguration { * @since 0.13 */ public static Builder builder() { - return new AutoValue_PrometheusStatsConfiguration.Builder(); + return new AutoValue_PrometheusStatsConfiguration.Builder() + .setRegistry(CollectorRegistry.defaultRegistry) + .setNamespace(""); } /** @@ -70,6 +78,15 @@ public abstract static class Builder { */ public abstract Builder setRegistry(CollectorRegistry registry); + /** + * Sets the namespace used for Prometheus metrics. + * + * @param namespace the namespace. + * @return this. + * @since 0.21 + */ + public abstract Builder setNamespace(String namespace); + /** * Builds a new {@link PrometheusStatsConfiguration} with current settings. * diff --git a/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtilsTest.java b/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtilsTest.java index 1e0cb5e0b4..646d14b9ff 100644 --- a/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtilsTest.java +++ b/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusExportUtilsTest.java @@ -188,22 +188,49 @@ public void getType() { public void createDescribableMetricFamilySamples() { assertThat( PrometheusExportUtils.createDescribableMetricFamilySamples( - CUMULATIVE_METRIC_DESCRIPTOR)) + CUMULATIVE_METRIC_DESCRIPTOR, "")) .isEqualTo( new MetricFamilySamples( METRIC_NAME, Type.COUNTER, METRIC_DESCRIPTION, Collections.emptyList())); assertThat( - PrometheusExportUtils.createDescribableMetricFamilySamples(SUMMARY_METRIC_DESCRIPTOR)) + PrometheusExportUtils.createDescribableMetricFamilySamples( + SUMMARY_METRIC_DESCRIPTOR, "")) .isEqualTo( new MetricFamilySamples( METRIC_NAME2, Type.SUMMARY, METRIC_DESCRIPTION, Collections.emptyList())); assertThat( - PrometheusExportUtils.createDescribableMetricFamilySamples(HISTOGRAM_METRIC_DESCRIPTOR)) + PrometheusExportUtils.createDescribableMetricFamilySamples( + HISTOGRAM_METRIC_DESCRIPTOR, "")) .isEqualTo( new MetricFamilySamples( METRIC_NAME3, Type.HISTOGRAM, METRIC_DESCRIPTION, Collections.emptyList())); } + @Test + public void createDescribableMetricFamilySamples_WithNamespace() { + String namespace1 = "myorg"; + assertThat( + PrometheusExportUtils.createDescribableMetricFamilySamples( + CUMULATIVE_METRIC_DESCRIPTOR, namespace1)) + .isEqualTo( + new MetricFamilySamples( + namespace1 + '_' + METRIC_NAME, + Type.COUNTER, + METRIC_DESCRIPTION, + Collections.emptyList())); + + String namespace2 = "opencensus/"; + assertThat( + PrometheusExportUtils.createDescribableMetricFamilySamples( + CUMULATIVE_METRIC_DESCRIPTOR, namespace2)) + .isEqualTo( + new MetricFamilySamples( + "opencensus_" + METRIC_NAME, + Type.COUNTER, + METRIC_DESCRIPTION, + Collections.emptyList())); + } + @Test public void getSamples() { assertThat( @@ -306,7 +333,7 @@ public void createDescribableMetricFamilySamples_Histogram_DisallowLeLabelName() "Prometheus Histogram cannot have a label named 'le', " + "because it is a reserved label for bucket boundaries. " + "Please remove this key from your view."); - PrometheusExportUtils.createDescribableMetricFamilySamples(LE_LABEL_METRIC_DESCRIPTOR); + PrometheusExportUtils.createDescribableMetricFamilySamples(LE_LABEL_METRIC_DESCRIPTOR, ""); } @Test @@ -315,12 +342,13 @@ public void createDescribableMetricFamilySamples_Summary_DisallowQuantileLabelNa thrown.expectMessage( "Prometheus Summary cannot have a label named 'quantile', " + "because it is a reserved label. Please remove this key from your view."); - PrometheusExportUtils.createDescribableMetricFamilySamples(QUANTILE_LABEL_METRIC_DESCRIPTOR); + PrometheusExportUtils.createDescribableMetricFamilySamples( + QUANTILE_LABEL_METRIC_DESCRIPTOR, ""); } @Test public void createMetricFamilySamples() { - assertThat(PrometheusExportUtils.createMetricFamilySamples(LONG_METRIC)) + assertThat(PrometheusExportUtils.createMetricFamilySamples(LONG_METRIC, "")) .isEqualTo( new MetricFamilySamples( METRIC_NAME, @@ -332,7 +360,7 @@ public void createMetricFamilySamples() { Arrays.asList("k1", "k2"), Arrays.asList("v1", "v2"), 123456789)))); - assertThat(PrometheusExportUtils.createMetricFamilySamples(SUMMARY_METRIC)) + assertThat(PrometheusExportUtils.createMetricFamilySamples(SUMMARY_METRIC, "")) .isEqualTo( new MetricFamilySamples( METRIC_NAME2, @@ -354,7 +382,7 @@ public void createMetricFamilySamples() { Arrays.asList("k_3", LABEL_NAME_QUANTILE), Arrays.asList("v1", "0.99"), 10.2)))); - assertThat(PrometheusExportUtils.createMetricFamilySamples(DISTRIBUTION_METRIC)) + assertThat(PrometheusExportUtils.createMetricFamilySamples(DISTRIBUTION_METRIC, "")) .isEqualTo( new MetricFamilySamples( METRIC_NAME3, @@ -392,4 +420,21 @@ public void createMetricFamilySamples() { Collections.singletonList("v-3"), 22.0)))); } + + @Test + public void createMetricFamilySamples_WithNamespace() { + String namespace = "opencensus_"; + assertThat(PrometheusExportUtils.createMetricFamilySamples(LONG_METRIC, namespace)) + .isEqualTo( + new MetricFamilySamples( + namespace + METRIC_NAME, + Type.COUNTER, + METRIC_DESCRIPTION, + Collections.singletonList( + new Sample( + namespace + METRIC_NAME, + Arrays.asList("k1", "k2"), + Arrays.asList("v1", "v2"), + 123456789)))); + } } diff --git a/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollectorTest.java b/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollectorTest.java index 5553fe678a..1c1e13199f 100644 --- a/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollectorTest.java +++ b/exporters/stats/prometheus/src/test/java/io/opencensus/exporter/stats/prometheus/PrometheusStatsCollectorTest.java @@ -108,7 +108,8 @@ public void setUp() { @Test public void testCollect() { - PrometheusStatsCollector collector = new PrometheusStatsCollector(mockMetricProducerManager); + PrometheusStatsCollector collector = + new PrometheusStatsCollector(mockMetricProducerManager, ""); assertThat(collector.collect()) .containsExactly( new MetricFamilySamples( @@ -151,24 +152,40 @@ public void testCollect() { @Test public void testCollect_SkipDistributionMetricWithLeLabelKey() { doReturn(Collections.singletonList(LE_LABEL_METRIC)).when(mockMetricProducer).getMetrics(); - PrometheusStatsCollector collector = new PrometheusStatsCollector(mockMetricProducerManager); + PrometheusStatsCollector collector = + new PrometheusStatsCollector(mockMetricProducerManager, ""); assertThat(collector.collect()).isEmpty(); } @Test public void testDescribe() { - PrometheusStatsCollector collector = new PrometheusStatsCollector(mockMetricProducerManager); + PrometheusStatsCollector collector = + new PrometheusStatsCollector(mockMetricProducerManager, ""); assertThat(collector.describe()) .containsExactly( new MetricFamilySamples( METRIC_NAME, Type.HISTOGRAM, METRIC_DESCRIPTION, Collections.emptyList())); } + @Test + public void testDescribe_WithNamespace() { + String namespace = "myorg"; + PrometheusStatsCollector collector = + new PrometheusStatsCollector(mockMetricProducerManager, namespace); + assertThat(collector.describe()) + .containsExactly( + new MetricFamilySamples( + namespace + '_' + METRIC_NAME, + Type.HISTOGRAM, + METRIC_DESCRIPTION, + Collections.emptyList())); + } + @Test public void testCollect_WithNoopViewManager() { PrometheusStatsCollector collector = new PrometheusStatsCollector( - ExportComponent.newNoopExportComponent().getMetricProducerManager()); + ExportComponent.newNoopExportComponent().getMetricProducerManager(), ""); assertThat(collector.collect()).isEmpty(); } @@ -176,7 +193,7 @@ public void testCollect_WithNoopViewManager() { public void testDescribe_WithNoopViewManager() { PrometheusStatsCollector collector = new PrometheusStatsCollector( - ExportComponent.newNoopExportComponent().getMetricProducerManager()); + ExportComponent.newNoopExportComponent().getMetricProducerManager(), ""); assertThat(collector.describe()).isEmpty(); } }