From b5bc9cbc7c057a5f3cd28c1b640219c13d24a869 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 2 Aug 2022 11:44:33 -0700 Subject: [PATCH 1/5] improve PrometheusHttpListener options --- .../README.md | 6 +-- examples/Console/TestPrometheusExporter.cs | 2 +- .../.publicApi/net462/PublicAPI.Unshipped.txt | 4 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 4 +- .../PrometheusHttpListener.cs | 12 ++--- .../PrometheusHttpListenerOptions.cs | 20 +++---- .../README.md | 22 +++----- .../PrometheusHttpListenerTests.cs | 52 +++++++++++-------- .../Program.cs | 2 +- test/OpenTelemetry.Tests.Stress/Skeleton.cs | 2 +- 10 files changed, 59 insertions(+), 67 deletions(-) diff --git a/docs/metrics/getting-started-prometheus-grafana/README.md b/docs/metrics/getting-started-prometheus-grafana/README.md index 62953f4f130..1e2f2e35e99 100644 --- a/docs/metrics/getting-started-prometheus-grafana/README.md +++ b/docs/metrics/getting-started-prometheus-grafana/README.md @@ -49,10 +49,10 @@ with .AddPrometheusHttpListener() ``` -`PrometheusHttpListener` is a wrapper that contains `PrometheusExporter`. -With `AddPrometheusHttpListener()`, OpenTelemetry `PrometheusExporter` will export +`PrometheusHttpListener` is a wrapper that contains `PrometheusExporter`. With +`AddPrometheusHttpListener()`, OpenTelemetry `PrometheusExporter` will export data via the endpoint defined by -[PrometheusHttpListenerOptions.Prefixes](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md#prefixes), +[PrometheusHttpListenerOptions.UriPrefixes](../../../src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md#uriprefixes), which is `http://localhost:9464/` by default. ```mermaid diff --git a/examples/Console/TestPrometheusExporter.cs b/examples/Console/TestPrometheusExporter.cs index 0b1284c4ae0..befb0e5d4e9 100644 --- a/examples/Console/TestPrometheusExporter.cs +++ b/examples/Console/TestPrometheusExporter.cs @@ -52,7 +52,7 @@ internal static object Run(int port) .AddMeter(MyMeter.Name) .AddMeter(MyMeter2.Name) .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { $"http://localhost:{port}/" }) + options => options.UriPrefixes = new string[] { $"http://localhost:{port}/" }) .Build(); var process = Process.GetCurrentProcess(); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt index 7b61e63513e..e05aa673ed4 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/net462/PublicAPI.Unshipped.txt @@ -1,6 +1,6 @@ OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.set -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 7b61e63513e..e05aa673ed4 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,6 +1,6 @@ OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.get -> System.Collections.Generic.IReadOnlyCollection -OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.Prefixes.set -> void +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.UriPrefixes.set -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string OpenTelemetry.Exporter.Prometheus.PrometheusHttpListenerOptions.ScrapeEndpointPath.set -> void diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index ce3a5a48ed8..3bd122a7732 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -39,14 +39,12 @@ internal sealed class PrometheusHttpListener : IDisposable public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListenerOptions options) { Guard.ThrowIfNull(exporter); - - if ((options.Prefixes?.Count ?? 0) <= 0) - { - throw new ArgumentException("No Prefixes were specified on PrometheusHttpListenerOptions."); - } + Guard.ThrowIfNull(options); this.exporter = exporter; + string path = this.exporter.ScrapeEndpointPath; + if (!path.StartsWith("/")) { path = $"/{path}"; @@ -57,9 +55,9 @@ public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListene path = $"{path}/"; } - foreach (string prefix in options.Prefixes) + foreach (string uriPrefix in options.UriPrefixes) { - this.httpListener.Prefixes.Add($"{prefix.TrimEnd('/')}{path}"); + this.httpListener.Prefixes.Add($"{uriPrefix.TrimEnd('/')}{path}"); } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index dc1e8e5d9ea..d4b02b15924 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -25,7 +25,7 @@ namespace OpenTelemetry.Exporter.Prometheus /// public class PrometheusHttpListenerOptions { - private IReadOnlyCollection prefixes = new string[] { "http://localhost:9464/" }; + private IReadOnlyCollection uriPrefixes = new string[] { "http://localhost:9464/" }; /// /// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics". @@ -33,28 +33,22 @@ public class PrometheusHttpListenerOptions public string ScrapeEndpointPath { get; set; } = "/metrics"; /// - /// Gets or sets the prefixes to use for the http listener. + /// Gets or sets the URI (Uniform Resource Identifier) prefixes to use for the http listener. /// Default value: ["http://localhost:9464/"]. /// - public IReadOnlyCollection Prefixes + public IReadOnlyCollection UriPrefixes { - get => this.prefixes; + get => this.uriPrefixes; set { Guard.ThrowIfNull(value); - foreach (string inputUri in value) + if ((value.Count) == 0) { - if (!(Uri.TryCreate(inputUri, UriKind.Absolute, out var uri) && - (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))) - { - throw new ArgumentException( - "Prometheus HttpListener prefix path should be a valid URI with http/https scheme.", - nameof(this.prefixes)); - } + throw new ArgumentException("Empty list provided.", nameof(this.UriPrefixes)); } - this.prefixes = value; + this.uriPrefixes = value; } } } diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md index b997b5adecc..b97cb0bd258 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/README.md @@ -15,38 +15,32 @@ instance for Prometheus to scrape. ### Step 1: Install Package -Install - ```shell dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener ``` ### Step 2: Add PrometheusHttpListener -Add and configure `PrometheusHttpListener` with `PrometheusHttpListenerOptions`. - -For example: - ```csharp -using var meterProvider = Sdk.CreateMeterProviderBuilder() +var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MyMeter.Name) .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { "http://localhost:9464/" }) + options => options.UriPrefixes = new string[] { "http://localhost:9464/" }) .Build(); ``` -### Prefixes +### UriPrefixes -Defines the prefixes which will be used by the listener. The default value is `["http://localhost:9464/"]`. -You may specify multiple endpoints. +Defines one or more URI (Uniform Resource Identifier) prefixes which will be +used by the HTTP listener. The default value is `["http://localhost:9464/"]`. -For details see: +Refer to [HttpListenerPrefixCollection.Add(String)](https://docs.microsoft.com/dotnet/api/system.net.httplistenerprefixcollection.add) +for more details. ### ScrapeEndpointPath -Defines the path for the Prometheus scrape endpoint for by -`UseOpenTelemetryPrometheusScrapingEndpoint`. Default value: `"/metrics"`. +Defines the Prometheus scrape endpoint path. Default value: `"/metrics"`. ## Troubleshooting diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index bfaa2f06f56..146caf73b57 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -35,38 +35,44 @@ public class PrometheusHttpListenerTests [InlineData("https://example.com")] [InlineData("http://127.0.0.1")] [InlineData("http://example.com", "https://example.com", "http://127.0.0.1")] - public void ServerEndpointSanityCheckPositiveTest(params string[] uris) + public void UriPrefixesPositiveTest(params string[] uriPrefixes) { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusHttpListener(options => options.Prefixes = uris) + .AddPrometheusHttpListener(options => options.UriPrefixes = uriPrefixes) .Build(); } - [Theory] - [InlineData("")] - [InlineData(null)] - [InlineData("ftp://example.com")] - [InlineData("http://example.com", "https://example.com", "ftp://example.com")] - public void ServerEndpointSanityCheckNegativeTest(params string[] uris) + [Fact] + public void UriPrefixesNull() { - try + Assert.Throws(() => { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() - .AddPrometheusHttpListener(options => options.Prefixes = uris) + .AddPrometheusHttpListener(options => options.UriPrefixes = null) .Build(); - } - catch (Exception ex) + }); + } + + [Fact] + public void UriPrefixesEmptyList() + { + Assert.Throws(() => { - if (ex is not ArgumentNullException) - { - Assert.Equal("System.ArgumentException", ex.GetType().ToString()); -#if NETFRAMEWORK - Assert.Equal("Prometheus HttpListener prefix path should be a valid URI with http/https scheme.\r\nParameter name: prefixes", ex.Message); -#else - Assert.Equal("Prometheus HttpListener prefix path should be a valid URI with http/https scheme. (Parameter 'prefixes')", ex.Message); -#endif - } - } + using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() + .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { }) + .Build(); + }); + } + + [Fact] + public void UriPrefixesInvalid() + { + Assert.Throws(() => + { + using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() + .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { "ftp://example.com" }) + .Build(); + }); } [Fact] @@ -98,7 +104,7 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) - .AddPrometheusHttpListener(options => options.Prefixes = new string[] { address }) + .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { address }) .Build(); } diff --git a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs index 91d5e64a26a..da3215aee79 100644 --- a/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs +++ b/test/OpenTelemetry.Tests.Stress.Metrics/Program.cs @@ -47,7 +47,7 @@ public static void Main() using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(TestMeter.Name) .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { $"http://localhost:9185/" }) + options => options.UriPrefixes = new string[] { $"http://localhost:9185/" }) .Build(); Stress(prometheusPort: 9184); diff --git a/test/OpenTelemetry.Tests.Stress/Skeleton.cs b/test/OpenTelemetry.Tests.Stress/Skeleton.cs index 967bcd53a49..c69e2543a1a 100644 --- a/test/OpenTelemetry.Tests.Stress/Skeleton.cs +++ b/test/OpenTelemetry.Tests.Stress/Skeleton.cs @@ -76,7 +76,7 @@ public static void Stress(int concurrency = 0, int prometheusPort = 0) .AddMeter(meter.Name) .AddRuntimeInstrumentation() .AddPrometheusHttpListener( - options => options.Prefixes = new string[] { $"http://localhost:{prometheusPort}/" }) + options => options.UriPrefixes = new string[] { $"http://localhost:{prometheusPort}/" }) .Build() : null; var statistics = new long[concurrency]; From a5bbdd0390c10bd38530b392f54d66b88bf893e4 Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 2 Aug 2022 11:50:32 -0700 Subject: [PATCH 2/5] changelog --- .../CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 324932a4262..c46f2c9b97f 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -8,6 +8,9 @@ ([#3430](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3430) [#3503](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3503) [#3507](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3507)) +* Fixed bug + [#2840](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2840). + ([#3521](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3521)) ## 1.3.0-rc.2 From b3434a59f01c8608db1810a3da0dfc3add8dcd9a Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 2 Aug 2022 11:55:50 -0700 Subject: [PATCH 3/5] nits --- .../PrometheusHttpListenerOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index d4b02b15924..68f88c16424 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -43,7 +43,7 @@ public IReadOnlyCollection UriPrefixes { Guard.ThrowIfNull(value); - if ((value.Count) == 0) + if (value.Count == 0) { throw new ArgumentException("Empty list provided.", nameof(this.UriPrefixes)); } From e16f4ec4ab8294fa20302e3dff248d8a241ce96d Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 2 Aug 2022 14:03:15 -0700 Subject: [PATCH 4/5] improve change log --- .../CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index c46f2c9b97f..9d516152808 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -9,7 +9,8 @@ [#3503](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3503) [#3507](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3507)) * Fixed bug - [#2840](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2840). + [#2840](https://github.com/open-telemetry/opentelemetry-dotnet/issues/2840) by + allowing `+` and `*` to be used in the URI prefixes (e.g. `"http://*:9184"`). ([#3521](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3521)) ## 1.3.0-rc.2 From 56d94c22c3465a6b4a3dfbd490d45eaa0dcb1abf Mon Sep 17 00:00:00 2001 From: Reiley Yang Date: Tue, 2 Aug 2022 15:15:11 -0700 Subject: [PATCH 5/5] more tests --- .../PrometheusHttpListenerTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index 146caf73b57..fb281a3563f 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -31,10 +31,14 @@ public class PrometheusHttpListenerTests private readonly string meterName = Utils.GetCurrentMethodName(); [Theory] - [InlineData("http://example.com")] + [InlineData("http://+:9184")] + [InlineData("http://*:9184")] + [InlineData("http://+:9184/")] + [InlineData("http://*:9184/")] [InlineData("https://example.com")] [InlineData("http://127.0.0.1")] [InlineData("http://example.com", "https://example.com", "http://127.0.0.1")] + [InlineData("http://example.com")] public void UriPrefixesPositiveTest(params string[] uriPrefixes) { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder()