From 05aee52517084a8b1493af20b4721cc9f3a86b5f Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Fri, 10 Oct 2025 23:42:12 -0400 Subject: [PATCH 01/11] add disableTimestamp option in serializer with unit tests --- .../Internal/PrometheusSerializerExt.cs | 22 ++- .../PrometheusSerializerTests.cs | 146 ++++++++++-------- 2 files changed, 98 insertions(+), 70 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs index 70d67f468bf..a952a08816d 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs @@ -22,7 +22,7 @@ public static bool CanWriteMetric(Metric metric) return true; } - public static int WriteMetric(byte[] buffer, int cursor, Metric metric, PrometheusMetric prometheusMetric, bool openMetricsRequested = false) + public static int WriteMetric(byte[] buffer, int cursor, Metric metric, PrometheusMetric prometheusMetric, bool openMetricsRequested = false, bool disableTimestamp = false) { cursor = WriteTypeMetadata(buffer, cursor, prometheusMetric, openMetricsRequested); cursor = WriteUnitMetadata(buffer, cursor, prometheusMetric, openMetricsRequested); @@ -68,7 +68,10 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe buffer[cursor++] = unchecked((byte)' '); - cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + if (!disableTimestamp) + { + cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + } buffer[cursor++] = ASCII_LINEFEED; } @@ -105,7 +108,10 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe cursor = WriteLong(buffer, cursor, totalCount); buffer[cursor++] = unchecked((byte)' '); - cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + if (!disableTimestamp) + { + cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + } buffer[cursor++] = ASCII_LINEFEED; } @@ -120,7 +126,10 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe cursor = WriteDouble(buffer, cursor, metricPoint.GetHistogramSum()); buffer[cursor++] = unchecked((byte)' '); - cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + if (!disableTimestamp) + { + cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + } buffer[cursor++] = ASCII_LINEFEED; @@ -134,7 +143,10 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe cursor = WriteLong(buffer, cursor, metricPoint.GetHistogramCount()); buffer[cursor++] = unchecked((byte)' '); - cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + if (!disableTimestamp) + { + cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); + } buffer[cursor++] = ASCII_LINEFEED; } diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 2864fe59b5c..43c97126cc5 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -11,8 +11,10 @@ namespace OpenTelemetry.Exporter.Prometheus.Tests; public sealed class PrometheusSerializerTests { - [Fact] - public void GaugeZeroDimension() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void GaugeZeroDimension(bool disableTimestamp) { var buffer = new byte[85000]; var metrics = new List(); @@ -27,13 +29,15 @@ public void GaugeZeroDimension() provider.ForceFlush(); - var cursor = WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( + var cursor = WriteMetric(buffer, 0, metrics[0], false, disableTimestamp); + var timestampPart = disableTimestamp ? string.Empty : "\\d+"; + var output = Encoding.UTF8.GetString(buffer, 0, cursor); + var expected = ("^" + "# TYPE test_gauge gauge\n" - + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123 {timestampPart}\n" + + "$").Replace('\'', '"'); + Assert.Matches(expected, output); } [Fact] @@ -255,8 +259,10 @@ public void SumNonMonotonicDouble() Encoding.UTF8.GetString(buffer, 0, cursor)); } - [Fact] - public void HistogramZeroDimension() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void HistogramZeroDimension(bool disableTimestamp) { var buffer = new byte[85000]; var metrics = new List(); @@ -273,30 +279,32 @@ public void HistogramZeroDimension() provider.ForceFlush(); - var cursor = WriteMetric(buffer, 0, metrics[0]); - Assert.Matches( + var cursor = WriteMetric(buffer, 0, metrics[0], false, disableTimestamp); + var timestampPart = disableTimestamp ? string.Empty : "\\d+"; + var output = Encoding.UTF8.GetString(buffer, 0, cursor); + var expected = ("^" + "# TYPE test_histogram histogram\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='0'}} 0 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5'}} 0 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10'}} 0 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='25'}} 1 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='50'}} 1 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='75'}} 1 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='100'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='250'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='500'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='750'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='1000'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='2500'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5000'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='7500'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10000'}} 2 \\d+\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='\\+Inf'}} 2 \\d+\n" - + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 118 \\d+\n" - + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 2 \\d+\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='0'}} 0 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5'}} 0 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10'}} 0 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='25'}} 1 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='50'}} 1 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='75'}} 1 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='100'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='250'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='500'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='750'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='1000'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='2500'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5000'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='7500'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10000'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='\\+Inf'}} 2 {timestampPart}\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 118 {timestampPart}\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 2 {timestampPart}\n" + + "$").Replace('\'', '"'); + Assert.Matches(expected, output); } [Fact] @@ -499,8 +507,10 @@ public void ExponentialHistogramIsIgnoredForNow() Assert.False(PrometheusSerializer.CanWriteMetric(metrics[0])); } - [Fact] - public void SumWithOpenMetricsFormat() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void SumWithOpenMetricsFormat(bool disableTimestamp) { var buffer = new byte[85000]; var metrics = new List(); @@ -517,17 +527,21 @@ public void SumWithOpenMetricsFormat() provider.ForceFlush(); - var cursor = WriteMetric(buffer, 0, metrics[0], true); - Assert.Matches( + var cursor = WriteMetric(buffer, 0, metrics[0], true, disableTimestamp); + var timestampPart = disableTimestamp ? string.Empty : "\\d+\\.\\d{3}"; + var output = Encoding.UTF8.GetString(buffer, 0, cursor); + var expected = ("^" - + "# TYPE test_updown_counter gauge\n" - + $"test_updown_counter{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1 \\d+\\.\\d{{3}}\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); + + "# TYPE test_updown_counter gauge\n" + + $"test_updown_counter{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1 {timestampPart}\n" + + "$").Replace('\'', '"'); + Assert.Matches(expected, output); } - [Fact] - public void HistogramOneDimensionWithOpenMetricsFormat() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void HistogramOneDimensionWithOpenMetricsFormat(bool disableTimestamp) { var buffer = new byte[85000]; var metrics = new List(); @@ -544,30 +558,32 @@ public void HistogramOneDimensionWithOpenMetricsFormat() provider.ForceFlush(); - var cursor = WriteMetric(buffer, 0, metrics[0], true); - Assert.Matches( + var cursor = WriteMetric(buffer, 0, metrics[0], true, disableTimestamp); + var timestampPart = disableTimestamp ? string.Empty : "\\d+\\.\\d{3}"; + var output = Encoding.UTF8.GetString(buffer, 0, cursor); + var expected = ("^" + "# TYPE test_histogram histogram\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='0'}} 0 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5'}} 0 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10'}} 0 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='25'}} 1 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='50'}} 1 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='75'}} 1 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='100'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='250'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='500'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='750'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='1000'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='2500'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5000'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='7500'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10000'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='\\+Inf'}} 2 \\d+\\.\\d{{3}}\n" - + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 118 \\d+\\.\\d{{3}}\n" - + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 2 \\d+\\.\\d{{3}}\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='0'}} 0 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5'}} 0 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10'}} 0 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='25'}} 1 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='50'}} 1 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='75'}} 1 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='100'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='250'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='500'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='750'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='1000'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='2500'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5000'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='7500'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10000'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='\\+Inf'}} 2 {timestampPart}\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 118 {timestampPart}\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 2 {timestampPart}\n" + + "$").Replace('\'', '"'); + Assert.Matches(expected, output); } [Fact] @@ -653,8 +669,8 @@ public void HistogramOneDimensionWithScopeVersion() Encoding.UTF8.GetString(buffer, 0, cursor)); } - private static int WriteMetric(byte[] buffer, int cursor, Metric metric, bool useOpenMetrics = false) + private static int WriteMetric(byte[] buffer, int cursor, Metric metric, bool useOpenMetrics = false, bool disableTimestamp = false) { - return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric, false), useOpenMetrics); + return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric, false), useOpenMetrics, disableTimestamp); } } From 896fd2c9993211dbd1bc445de218a749f8b721b0 Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Fri, 10 Oct 2025 23:44:07 -0400 Subject: [PATCH 02/11] add disableTimestamp option for prometheus httpListener and AspNetCore --- .../.publicApi/PublicAPI.Unshipped.txt | 2 ++ .../PrometheusAspNetCoreOptions.cs | 9 +++++++++ .../.publicApi/PublicAPI.Unshipped.txt | 2 ++ .../Internal/PrometheusCollectionManager.cs | 3 ++- .../Internal/PrometheusExporter.cs | 3 +++ .../Internal/PrometheusExporterOptions.cs | 5 +++++ ...ometheusHttpListenerMeterProviderBuilderExtensions.cs | 1 + .../PrometheusHttpListenerOptions.cs | 5 +++++ 8 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt index 92eaa1e4111..043f62a17a9 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.DisableTimestamp.get -> bool +OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.DisableTimestamp.set -> void OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.DisableTotalNameSuffixForCounters.get -> bool OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.DisableTotalNameSuffixForCounters.set -> void OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.PrometheusAspNetCoreOptions() -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs index ed8186ec88d..171eb465692 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs @@ -38,5 +38,14 @@ public int ScrapeResponseCacheDurationMilliseconds set => this.ExporterOptions.ScrapeResponseCacheDurationMilliseconds = value; } + /// + /// Gets or sets a value indicating whether timestamps should be disabled. Default value: . + /// + public bool DisableTimestamp + { + get => this.ExporterOptions.DisableTimestamp; + set => this.ExporterOptions.DisableTimestamp = value; + } + internal PrometheusExporterOptions ExporterOptions { get; } = new(); } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt index 6caa1a77cb1..043ec1010ae 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.DisableTimestamp.get -> bool +OpenTelemetry.Exporter.PrometheusHttpListenerOptions.DisableTimestamp.set -> void OpenTelemetry.Exporter.PrometheusHttpListenerOptions.DisableTotalNameSuffixForCounters.get -> bool OpenTelemetry.Exporter.PrometheusHttpListenerOptions.DisableTotalNameSuffixForCounters.set -> void OpenTelemetry.Exporter.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection! diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index ce4c5384485..8e50d1c1da9 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -275,7 +275,8 @@ private ExportResult OnCollect(in Batch metrics) cursor, metric, this.GetPrometheusMetric(metric), - this.exporter.OpenMetricsRequested); + this.exporter.OpenMetricsRequested, + this.exporter.DisableTimestamp); break; } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs index 2d9ea4c27d1..f1cca1d3c19 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs @@ -27,6 +27,7 @@ public PrometheusExporter(PrometheusExporterOptions options) this.ScrapeResponseCacheDurationMilliseconds = options.ScrapeResponseCacheDurationMilliseconds; this.DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters; + this.DisableTimestamp = options.DisableTimestamp; this.CollectionManager = new PrometheusCollectionManager(this); } @@ -50,6 +51,8 @@ public PrometheusExporter(PrometheusExporterOptions options) internal bool OpenMetricsRequested { get; set; } + internal bool DisableTimestamp { get; set; } + internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); /// diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs index ec14b88b182..9f2f03bb5c9 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs @@ -33,4 +33,9 @@ public int ScrapeResponseCacheDurationMilliseconds /// 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 a value indicating whether timestamps should be disabled. Default value: . + /// + public bool DisableTimestamp { get; set; } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs index 94eea0f5742..508d4358e58 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs @@ -69,6 +69,7 @@ private static BaseExportingMetricReader BuildPrometheusHttpListenerMetricReader { ScrapeResponseCacheDurationMilliseconds = 0, DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters, + DisableTimestamp = options.DisableTimestamp, }); var reader = new BaseExportingMetricReader(exporter) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index 8909a3bd82e..581f42f63a6 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -24,6 +24,11 @@ public class PrometheusHttpListenerOptions /// public bool DisableTotalNameSuffixForCounters { get; set; } + /// + /// Gets or sets a value indicating whether timestamps should be disabled. Default value: . + /// + public bool DisableTimestamp { get; set; } + /// /// Gets or sets the URI (Uniform Resource Identifier) prefixes to use for the http listener. /// Default value: ["http://localhost:9464/"]. From 8d443d86f598d68d05303284af93a96ee40c9355 Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Sat, 11 Oct 2025 22:48:57 -0400 Subject: [PATCH 03/11] add disableTimestamp flag tests for Prometheus httpListener and AspNetCore --- .../PrometheusExporterMiddlewareTests.cs | 31 +++++++++++++------ .../PrometheusHttpListenerTests.cs | 29 ++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index 98d503e8e92..113edace584 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -51,13 +51,19 @@ public Task PrometheusExporterMiddlewareIntegration_OptionsFallback() services => services.Configure(o => o.ScrapeEndpointPath = null)); } - [Fact] - public Task PrometheusExporterMiddlewareIntegration_OptionsViaAddPrometheusExporter() + [Theory] + [InlineData(true)] + [InlineData(false)] + public Task PrometheusExporterMiddlewareIntegration_OptionsViaAddPrometheusExporter(bool disableTimestamp) { return RunPrometheusExporterMiddlewareIntegrationTest( "/metrics_from_AddPrometheusExporter", app => app.UseOpenTelemetryPrometheusScrapingEndpoint(), - configureOptions: o => o.ScrapeEndpointPath = "/metrics_from_AddPrometheusExporter"); + configureOptions: o => + { + o.ScrapeEndpointPath = "/metrics_from_AddPrometheusExporter"; + o.DisableTimestamp = disableTimestamp; + }); } [Fact] @@ -407,7 +413,9 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( if (!skipMetrics) { - await VerifyAsync(beginTimestamp, endTimestamp, response, requestOpenMetrics, meterTags); + var options = new PrometheusAspNetCoreOptions(); + configureOptions?.Invoke(options); + await VerifyAsync(beginTimestamp, endTimestamp, response, requestOpenMetrics, meterTags, options.DisableTimestamp); } else { @@ -419,7 +427,7 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( await host.StopAsync(); } - private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, HttpResponseMessage response, bool requestOpenMetrics, KeyValuePair[]? meterTags) + private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, HttpResponseMessage response, bool requestOpenMetrics, KeyValuePair[]? meterTags, bool disableTimestamp = false) { Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.True(response.Content.Headers.Contains("Last-Modified")); @@ -439,6 +447,8 @@ private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, Ht string content = (await response.Content.ReadAsStringAsync()).ReplaceLineEndings(); + var timestampPart = disableTimestamp ? string.Empty : "(\\d+)"; + var timestampPartOpenMetrics = disableTimestamp ? string.Empty : "(\\d+\\.\\d{3})"; string expected = requestOpenMetrics ? $$""" # TYPE target info @@ -449,14 +459,14 @@ private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, Ht otel_scope_info{otel_scope_name="{{MeterName}}"} 1 # TYPE counter_double_bytes counter # UNIT counter_double_bytes bytes - counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17 (\d+\.\d{3}) + counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17 {{timestampPartOpenMetrics}} # EOF """.ReplaceLineEndings() : $$""" # TYPE counter_double_bytes_total counter # UNIT counter_double_bytes_total bytes - counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17 (\d+) + counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17 {{timestampPart}} # EOF """.ReplaceLineEndings(); @@ -465,9 +475,12 @@ private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, Ht Assert.True(matches.Count == 1, content); - var timestamp = long.Parse(matches[0].Groups[1].Value.Replace(".", string.Empty, StringComparison.Ordinal), CultureInfo.InvariantCulture); + if (!disableTimestamp) + { + var timestamp = long.Parse(matches[0].Groups[1].Value.Replace(".", string.Empty, StringComparison.Ordinal), CultureInfo.InvariantCulture); - Assert.True(beginTimestamp <= timestamp && timestamp <= endTimestamp, $"{beginTimestamp} {timestamp} {endTimestamp}"); + Assert.True(beginTimestamp <= timestamp && timestamp <= endTimestamp, $"{beginTimestamp} {timestamp} {endTimestamp}"); + } } private static Task StartTestHostAsync( diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index 7ff58940692..6394fc8295a 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -61,10 +61,12 @@ public void UriPrefixesInvalid() }); } - [Fact] - public async Task PrometheusExporterHttpServerIntegration() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task PrometheusExporterHttpServerIntegration(bool disableTimestamp) { - await RunPrometheusExporterHttpServerIntegrationTest(); + await RunPrometheusExporterHttpServerIntegrationTest(disableTimestamp: disableTimestamp); } [Fact] @@ -73,10 +75,12 @@ public async Task PrometheusExporterHttpServerIntegration_NoMetrics() await RunPrometheusExporterHttpServerIntegrationTest(skipMetrics: true); } - [Fact] - public async Task PrometheusExporterHttpServerIntegration_NoOpenMetrics() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task PrometheusExporterHttpServerIntegration_NoOpenMetrics(bool disableTimestamp) { - await RunPrometheusExporterHttpServerIntegrationTest(acceptHeader: string.Empty); + await RunPrometheusExporterHttpServerIntegrationTest(acceptHeader: string.Empty, disableTimestamp: disableTimestamp); } [Fact] @@ -220,7 +224,7 @@ private static void TestPrometheusHttpListenerUriPrefixOptions(string[] uriPrefi }); } - private static MeterProvider BuildMeterProvider(Meter meter, IEnumerable> attributes, out string address) + private static MeterProvider BuildMeterProvider(Meter meter, IEnumerable> attributes, out string address, bool disableTimestamp = false) { Random random = new Random(); int retryAttempts = 5; @@ -242,6 +246,7 @@ private static MeterProvider BuildMeterProvider(Meter meter, IEnumerable { options.UriPrefixes = [generatedAddress]; + options.DisableTimestamp = disableTimestamp; }) .Build(); @@ -258,13 +263,13 @@ private static MeterProvider BuildMeterProvider(Meter meter, IEnumerable[]? meterTags = null) + private static async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetrics = false, string acceptHeader = "application/openmetrics-text", KeyValuePair[]? meterTags = null, bool disableTimestamp = false) { var requestOpenMetrics = acceptHeader.StartsWith("application/openmetrics-text", StringComparison.Ordinal); using var meter = new Meter(MeterName, MeterVersion, meterTags); - var provider = BuildMeterProvider(meter, [], out var address); + var provider = BuildMeterProvider(meter, [], out var address, disableTimestamp); var counterTags = new KeyValuePair[] { @@ -308,6 +313,8 @@ private static async Task RunPrometheusExporterHttpServerIntegrationTest(bool sk var content = await response.Content.ReadAsStringAsync(); + var timestampPart = disableTimestamp ? string.Empty : "(\\d+)"; + var timestampPartOpenMetrics = disableTimestamp ? string.Empty : "(\\d+\\.\\d{3})"; var expected = requestOpenMetrics ? "# TYPE target info\n" + "# HELP target Target metadata\n" @@ -317,11 +324,11 @@ private static async Task RunPrometheusExporterHttpServerIntegrationTest(bool sk + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + "# TYPE counter_double_bytes counter\n" + "# UNIT counter_double_bytes bytes\n" - + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17 {timestampPartOpenMetrics}\n" + "# EOF\n" : "# TYPE counter_double_bytes_total counter\n" + "# UNIT counter_double_bytes_total bytes\n" - + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17 (\\d+)\n" + + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17 {timestampPart}\n" + "# EOF\n"; Assert.Matches(("^" + expected + "$").Replace('\'', '"'), content); From b4a7d190664d47103b5306b864603d87d59502e2 Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Sun, 12 Oct 2025 22:20:57 -0400 Subject: [PATCH 04/11] added changelog entries --- src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md | 3 +++ .../CHANGELOG.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 13d43338279..16175f0ce04 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -7,6 +7,9 @@ Notes](../../RELEASENOTES.md). ## Unreleased +* Added the possibility to disable timestamps via the `PrometheusAspNetCoreOptions`. + ([#6600](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6600)) + * **Breaking Change** When targeting `net8.0`, the package now depends on version `8.0.0` of the `Microsoft.Extensions.DependencyInjection.Abstractions`, `Microsoft.Extensions.Diagnostics.Abstractions` and diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index c293fec214f..a6bd5ca03bf 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -7,6 +7,9 @@ Notes](../../RELEASENOTES.md). ## Unreleased +* Added the possibility to disable timestamps via the `PrometheusHttpListenerOptions`. + ([#6600](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6600)) + * **Breaking Change** When targeting `net8.0`, the package now depends on version `8.0.0` of the `Microsoft.Extensions.DependencyInjection.Abstractions`, `Microsoft.Extensions.Diagnostics.Abstractions` and From 50236d5d7368627333a2b232c4d2d0bf6ad82ed1 Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Tue, 14 Oct 2025 11:53:43 -0400 Subject: [PATCH 05/11] remove space if no timestamp is used --- .../CHANGELOG.md | 6 +++--- .../CHANGELOG.md | 6 +++--- .../Internal/PrometheusSerializerExt.cs | 9 ++++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 16175f0ce04..061bea516e0 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -7,15 +7,15 @@ Notes](../../RELEASENOTES.md). ## Unreleased -* Added the possibility to disable timestamps via the `PrometheusAspNetCoreOptions`. - ([#6600](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6600)) - * **Breaking Change** When targeting `net8.0`, the package now depends on version `8.0.0` of the `Microsoft.Extensions.DependencyInjection.Abstractions`, `Microsoft.Extensions.Diagnostics.Abstractions` and `Microsoft.Extensions.Logging.Configuration` NuGet packages. ([#6327](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6327)) +* Added the possibility to disable timestamps via the `PrometheusAspNetCoreOptions`. + ([#6600](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6600)) + ## 1.13.1-beta.1 Released 2025-Oct-10 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index a6bd5ca03bf..34670fa41ad 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -7,15 +7,15 @@ Notes](../../RELEASENOTES.md). ## Unreleased -* Added the possibility to disable timestamps via the `PrometheusHttpListenerOptions`. - ([#6600](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6600)) - * **Breaking Change** When targeting `net8.0`, the package now depends on version `8.0.0` of the `Microsoft.Extensions.DependencyInjection.Abstractions`, `Microsoft.Extensions.Diagnostics.Abstractions` and `Microsoft.Extensions.Logging.Configuration` NuGet packages. ([#6327](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6327)) +* Added the possibility to disable timestamps via the `PrometheusHttpListenerOptions`. + ([#6600](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6600)) + ## 1.13.1-beta.1 Released 2025-Oct-10 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs index a952a08816d..6579008933a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs @@ -66,10 +66,9 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe } } - buffer[cursor++] = unchecked((byte)' '); - if (!disableTimestamp) { + buffer[cursor++] = unchecked((byte)' '); cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); } @@ -106,10 +105,10 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe cursor = WriteAsciiStringNoEscape(buffer, cursor, "\"} "); cursor = WriteLong(buffer, cursor, totalCount); - buffer[cursor++] = unchecked((byte)' '); if (!disableTimestamp) { + buffer[cursor++] = unchecked((byte)' '); cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); } @@ -124,10 +123,10 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe buffer[cursor++] = unchecked((byte)' '); cursor = WriteDouble(buffer, cursor, metricPoint.GetHistogramSum()); - buffer[cursor++] = unchecked((byte)' '); if (!disableTimestamp) { + buffer[cursor++] = unchecked((byte)' '); cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); } @@ -141,10 +140,10 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe buffer[cursor++] = unchecked((byte)' '); cursor = WriteLong(buffer, cursor, metricPoint.GetHistogramCount()); - buffer[cursor++] = unchecked((byte)' '); if (!disableTimestamp) { + buffer[cursor++] = unchecked((byte)' '); cursor = WriteTimestamp(buffer, cursor, timestamp, openMetricsRequested); } From 0cd9dd7345e4e75ceab77086cbea38390fd8b44d Mon Sep 17 00:00:00 2001 From: Hannah Haering <157852144+hannahhaering@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:58:36 -0400 Subject: [PATCH 06/11] Update test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotr Kiełkowicz --- .../PrometheusSerializerTests.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 43c97126cc5..51af8b7018f 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -285,24 +285,24 @@ public void HistogramZeroDimension(bool disableTimestamp) var expected = ("^" + "# TYPE test_histogram histogram\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='0'}} 0 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5'}} 0 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10'}} 0 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='25'}} 1 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='50'}} 1 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='75'}} 1 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='100'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='250'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='500'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='750'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='1000'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='2500'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5000'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='7500'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10000'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='\\+Inf'}} 2 {timestampPart}\n" - + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 118 {timestampPart}\n" - + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='0'}} 0{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5'}} 0{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10'}} 0{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='25'}} 1{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='50'}} 1{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='75'}} 1{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='100'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='250'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='500'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='750'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='1000'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='2500'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='5000'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='7500'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='10000'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',le='\\+Inf'}} 2{timestampPart}\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 118{timestampPart}\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 2{timestampPart}\n" + "$").Replace('\'', '"'); Assert.Matches(expected, output); } From 5ec336d80cc30a028401b534fbb34458b49b90a0 Mon Sep 17 00:00:00 2001 From: Hannah Haering <157852144+hannahhaering@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:58:54 -0400 Subject: [PATCH 07/11] Update src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotr Kiełkowicz --- .../Internal/PrometheusSerializerExt.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs index 6579008933a..613f7acf4d7 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs @@ -22,7 +22,7 @@ public static bool CanWriteMetric(Metric metric) return true; } - public static int WriteMetric(byte[] buffer, int cursor, Metric metric, PrometheusMetric prometheusMetric, bool openMetricsRequested = false, bool disableTimestamp = false) + public static int WriteMetric(byte[] buffer, int cursor, Metric metric, PrometheusMetric prometheusMetric, bool openMetricsRequested, bool disableTimestamp) { cursor = WriteTypeMetadata(buffer, cursor, prometheusMetric, openMetricsRequested); cursor = WriteUnitMetadata(buffer, cursor, prometheusMetric, openMetricsRequested); From b311eee0b55601f72d3e61e422e1496192e89f52 Mon Sep 17 00:00:00 2001 From: Hannah Haering <157852144+hannahhaering@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:59:05 -0400 Subject: [PATCH 08/11] Update test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotr Kiełkowicz --- .../PrometheusSerializerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 51af8b7018f..6d7e8c97f9e 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -280,7 +280,7 @@ public void HistogramZeroDimension(bool disableTimestamp) provider.ForceFlush(); var cursor = WriteMetric(buffer, 0, metrics[0], false, disableTimestamp); - var timestampPart = disableTimestamp ? string.Empty : "\\d+"; + var timestampPart = disableTimestamp ? string.Empty : " \\d+"; var output = Encoding.UTF8.GetString(buffer, 0, cursor); var expected = ("^" From f005de3f534cb908578cad34d840667807951e88 Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Tue, 14 Oct 2025 13:21:27 -0400 Subject: [PATCH 09/11] fix method call --- test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs b/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs index 47d04dd0d10..30b029625ab 100644 --- a/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs +++ b/test/Benchmarks/Exporter/PrometheusSerializerBenchmarks.cs @@ -59,7 +59,7 @@ public void WriteMetric() int cursor = 0; foreach (var metric in this.metrics) { - cursor = PrometheusSerializer.WriteMetric(this.buffer, cursor, metric, this.GetPrometheusMetric(metric)); + cursor = PrometheusSerializer.WriteMetric(this.buffer, cursor, metric, this.GetPrometheusMetric(metric), openMetricsRequested: false, disableTimestamp: false); } } } From 5fdfebba7424737892415cdcb215e7115c90e5b0 Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Tue, 14 Oct 2025 13:32:34 -0400 Subject: [PATCH 10/11] fix tests --- .../PrometheusExporterMiddlewareTests.cs | 8 ++-- .../PrometheusSerializerTests.cs | 46 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index 113edace584..c0ae96f5770 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -447,8 +447,8 @@ private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, Ht string content = (await response.Content.ReadAsStringAsync()).ReplaceLineEndings(); - var timestampPart = disableTimestamp ? string.Empty : "(\\d+)"; - var timestampPartOpenMetrics = disableTimestamp ? string.Empty : "(\\d+\\.\\d{3})"; + var timestampPart = disableTimestamp ? string.Empty : " (\\d+)"; + var timestampPartOpenMetrics = disableTimestamp ? string.Empty : " (\\d+\\.\\d{3})"; string expected = requestOpenMetrics ? $$""" # TYPE target info @@ -459,14 +459,14 @@ private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, Ht otel_scope_info{otel_scope_name="{{MeterName}}"} 1 # TYPE counter_double_bytes counter # UNIT counter_double_bytes bytes - counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17 {{timestampPartOpenMetrics}} + counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17{{timestampPartOpenMetrics}} # EOF """.ReplaceLineEndings() : $$""" # TYPE counter_double_bytes_total counter # UNIT counter_double_bytes_total bytes - counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17 {{timestampPart}} + counter_double_bytes_total{otel_scope_name="{{MeterName}}",otel_scope_version="{{MeterVersion}}",{{additionalTags}}key1="value1",key2="value2"} 101.17{{timestampPart}} # EOF """.ReplaceLineEndings(); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 6d7e8c97f9e..aa060cf2545 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -30,12 +30,12 @@ public void GaugeZeroDimension(bool disableTimestamp) provider.ForceFlush(); var cursor = WriteMetric(buffer, 0, metrics[0], false, disableTimestamp); - var timestampPart = disableTimestamp ? string.Empty : "\\d+"; + var timestampPart = disableTimestamp ? string.Empty : " \\d+"; var output = Encoding.UTF8.GetString(buffer, 0, cursor); var expected = ("^" + "# TYPE test_gauge gauge\n" - + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123 {timestampPart}\n" + + $"test_gauge{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} 123{timestampPart}\n" + "$").Replace('\'', '"'); Assert.Matches(expected, output); } @@ -528,12 +528,12 @@ public void SumWithOpenMetricsFormat(bool disableTimestamp) provider.ForceFlush(); var cursor = WriteMetric(buffer, 0, metrics[0], true, disableTimestamp); - var timestampPart = disableTimestamp ? string.Empty : "\\d+\\.\\d{3}"; + var timestampPart = disableTimestamp ? string.Empty : " \\d+\\.\\d{3}"; var output = Encoding.UTF8.GetString(buffer, 0, cursor); var expected = ("^" + "# TYPE test_updown_counter gauge\n" - + $"test_updown_counter{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1 {timestampPart}\n" + + $"test_updown_counter{{otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1{timestampPart}\n" + "$").Replace('\'', '"'); Assert.Matches(expected, output); } @@ -559,29 +559,29 @@ public void HistogramOneDimensionWithOpenMetricsFormat(bool disableTimestamp) provider.ForceFlush(); var cursor = WriteMetric(buffer, 0, metrics[0], true, disableTimestamp); - var timestampPart = disableTimestamp ? string.Empty : "\\d+\\.\\d{3}"; + var timestampPart = disableTimestamp ? string.Empty : " \\d+\\.\\d{3}"; var output = Encoding.UTF8.GetString(buffer, 0, cursor); var expected = ("^" + "# TYPE test_histogram histogram\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='0'}} 0 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5'}} 0 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10'}} 0 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='25'}} 1 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='50'}} 1 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='75'}} 1 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='100'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='250'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='500'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='750'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='1000'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='2500'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5000'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='7500'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10000'}} 2 {timestampPart}\n" - + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='\\+Inf'}} 2 {timestampPart}\n" - + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 118 {timestampPart}\n" - + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 2 {timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='0'}} 0{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5'}} 0{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10'}} 0{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='25'}} 1{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='50'}} 1{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='75'}} 1{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='100'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='250'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='500'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='750'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='1000'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='2500'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='5000'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='7500'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='10000'}} 2{timestampPart}\n" + + $"test_histogram_bucket{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1',le='\\+Inf'}} 2{timestampPart}\n" + + $"test_histogram_sum{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 118{timestampPart}\n" + + $"test_histogram_count{{otel_scope_name='{Utils.GetCurrentMethodName()}',x='1'}} 2{timestampPart}\n" + "$").Replace('\'', '"'); Assert.Matches(expected, output); } From abda976f7bd6472d15f5742a6dd5a04b7fd44883 Mon Sep 17 00:00:00 2001 From: hannahhaering Date: Tue, 14 Oct 2025 13:41:21 -0400 Subject: [PATCH 11/11] fix tests 2 --- .../PrometheusHttpListenerTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index 6394fc8295a..c584ecc64cc 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -313,8 +313,8 @@ private static async Task RunPrometheusExporterHttpServerIntegrationTest(bool sk var content = await response.Content.ReadAsStringAsync(); - var timestampPart = disableTimestamp ? string.Empty : "(\\d+)"; - var timestampPartOpenMetrics = disableTimestamp ? string.Empty : "(\\d+\\.\\d{3})"; + var timestampPart = disableTimestamp ? string.Empty : " (\\d+)"; + var timestampPartOpenMetrics = disableTimestamp ? string.Empty : " (\\d+\\.\\d{3})"; var expected = requestOpenMetrics ? "# TYPE target info\n" + "# HELP target Target metadata\n" @@ -324,11 +324,11 @@ private static async Task RunPrometheusExporterHttpServerIntegrationTest(bool sk + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + "# TYPE counter_double_bytes counter\n" + "# UNIT counter_double_bytes bytes\n" - + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17 {timestampPartOpenMetrics}\n" + + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17{timestampPartOpenMetrics}\n" + "# EOF\n" : "# TYPE counter_double_bytes_total counter\n" + "# UNIT counter_double_bytes_total bytes\n" - + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17 {timestampPart}\n" + + $"counter_double_bytes_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',{additionalTags}key1='value1',key2='value2'}} 101.17{timestampPart}\n" + "# EOF\n"; Assert.Matches(("^" + expected + "$").Replace('\'', '"'), content);