From 2583b6c65f71a27cfdad2529ab6ea9a60e26e4e2 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 13 Sep 2022 12:04:47 -0700 Subject: [PATCH 1/3] Output EOF following the OpenMetrics exposition recommendation --- .../Internal/PrometheusCollectionManager.cs | 52 ++++++++++++++----- .../Internal/PrometheusSerializer.cs | 9 ++++ .../PrometheusHttpListenerTests.cs | 2 +- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index 265031eefc..583c223491 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -191,27 +191,37 @@ private ExportResult OnCollect(Batch metrics) } catch (IndexOutOfRangeException) { - var bufferSize = this.buffer.Length * 2; - - // there are two cases we might run into the following condition: - // 1. we have many metrics to be exported - in this case we probably want - // to put some upper limit and allow the user to configure it. - // 2. we got an IndexOutOfRangeException which was triggered by some other - // code instead of the buffer[cursor++] - in this case we should give up - // at certain point rather than allocating like crazy. - if (bufferSize > 100 * 1024 * 1024) + if (!this.IncreaseBufferSize()) { + // there are two cases we might run into the following condition: + // 1. we have many metrics to be exported - in this case we probably want + // to put some upper limit and allow the user to configure it. + // 2. we got an IndexOutOfRangeException which was triggered by some other + // code instead of the buffer[cursor++] - in this case we should give up + // at certain point rather than allocating like crazy. throw; } + } + } + } - var newBuffer = new byte[bufferSize]; - this.buffer.CopyTo(newBuffer, 0); - this.buffer = newBuffer; + while (true) + { + try + { + cursor = PrometheusSerializer.WriteEof(this.buffer, cursor); + break; + } + catch (IndexOutOfRangeException) + { + if (!this.IncreaseBufferSize()) + { + throw; } } } - this.previousDataView = new ArraySegment(this.buffer, 0, Math.Max(cursor - 1, 0)); + this.previousDataView = new ArraySegment(this.buffer, 0, cursor); return ExportResult.Success; } catch (Exception) @@ -221,6 +231,22 @@ private ExportResult OnCollect(Batch metrics) } } + private bool IncreaseBufferSize() + { + var newBufferSize = this.buffer.Length * 2; + + if (newBufferSize > 100 * 1024 * 1024) + { + return false; + } + + var newBuffer = new byte[newBufferSize]; + this.buffer.CopyTo(newBuffer, 0); + this.buffer = newBuffer; + + return true; + } + public readonly struct CollectionResponse { public CollectionResponse(ArraySegment view, DateTime generatedAtUtc, bool fromCache) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs index 5c38be07a7..2e81582c08 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs @@ -270,6 +270,15 @@ public static int WriteMetricName(byte[] buffer, int cursor, string metricName, return cursor; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int WriteEof(byte[] buffer, int cursor) + { + cursor = WriteAsciiStringNoEscape(buffer, cursor, "# EOF"); + buffer[cursor++] = ASCII_LINEFEED; + + return cursor; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteHelpMetadata(byte[] buffer, int cursor, string metricName, string metricUnit, string metricDescription) { diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index a35dcf89b2..b5710a1d1e 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -135,7 +135,7 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri Assert.Equal("text/plain; charset=utf-8; version=0.0.4", response.Content.Headers.ContentType.ToString()); Assert.Matches( - "^# TYPE counter_double counter\ncounter_double{key1='value1',key2='value2'} 101.17 \\d+\n$".Replace('\'', '"'), + "^# TYPE counter_double counter\ncounter_double{key1='value1',key2='value2'} 101.17 \\d+\n\n# EOF\n$".Replace('\'', '"'), await response.Content.ReadAsStringAsync().ConfigureAwait(false)); } else From 7d89f28ca67c0d66af8a4a00e6e82b045d7c6ecc Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 13 Sep 2022 18:57:51 -0700 Subject: [PATCH 2/3] update test case --- .../PrometheusExporterMiddlewareTests.cs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index 07db88f710..23cda76865 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -261,21 +262,18 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - string[] lines = content.Split('\n'); + var matches = Regex.Matches( + content, + ("^" + + "# TYPE counter_double counter\n" + + "counter_double{key1='value1',key2='value2'} 101.17 (\\d+)\n" + + "\n" + + "# EOF\n" + + "$").Replace('\'', '"')); - Assert.Equal( - $"# TYPE counter_double counter", - lines[0]); + Assert.Single(matches); - Assert.Contains( - $"counter_double{{key1=\"value1\",key2=\"value2\"}} 101.17", - lines[1]); - - var index = content.LastIndexOf(' '); - - Assert.Equal('\n', content[^1]); - - var timestamp = long.Parse(content.Substring(index, content.Length - index - 1)); + var timestamp = long.Parse(matches[0].Groups[1].Value); Assert.True(beginTimestamp <= timestamp && timestamp <= endTimestamp); } From 92e3b7466aa428529530733ffceb06c5aa3d0906 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 13 Sep 2022 19:05:42 -0700 Subject: [PATCH 3/3] changelog --- 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 f2fd26ccd5..23bb14d6e0 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -11,6 +11,9 @@ instead of 200, when no metrics are collected ([#3648](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3648)) * Added support for OpenMetrics UNIT metadata ([#3651](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3651)) +* Added `"# EOF\n"` ending following the [OpenMetrics + specification](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md) + ([#3654](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3654)) ## 1.4.0-alpha.2 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 9ab03e4de8..448bb0891a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -11,6 +11,9 @@ ([#3648](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3648)) * Added support for OpenMetrics UNIT metadata ([#3651](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3651)) +* Added `"# EOF\n"` ending following the [OpenMetrics + specification](https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md) + ([#3654](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3654)) ## 1.4.0-alpha.2