diff --git a/src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt index 340bdb69a7f..0c45b3f187c 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus/.publicApi/netcoreapp3.1/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions +Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions OpenTelemetry.Exporter.PrometheusExporter OpenTelemetry.Exporter.PrometheusExporter.Collect.get -> System.Func OpenTelemetry.Exporter.PrometheusExporter.Collect.set -> void @@ -20,4 +21,7 @@ static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensio static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, OpenTelemetry.Metrics.MeterProvider meterProvider, System.Func predicate, string path, System.Action configureBranchedPipeline) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, string path) -> Microsoft.AspNetCore.Builder.IApplicationBuilder static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, System.Func predicate) -> Microsoft.AspNetCore.Builder.IApplicationBuilder -static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder \ No newline at end of file +static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure = null) -> OpenTelemetry.Metrics.MeterProviderBuilder +static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder +static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder +static Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions.MapPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path = null, OpenTelemetry.Metrics.MeterProvider meterProvider = null, System.Action configureBranchedPipeline = null) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder diff --git a/src/OpenTelemetry.Exporter.Prometheus/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus/CHANGELOG.md index 68055853b76..5787a17c1db 100644 --- a/src/OpenTelemetry.Exporter.Prometheus/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Added `IEndpointRouteBuilder` extension methods to help with Prometheus + middleware configuration on ASP.NET Core + ([#3295](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3295)) + ## 1.3.0-rc.2 Released 2022-June-1 diff --git a/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterEndpointRouteBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterEndpointRouteBuilderExtensions.cs new file mode 100644 index 00000000000..02b58a34201 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus/PrometheusExporterEndpointRouteBuilderExtensions.cs @@ -0,0 +1,112 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETCOREAPP3_1_OR_GREATER + +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Exporter; +using OpenTelemetry.Exporter.Prometheus; +using OpenTelemetry.Internal; +using OpenTelemetry.Metrics; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Provides extension methods for to add + /// Prometheus scraping endpoint. + /// + public static class PrometheusExporterEndpointRouteBuilderExtensions + { + /// + /// Adds OpenTelemetry Prometheus scraping endpoint middleware to an + /// instance. + /// + /// Note: A branched pipeline is created for the route + /// specified by . + /// The to add + /// middleware to. + /// A convention routes for the Prometheus scraping endpoint. + public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint(this IEndpointRouteBuilder endpoints) + => MapPrometheusScrapingEndpoint(endpoints, path: null, meterProvider: null, configureBranchedPipeline: null); + + /// + /// Adds OpenTelemetry Prometheus scraping endpoint middleware to an + /// instance. + /// + /// The to add + /// middleware to. + /// The path to use for the branched pipeline. + /// A convention routes for the Prometheus scraping endpoint. + public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint(this IEndpointRouteBuilder endpoints, string path) + { + Guard.ThrowIfNull(path); + return MapPrometheusScrapingEndpoint(endpoints, path, meterProvider: null, configureBranchedPipeline: null); + } + + /// + /// Adds OpenTelemetry Prometheus scraping endpoint middleware to an + /// instance. + /// + /// The to add + /// middleware to. + /// Optional path to use for the branched pipeline. + /// If not provided then + /// is used. + /// Optional + /// containing a otherwise the primary + /// SDK provider will be resolved using application services. + /// Optional callback to + /// configure the branched pipeline. Called before registration of the + /// Prometheus middleware. + /// A convention routes for the Prometheus scraping endpoint. + public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint( + this IEndpointRouteBuilder endpoints, + string path = null, + MeterProvider meterProvider = null, + Action configureBranchedPipeline = null) + { + var builder = endpoints.CreateApplicationBuilder(); + + // Note: Order is important here. MeterProvider is accessed before + // GetOptions so that any changes made to + // PrometheusExporterOptions in deferred AddPrometheusExporter + // configure actions are reflected. + meterProvider ??= endpoints.ServiceProvider.GetRequiredService(); + + if (path == null) + { + var options = endpoints.ServiceProvider.GetOptions(); + path = options.ScrapeEndpointPath ?? PrometheusExporterOptions.DefaultScrapeEndpointPath; + } + + if (!path.StartsWith("/")) + { + path = $"/{path}"; + } + + configureBranchedPipeline?.Invoke(builder); + + builder.UseMiddleware(meterProvider); + + return endpoints.Map(new PathString(path), builder.Build()); + } + } +} +#endif diff --git a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterMiddlewareTests.cs index 619c8b6c3d8..a11ea430423 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.Tests/PrometheusExporterMiddlewareTests.cs @@ -167,6 +167,42 @@ public Task PrometheusExporterMiddlewareIntegration_NoMetrics() skipMetrics: true); } + [Fact] + public Task PrometheusExporterMiddlewareIntegration_MapEndpoint() + { + return RunPrometheusExporterMiddlewareIntegrationTest( + "/metrics", + app => app.UseRouting().UseEndpoints(builder => builder.MapPrometheusScrapingEndpoint()), + services => services.AddRouting()); + } + + [Fact] + public Task PrometheusExporterMiddlewareIntegration_MapEndpoint_WithPathOverride() + { + return RunPrometheusExporterMiddlewareIntegrationTest( + "/metrics_path", + app => app.UseRouting().UseEndpoints(builder => builder.MapPrometheusScrapingEndpoint("metrics_path")), + services => services.AddRouting()); + } + + [Fact] + public async Task PrometheusExporterMiddlewareIntegration_MapEndpoint_WithMeterProvider() + { + using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() + .AddMeter(MeterName) + .AddPrometheusExporter() + .Build(); + + await RunPrometheusExporterMiddlewareIntegrationTest( + "/metrics", + app => app.UseRouting().UseEndpoints(builder => builder.MapPrometheusScrapingEndpoint( + path: null, + meterProvider: meterProvider, + configureBranchedPipeline: null)), + services => services.AddRouting(), + registerMeterProvider: false).ConfigureAwait(false); + } + private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string path, Action configure,