diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt index 0dd120b12da..99d1e9e4939 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt @@ -1,6 +1,8 @@ Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions OpenTelemetry.Exporter.PrometheusAspNetCoreOptions +OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.DisableTotalNameSuffixForCounters.get -> bool +OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.DisableTotalNameSuffixForCounters.set -> void OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.PrometheusAspNetCoreOptions() -> void OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.ScrapeEndpointPath.get -> string OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.ScrapeEndpointPath.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index ba6c9ba7024..56749e55689 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added option to disable _total suffix addition to counter metrics + ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) + * Export OpenMetrics format from Prometheus exporters ([#5107](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5107)) * For requests with OpenMetrics format, scope info is automatically added ([#5086](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5086) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs index dd40405599c..75acb565175 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs @@ -17,6 +17,15 @@ public class PrometheusAspNetCoreOptions /// public string ScrapeEndpointPath { get; set; } = DefaultScrapeEndpointPath; + /// + /// Gets or sets a value indicating whether addition of _total suffix for counter metric names is disabled. Default value: . + /// + public bool DisableTotalNameSuffixForCounters + { + get => this.ExporterOptions.DisableTotalNameSuffixForCounters; + set => this.ExporterOptions.DisableTotalNameSuffixForCounters = value; + } + /// /// Gets or sets the cache duration in milliseconds for scrape responses. Default value: 300. /// diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt index 9bc2e72461d..d05f12424ea 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ OpenTelemetry.Exporter.PrometheusHttpListenerOptions +OpenTelemetry.Exporter.PrometheusHttpListenerOptions.DisableTotalNameSuffixForCounters.get -> bool +OpenTelemetry.Exporter.PrometheusHttpListenerOptions.DisableTotalNameSuffixForCounters.set -> void OpenTelemetry.Exporter.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection OpenTelemetry.Exporter.PrometheusHttpListenerOptions.UriPrefixes.set -> void OpenTelemetry.Exporter.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 279e18d90c2..8deaf5e9a56 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added option to disable _total suffix addition to counter metrics + ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) + * Export OpenMetrics format from Prometheus exporters ([#5107](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5107)) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index b8b4f4888ae..d92c48f1f2d 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -283,7 +283,7 @@ private PrometheusMetric GetPrometheusMetric(Metric metric) // Optimize writing metrics with bounded cache that has pre-calculated Prometheus names. if (!this.metricsCache.TryGetValue(metric, out var prometheusMetric)) { - prometheusMetric = PrometheusMetric.Create(metric); + prometheusMetric = PrometheusMetric.Create(metric, this.exporter.DisableTotalNameSuffixForCounters); // Add to the cache if there is space. if (this.metricsCacheCount < MaxCachedMetrics) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs index b97d7e5d7f6..578d0329ada 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs @@ -25,6 +25,7 @@ public PrometheusExporter(PrometheusExporterOptions options) Guard.ThrowIfNull(options); this.ScrapeResponseCacheDurationMilliseconds = options.ScrapeResponseCacheDurationMilliseconds; + this.DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters; this.CollectionManager = new PrometheusCollectionManager(this); } @@ -50,6 +51,8 @@ internal Func, ExportResult> OnExport internal int ScrapeResponseCacheDurationMilliseconds { get; } + internal bool DisableTotalNameSuffixForCounters { get; } + internal bool OpenMetricsRequested { get; set; } /// diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs index e52f262f67f..ec14b88b182 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs @@ -28,4 +28,9 @@ public int ScrapeResponseCacheDurationMilliseconds this.scrapeResponseCacheDurationMilliseconds = value; } } + + /// + /// Gets or sets a value indicating whether addition of _total suffix for counter metric names is disabled. Default value: . + /// + public bool DisableTotalNameSuffixForCounters { get; set; } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs index 6e93552f951..a39a426136a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs @@ -19,7 +19,7 @@ UpDownCounter becomes gauge PrometheusType.Untyped, PrometheusType.Counter, PrometheusType.Gauge, PrometheusType.Summary, PrometheusType.Histogram, PrometheusType.Histogram, PrometheusType.Histogram, PrometheusType.Histogram, PrometheusType.Gauge, }; - public PrometheusMetric(string name, string unit, PrometheusType type) + public PrometheusMetric(string name, string unit, PrometheusType type, bool disableTotalNameSuffixForCounters) { // The metric name is // required to match the regex: `[a-zA-Z_:]([a-zA-Z0-9_:])*`. Invalid characters @@ -48,7 +48,7 @@ public PrometheusMetric(string name, string unit, PrometheusType type) // If the metric name for monotonic Sum metric points does not end in a suffix of `_total` a suffix of `_total` MUST be added by default, otherwise the name MUST remain unchanged. // Exporters SHOULD provide a configuration option to disable the addition of `_total` suffixes. // https://github.com/open-telemetry/opentelemetry-specification/blob/b2f923fb1650dde1f061507908b834035506a796/specification/compatibility/prometheus_and_openmetrics.md#L286 - if (type == PrometheusType.Counter && !sanitizedName.EndsWith("_total")) + if (type == PrometheusType.Counter && !sanitizedName.EndsWith("_total") && !disableTotalNameSuffixForCounters) { sanitizedName += "_total"; } @@ -71,9 +71,9 @@ public PrometheusMetric(string name, string unit, PrometheusType type) public PrometheusType Type { get; } - public static PrometheusMetric Create(Metric metric) + public static PrometheusMetric Create(Metric metric, bool disableTotalNameSuffixForCounters) { - return new PrometheusMetric(metric.Name, metric.Unit, GetPrometheusType(metric)); + return new PrometheusMetric(metric.Name, metric.Unit, GetPrometheusType(metric), disableTotalNameSuffixForCounters); } internal static string SanitizeMetricName(string metricName) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs index 95e6b001072..929774a11f9 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs @@ -65,7 +65,11 @@ public static MeterProviderBuilder AddPrometheusHttpListener( private static MetricReader BuildPrometheusHttpListenerMetricReader( PrometheusHttpListenerOptions options) { - var exporter = new PrometheusExporter(new PrometheusExporterOptions { ScrapeResponseCacheDurationMilliseconds = 0 }); + var exporter = new PrometheusExporter(new PrometheusExporterOptions + { + ScrapeResponseCacheDurationMilliseconds = 0, + DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters, + }); var reader = new BaseExportingMetricReader(exporter) { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index 959de162ddd..d0c6bd2edf0 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -17,6 +17,11 @@ public class PrometheusHttpListenerOptions /// public string ScrapeEndpointPath { get; set; } = "/metrics"; + /// + /// Gets or sets a value indicating whether addition of _total suffix for counter metric names is disabled. Default value: . + /// + public bool DisableTotalNameSuffixForCounters { get; set; } + /// /// Gets or sets the URI (Uniform Resource Identifier) prefixes to use for the http listener. /// Default value: ["http://localhost:9464/"]. diff --git a/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs b/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs index 347f9061254..caeda40f44c 100644 --- a/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs +++ b/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs @@ -66,7 +66,7 @@ private PrometheusMetric GetPrometheusMetric(Metric metric) { if (!this.cache.TryGetValue(metric, out var prometheusMetric)) { - prometheusMetric = PrometheusMetric.Create(metric); + prometheusMetric = PrometheusMetric.Create(metric, false); this.cache[metric] = prometheusMetric; } diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusMetricTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusMetricTests.cs index 362356d4a22..81ce3e1f7da 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusMetricTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusMetricTests.cs @@ -118,84 +118,97 @@ public void Unit_AnnotationMismatch_Close() [Fact] public void Name_SpecialCaseGuage_AppendRatio() { - AssertName("sample", "1", PrometheusType.Gauge, "sample_ratio"); + AssertName("sample", "1", PrometheusType.Gauge, false, "sample_ratio"); } [Fact] public void Name_GuageWithUnit_NoAppendRatio() { - AssertName("sample", "unit", PrometheusType.Gauge, "sample_unit"); + AssertName("sample", "unit", PrometheusType.Gauge, false, "sample_unit"); } [Fact] public void Name_SpecialCaseCounter_AppendTotal() { - AssertName("sample", "unit", PrometheusType.Counter, "sample_unit_total"); + AssertName("sample", "unit", PrometheusType.Counter, false, "sample_unit_total"); } [Fact] public void Name_SpecialCaseCounterWithoutUnit_DropUnitAppendTotal() { - AssertName("sample", "1", PrometheusType.Counter, "sample_total"); + AssertName("sample", "1", PrometheusType.Counter, false, "sample_total"); + } + + [Fact] + public void Name_DisableTotalSuffixAddition_TotalNotAppended() + { + AssertName("sample", "1", PrometheusType.Counter, true, "sample"); + } + + [Fact] + public void Name_TotalSuffixAlreadyPresent_DisableTotalSuffixAddition_TotalNotRemoved() + { + AssertName("sample_total", "1", PrometheusType.Counter, true, "sample_total"); } [Fact] public void Name_SpecialCaseCounterWithNumber_AppendTotal() { - AssertName("sample", "2", PrometheusType.Counter, "sample_2_total"); + AssertName("sample", "2", PrometheusType.Counter, false, "sample_2_total"); } [Fact] public void Name_UnsupportedMetricNameChars_Drop() { - AssertName("s%%ple", "%/m", PrometheusType.Summary, "s_ple_percent_per_minute"); + AssertName("s%%ple", "%/m", PrometheusType.Summary, false, "s_ple_percent_per_minute"); } [Fact] public void Name_UnitOtherThanOne_Normal() { - AssertName("metric_name", "2", PrometheusType.Summary, "metric_name_2"); + AssertName("metric_name", "2", PrometheusType.Summary, false, "metric_name_2"); } [Fact] public void Name_UnitAlreadyPresentInName_NotAppended() { - AssertName("metric_name_total", "total", PrometheusType.Counter, "metric_name_total"); + AssertName("metric_name_total", "total", PrometheusType.Counter, false, "metric_name_total"); } [Fact] public void Name_UnitAlreadyPresentInName_TotalNonCounterType_NotAppended() { - AssertName("metric_name_total", "total", PrometheusType.Summary, "metric_name_total"); + AssertName("metric_name_total", "total", PrometheusType.Summary, false, "metric_name_total"); } [Fact] public void Name_UnitAlreadyPresentInName_CustomGauge_NotAppended() { - AssertName("metric_hertz", "hertz", PrometheusType.Gauge, "metric_hertz"); + AssertName("metric_hertz", "hertz", PrometheusType.Gauge, false, "metric_hertz"); } [Fact] public void Name_UnitAlreadyPresentInName_CustomCounter_NotAppended() { - AssertName("metric_hertz_total", "hertz_total", PrometheusType.Counter, "metric_hertz_total"); + AssertName("metric_hertz_total", "hertz_total", PrometheusType.Counter, false, "metric_hertz_total"); } [Fact] public void Name_UnitAlreadyPresentInName_OrderMatters_Appended() { - AssertName("metric_total_hertz", "hertz_total", PrometheusType.Counter, "metric_total_hertz_hertz_total"); + AssertName("metric_total_hertz", "hertz_total", PrometheusType.Counter, false, "metric_total_hertz_hertz_total"); } [Fact] public void Name_StartWithNumber_UnderscoreStart() { - AssertName("2_metric_name", "By", PrometheusType.Summary, "_metric_name_bytes"); + AssertName("2_metric_name", "By", PrometheusType.Summary, false, "_metric_name_bytes"); } - private static void AssertName(string name, string unit, PrometheusType type, string expected) + private static void AssertName( + string name, string unit, PrometheusType type, bool disableTotalNameSuffixForCounters, string expected) { - var prometheusMetric = new PrometheusMetric(name, unit, type); + var prometheusMetric = new PrometheusMetric(name, unit, type, disableTotalNameSuffixForCounters); Assert.Equal(expected, prometheusMetric.Name); } diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 55c4412b27e..7c4a95b05f4 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -655,6 +655,6 @@ public void HistogramOneDimensionWithScopeVersion() private static int WriteMetric(byte[] buffer, int cursor, Metric metric, bool useOpenMetrics = false) { - return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric), useOpenMetrics); + return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric, false), useOpenMetrics); } }