diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Shipped.txt index e69de29bb2d..7dc5c58110b 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt index 99d1e9e4939..92eaa1e4111 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt @@ -4,18 +4,18 @@ OpenTelemetry.Exporter.PrometheusAspNetCoreOptions OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.DisableTotalNameSuffixForCounters.get -> bool OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.DisableTotalNameSuffixForCounters.set -> void OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.PrometheusAspNetCoreOptions() -> void -OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.ScrapeEndpointPath.get -> string? OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.ScrapeEndpointPath.set -> void OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.ScrapeResponseCacheDurationMilliseconds.get -> int OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.ScrapeResponseCacheDurationMilliseconds.set -> void OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions -static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) -> Microsoft.AspNetCore.Builder.IApplicationBuilder -static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, OpenTelemetry.Metrics.MeterProvider meterProvider, System.Func predicate, string path, System.Action configureBranchedPipeline, string optionsName) -> 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 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, OpenTelemetry.Metrics.MeterProvider meterProvider, System.Action configureBranchedPipeline, string optionsName) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder -static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder -static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, string name, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder -static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder +static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app) -> Microsoft.AspNetCore.Builder.IApplicationBuilder! +static Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions.UseOpenTelemetryPrometheusScrapingEndpoint(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app, OpenTelemetry.Metrics.MeterProvider? meterProvider, System.Func? predicate, string? path, System.Action? configureBranchedPipeline, string? optionsName) -> 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 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, OpenTelemetry.Metrics.MeterProvider? meterProvider, System.Action? configureBranchedPipeline, string? optionsName) -> Microsoft.AspNetCore.Builder.IEndpointConventionBuilder! +static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder! builder) -> OpenTelemetry.Metrics.MeterProviderBuilder! +static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder! builder, string? name, System.Action? configure) -> OpenTelemetry.Metrics.MeterProviderBuilder! +static OpenTelemetry.Metrics.PrometheusExporterMeterProviderBuilderExtensions.AddPrometheusExporter(this OpenTelemetry.Metrics.MeterProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Metrics.MeterProviderBuilder! diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj index a143d39ac12..f6a49fe6a64 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj @@ -7,9 +7,6 @@ $(PackageTags);prometheus;metrics coreunstable- $(DefineConstants);PROMETHEUS_ASPNETCORE - - - disable @@ -25,7 +22,6 @@ - @@ -36,6 +32,8 @@ + + diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs index 75acb565175..ed8186ec88d 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs @@ -15,7 +15,7 @@ public class PrometheusAspNetCoreOptions /// /// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics". /// - public string ScrapeEndpointPath { get; set; } = DefaultScrapeEndpointPath; + public string? ScrapeEndpointPath { get; set; } = DefaultScrapeEndpointPath; /// /// Gets or sets a value indicating whether addition of _total suffix for counter metric names is disabled. Default value: . diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs index 946f07d05aa..38559e6b201 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterApplicationBuilderExtensions.cs @@ -93,11 +93,11 @@ public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint(thi /// cref="IApplicationBuilder"/> for chaining calls. public static IApplicationBuilder UseOpenTelemetryPrometheusScrapingEndpoint( this IApplicationBuilder app, - MeterProvider meterProvider, - Func predicate, - string path, - Action configureBranchedPipeline, - string optionsName) + MeterProvider? meterProvider, + Func? predicate, + string? path, + Action? configureBranchedPipeline, + string? optionsName) { // Note: Order is important here. MeterProvider is accessed before // GetOptions so that any changes made to diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs index 4e70f2f76c9..45639b378fc 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterEndpointRouteBuilderExtensions.cs @@ -64,10 +64,10 @@ public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint(this IEnd /// A convention routes for the Prometheus scraping endpoint. public static IEndpointConventionBuilder MapPrometheusScrapingEndpoint( this IEndpointRouteBuilder endpoints, - string path, - MeterProvider meterProvider, - Action configureBranchedPipeline, - string optionsName) + string? path, + MeterProvider? meterProvider, + Action? configureBranchedPipeline, + string? optionsName) { var builder = endpoints.CreateApplicationBuilder(); diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs index 6a075fc1b99..f73455d3b08 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMeterProviderBuilderExtensions.cs @@ -37,13 +37,13 @@ public static MeterProviderBuilder AddPrometheusExporter( /// Adds to the . /// /// builder to use. - /// Name which is used when retrieving options. - /// Callback action for configuring . + /// Optional name which is used when retrieving options. + /// Optional callback action for configuring . /// The instance of to chain the calls. public static MeterProviderBuilder AddPrometheusExporter( this MeterProviderBuilder builder, - string name, - Action configure) + string? name, + Action? configure) { Guard.ThrowIfNull(builder); diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs index fdfcd2b7113..c2bd1cc2572 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs @@ -25,8 +25,9 @@ internal sealed class PrometheusExporterMiddleware public PrometheusExporterMiddleware(MeterProvider meterProvider, RequestDelegate next) { Guard.ThrowIfNull(meterProvider); + Guard.ThrowIfNull(next); - if (!meterProvider.TryFindExporter(out PrometheusExporter exporter)) + if (!meterProvider.TryFindExporter(out PrometheusExporter? exporter)) { throw new ArgumentException("A PrometheusExporter could not be found configured on the provided MeterProvider."); } @@ -36,6 +37,8 @@ public PrometheusExporterMiddleware(MeterProvider meterProvider, RequestDelegate internal PrometheusExporterMiddleware(PrometheusExporter exporter) { + Debug.Assert(exporter != null, "exporter was null"); + this.exporter = exporter; } @@ -71,7 +74,7 @@ public async Task InvokeAsync(HttpContext httpContext) ? "application/openmetrics-text; version=1.0.0; charset=utf-8" : "text/plain; charset=utf-8; version=0.0.4"; - await response.Body.WriteAsync(dataView.Array, 0, dataView.Count).ConfigureAwait(false); + await response.Body.WriteAsync(dataView.Array!, 0, dataView.Count).ConfigureAwait(false); } else { @@ -93,8 +96,6 @@ public async Task InvokeAsync(HttpContext httpContext) response.StatusCode = 500; } } - - this.exporter.OnExport = null; } private static bool AcceptsOpenMetrics(HttpRequest request) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Shipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Shipped.txt index e69de29bb2d..7dc5c58110b 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt index d05f12424ea..6caa1a77cb1 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt @@ -1,12 +1,12 @@ OpenTelemetry.Exporter.PrometheusHttpListenerOptions OpenTelemetry.Exporter.PrometheusHttpListenerOptions.DisableTotalNameSuffixForCounters.get -> bool OpenTelemetry.Exporter.PrometheusHttpListenerOptions.DisableTotalNameSuffixForCounters.set -> void -OpenTelemetry.Exporter.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection +OpenTelemetry.Exporter.PrometheusHttpListenerOptions.UriPrefixes.get -> System.Collections.Generic.IReadOnlyCollection! OpenTelemetry.Exporter.PrometheusHttpListenerOptions.UriPrefixes.set -> void OpenTelemetry.Exporter.PrometheusHttpListenerOptions.PrometheusHttpListenerOptions() -> void -OpenTelemetry.Exporter.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string +OpenTelemetry.Exporter.PrometheusHttpListenerOptions.ScrapeEndpointPath.get -> string? OpenTelemetry.Exporter.PrometheusHttpListenerOptions.ScrapeEndpointPath.set -> void OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions -static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder) -> OpenTelemetry.Metrics.MeterProviderBuilder -static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, string name, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder -static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder builder, System.Action configure) -> OpenTelemetry.Metrics.MeterProviderBuilder +static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder! builder) -> OpenTelemetry.Metrics.MeterProviderBuilder! +static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder! builder, string? name, System.Action? configure) -> OpenTelemetry.Metrics.MeterProviderBuilder! +static OpenTelemetry.Metrics.PrometheusHttpListenerMeterProviderBuilderExtensions.AddPrometheusHttpListener(this OpenTelemetry.Metrics.MeterProviderBuilder! builder, System.Action! configure) -> OpenTelemetry.Metrics.MeterProviderBuilder! diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index 4c78a12e49c..1c74e36bcf8 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Diagnostics; using System.Runtime.CompilerServices; using OpenTelemetry.Metrics; @@ -12,7 +13,7 @@ internal sealed class PrometheusCollectionManager private readonly PrometheusExporter exporter; private readonly int scrapeResponseCacheDurationMilliseconds; - private readonly Func, ExportResult> onCollectRef; + private readonly PrometheusExporter.ExportFunc onCollectRef; private readonly Dictionary metricsCache; private readonly HashSet scopes; private int metricsCacheCount; @@ -26,7 +27,7 @@ internal sealed class PrometheusCollectionManager private DateTime? previousOpenMetricsDataViewGeneratedAtUtc; private int readerCount; private bool collectionRunning; - private TaskCompletionSource collectionTcs; + private TaskCompletionSource? collectionTcs; public PrometheusCollectionManager(PrometheusExporter exporter) { @@ -115,7 +116,7 @@ public Task EnterCollect(bool openMetricsRequested) ? this.previousOpenMetricsDataViewGeneratedAtUtc : this.previousPlainTextDataViewGeneratedAtUtc; - response = new CollectionResponse(this.previousOpenMetricsDataView, this.previousPlainTextDataView, previousDataViewGeneratedAtUtc.Value, fromCache: false); + response = new CollectionResponse(this.previousOpenMetricsDataView, this.previousPlainTextDataView, previousDataViewGeneratedAtUtc!.Value, fromCache: false); } else { @@ -188,14 +189,16 @@ private void WaitForReadersToComplete() [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ExecuteCollect(bool openMetricsRequested) { + Debug.Assert(this.exporter.Collect != null, "this.exporter.Collect was null"); + this.exporter.OnExport = this.onCollectRef; this.exporter.OpenMetricsRequested = openMetricsRequested; - var result = this.exporter.Collect(Timeout.Infinite); + var result = this.exporter.Collect!(Timeout.Infinite); this.exporter.OnExport = null; return result; } - private ExportResult OnCollect(Batch metrics) + private ExportResult OnCollect(in Batch metrics) { var cursor = 0; ref byte[] buffer = ref (this.exporter.OpenMetricsRequested ? ref this.openMetricsBuffer : ref this.plainTextBuffer); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs index 292b0aa7c31..c5b0e0e64d3 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs @@ -1,6 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Diagnostics; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -13,9 +14,9 @@ namespace OpenTelemetry.Exporter.Prometheus; [ExportModes(ExportModes.Pull)] internal sealed class PrometheusExporter : BaseExporter, IPullMetricExporter { - private Func funcCollect; - private Func, ExportResult> funcExport; - private Resource resource; + private Func? funcCollect; + private ExportFunc? funcExport; + private Resource? resource; private bool disposed; /// @@ -32,22 +33,24 @@ public PrometheusExporter(PrometheusExporterOptions options) this.CollectionManager = new PrometheusCollectionManager(this); } + public delegate ExportResult ExportFunc(in Batch batch); + /// /// Gets or sets the Collect delegate. /// - public Func Collect + public Func? Collect { get => this.funcCollect; set => this.funcCollect = value; } - internal Func, ExportResult> OnExport + internal ExportFunc? OnExport { get => this.funcExport; set => this.funcExport = value; } - internal Action OnDispose { get; set; } + internal Action? OnDispose { get; set; } internal PrometheusCollectionManager CollectionManager { get; } @@ -62,7 +65,9 @@ internal Func, ExportResult> OnExport /// public override ExportResult Export(in Batch metrics) { - return this.OnExport(metrics); + Debug.Assert(this.OnExport != null, "this.OnExport was null"); + + return this.OnExport!(in metrics); } /// diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusHeadersParser.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusHeadersParser.cs index 81576b723dc..2c2c8f5b7d6 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusHeadersParser.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusHeadersParser.cs @@ -7,7 +7,7 @@ internal static class PrometheusHeadersParser { private const string OpenMetricsMediaType = "application/openmetrics-text"; - internal static bool AcceptsOpenMetrics(string contentType) + internal static bool AcceptsOpenMetrics(string? contentType) { var value = contentType.AsSpan(); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs index 6f18f06a536..800963454a7 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs @@ -1,6 +1,8 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Text; using OpenTelemetry.Metrics; @@ -29,7 +31,7 @@ public PrometheusMetric(string name, string unit, PrometheusType type, bool disa var sanitizedName = SanitizeMetricName(name); var openMetricsName = SanitizeOpenMetricsName(sanitizedName); - string sanitizedUnit = null; + string? sanitizedUnit = null; if (!string.IsNullOrEmpty(unit)) { sanitizedUnit = GetUnit(unit); @@ -80,7 +82,7 @@ public PrometheusMetric(string name, string unit, PrometheusType type, bool disa public string OpenMetricsMetadataName { get; } - public string Unit { get; } + public string? Unit { get; } public PrometheusType Type { get; } @@ -91,7 +93,7 @@ public static PrometheusMetric Create(Metric metric, bool disableTotalNameSuffix internal static string SanitizeMetricName(string metricName) { - StringBuilder sb = null; + StringBuilder? sb = null; var lastCharUnderscore = false; for (var i = 0; i < metricName.Length; i++) @@ -134,7 +136,7 @@ internal static string RemoveAnnotations(string unit) // https://ucum.org/ucum#section-Character-Set-and-Lexical-Rules // What should happen if they are nested isn't defined. // Right now the remove annotations code doesn't attempt to balance multiple start and end braces. - StringBuilder sb = null; + StringBuilder? sb = null; var hasOpenBrace = false; var startOpenBraceIndex = 0; @@ -168,7 +170,10 @@ internal static string RemoveAnnotations(string unit) return unit; } - sb.Append(unit, lastWriteIndex, unit.Length - lastWriteIndex); + Debug.Assert(sb != null, "sb was null"); + + sb!.Append(unit, lastWriteIndex, unit.Length - lastWriteIndex); + return sb.ToString(); } @@ -204,7 +209,7 @@ private static string GetUnit(string unit) return updatedUnit; } - private static bool TryProcessRateUnits(string updatedUnit, out string updatedPerUnit) + private static bool TryProcessRateUnits(string updatedUnit, [NotNullWhen(true)] out string? updatedPerUnit) { updatedPerUnit = null; diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs index b177dda5a04..7623de4067a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs @@ -177,7 +177,7 @@ public static int WriteLabelValue(byte[] buffer, int cursor, string value) { Debug.Assert(value != null, $"{nameof(value)} should not be null."); - for (int i = 0; i < value.Length; i++) + for (int i = 0; i < value!.Length; i++) { var ordinal = (ushort)value[i]; switch (ordinal) @@ -204,7 +204,7 @@ public static int WriteLabelValue(byte[] buffer, int cursor, string value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int WriteLabel(byte[] buffer, int cursor, string labelKey, object labelValue) + public static int WriteLabel(byte[] buffer, int cursor, string labelKey, object? labelValue) { cursor = WriteLabelKey(buffer, cursor, labelKey); buffer[cursor++] = unchecked((byte)'='); @@ -216,7 +216,7 @@ public static int WriteLabel(byte[] buffer, int cursor, string labelKey, object return cursor; - static string GetLabelValueString(object labelValue) + static string GetLabelValueString(object? labelValue) { // TODO: Attribute values should be written as their JSON representation. Extra logic may need to be added here to correctly convert other .NET types. // More detail: https://github.com/open-telemetry/opentelemetry-dotnet/issues/4822#issuecomment-1707328495 @@ -235,6 +235,8 @@ public static int WriteMetricName(byte[] buffer, int cursor, PrometheusMetric me // Metric name has already been escaped. var name = openMetricsRequested ? metric.OpenMetricsName : metric.Name; + Debug.Assert(!string.IsNullOrWhiteSpace(name), "name was null or whitespace"); + for (int i = 0; i < name.Length; i++) { var ordinal = (ushort)name[i]; @@ -250,6 +252,8 @@ public static int WriteMetricMetadataName(byte[] buffer, int cursor, PrometheusM // Metric name has already been escaped. var name = openMetricsRequested ? metric.OpenMetricsMetadataName : metric.Name; + Debug.Assert(!string.IsNullOrWhiteSpace(name), "name was null or whitespace"); + for (int i = 0; i < name.Length; i++) { var ordinal = (ushort)name[i]; @@ -321,7 +325,7 @@ public static int WriteUnitMetadata(byte[] buffer, int cursor, PrometheusMetric buffer[cursor++] = unchecked((byte)' '); // Unit name has already been escaped. - for (int i = 0; i < metric.Unit.Length; i++) + for (int i = 0; i < metric.Unit!.Length; i++) { var ordinal = (ushort)metric.Unit[i]; buffer[cursor++] = unchecked((byte)ordinal); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj index 3766816a7e1..91fb3f461cf 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj @@ -5,9 +5,6 @@ Stand-alone HttpListener for hosting OpenTelemetry .NET Prometheus Exporter $(PackageTags);prometheus;metrics coreunstable- - - - disable @@ -19,9 +16,10 @@ + - + diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs index 8576873f520..284a99acc87 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs @@ -13,8 +13,8 @@ internal sealed class PrometheusHttpListener : IDisposable private readonly HttpListener httpListener = new(); private readonly object syncObject = new(); - private CancellationTokenSource tokenSource; - private Task workerThread; + private CancellationTokenSource? tokenSource; + private Task? workerThread; /// /// Initializes a new instance of the class. @@ -28,7 +28,7 @@ public PrometheusHttpListener(PrometheusExporter exporter, PrometheusHttpListene this.exporter = exporter; - string path = options.ScrapeEndpointPath; + string path = options.ScrapeEndpointPath ?? PrometheusHttpListenerOptions.DefaultScrapeEndpointPath; if (!path.StartsWith("/")) { @@ -83,7 +83,7 @@ public void Stop() } this.tokenSource.Cancel(); - this.workerThread.Wait(); + this.workerThread!.Wait(); this.tokenSource = null; } } @@ -116,7 +116,7 @@ private void WorkerProc() try { using var scope = SuppressInstrumentationScope.Begin(); - while (!this.tokenSource.IsCancellationRequested) + while (!this.tokenSource!.IsCancellationRequested) { var ctxTask = this.httpListener.GetContextAsync(); ctxTask.Wait(this.tokenSource.Token); @@ -164,7 +164,7 @@ private async Task ProcessRequestAsync(HttpListenerContext context) ? "application/openmetrics-text; version=1.0.0; charset=utf-8" : "text/plain; charset=utf-8; version=0.0.4"; - await context.Response.OutputStream.WriteAsync(dataView.Array, 0, dataView.Count).ConfigureAwait(false); + await context.Response.OutputStream.WriteAsync(dataView.Array!, 0, dataView.Count).ConfigureAwait(false); } else { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs index 929774a11f9..7289432cdcf 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs @@ -37,13 +37,13 @@ public static MeterProviderBuilder AddPrometheusHttpListener( /// Adds PrometheusHttpListener to MeterProviderBuilder. /// /// builder to use. - /// Name which is used when retrieving options. - /// Callback action for configuring . + /// Optional name which is used when retrieving options. + /// Optional callback action for configuring . /// The instance of to chain calls. public static MeterProviderBuilder AddPrometheusHttpListener( this MeterProviderBuilder builder, - string name, - Action configure) + string? name, + Action? configure) { Guard.ThrowIfNull(builder); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index d0c6bd2edf0..dbe20b726c4 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -10,12 +10,14 @@ namespace OpenTelemetry.Exporter; /// public class PrometheusHttpListenerOptions { + internal const string DefaultScrapeEndpointPath = "/metrics"; + private IReadOnlyCollection uriPrefixes = new[] { "http://localhost:9464/" }; /// /// Gets or sets the path to use for the scraping endpoint. Default value: "/metrics". /// - public string ScrapeEndpointPath { get; set; } = "/metrics"; + public string? ScrapeEndpointPath { get; set; } = DefaultScrapeEndpointPath; /// /// Gets or sets a value indicating whether addition of _total suffix for counter metric names is disabled. Default value: . diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests.csproj b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests.csproj index b361d72e7ee..7a91330ca56 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests.csproj +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests.csproj @@ -3,9 +3,6 @@ Unit test project for Prometheus Exporter AspNetCore for OpenTelemetry $(TargetFrameworksForAspNetCoreTests) $(DefineConstants);PROMETHEUS_ASPNETCORE - - - disable diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index b63cabc36b3..3f23d764e55 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -110,7 +110,7 @@ public Task PrometheusExporterMiddlewareIntegration_MixedPredicateAndPath() services => services.Configure(o => o.ScrapeEndpointPath = "/metrics_options"), validateResponse: rsp => { - if (!rsp.Headers.TryGetValues("X-MiddlewareExecuted", out IEnumerable headers)) + if (!rsp.Headers.TryGetValues("X-MiddlewareExecuted", out IEnumerable? headers)) { headers = Array.Empty(); } @@ -137,7 +137,7 @@ public Task PrometheusExporterMiddlewareIntegration_MixedPath() services => services.Configure(o => o.ScrapeEndpointPath = "/metrics_options"), validateResponse: rsp => { - if (!rsp.Headers.TryGetValues("X-MiddlewareExecuted", out IEnumerable headers)) + if (!rsp.Headers.TryGetValues("X-MiddlewareExecuted", out IEnumerable? headers)) { headers = Array.Empty(); } @@ -254,10 +254,10 @@ public async Task PrometheusExporterMiddlewareIntegration_CanServeOpenMetricsAnd using var host = await StartTestHostAsync( app => app.UseOpenTelemetryPrometheusScrapingEndpoint()); - var tags = new KeyValuePair[] + var tags = new KeyValuePair[] { - new KeyValuePair("key1", "value1"), - new KeyValuePair("key2", "value2"), + new("key1", "value1"), + new("key2", "value2"), }; using var meter = new Meter(MeterName, MeterVersion); @@ -315,10 +315,10 @@ public async Task PrometheusExporterMiddlewareIntegration_TestBufferSizeIncrease private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string path, Action configure, - Action configureServices = null, - Action validateResponse = null, + Action? configureServices = null, + Action? validateResponse = null, bool registerMeterProvider = true, - Action configureOptions = null, + Action? configureOptions = null, bool skipMetrics = false, string acceptHeader = "application/openmetrics-text") { @@ -326,10 +326,10 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( using var host = await StartTestHostAsync(configure, configureServices, registerMeterProvider, configureOptions); - var tags = new KeyValuePair[] + var tags = new KeyValuePair[] { - new KeyValuePair("key1", "value1"), - new KeyValuePair("key2", "value2"), + new("key1", "value1"), + new("key2", "value2"), }; using var meter = new Meter(MeterName, MeterVersion); @@ -375,11 +375,11 @@ private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, Ht if (requestOpenMetrics) { - Assert.Equal("application/openmetrics-text; version=1.0.0; charset=utf-8", response.Content.Headers.ContentType.ToString()); + Assert.Equal("application/openmetrics-text; version=1.0.0; charset=utf-8", response.Content.Headers.ContentType!.ToString()); } else { - Assert.Equal("text/plain; charset=utf-8; version=0.0.4", response.Content.Headers.ContentType.ToString()); + Assert.Equal("text/plain; charset=utf-8; version=0.0.4", response.Content.Headers.ContentType!.ToString()); } string content = (await response.Content.ReadAsStringAsync()).ReplaceLineEndings(); @@ -417,9 +417,9 @@ private static async Task VerifyAsync(long beginTimestamp, long endTimestamp, Ht private static Task StartTestHostAsync( Action configure, - Action configureServices = null, + Action? configureServices = null, bool registerMeterProvider = true, - Action configureOptions = null) + Action? configureOptions = null) { return new HostBuilder() .ConfigureWebHost(webBuilder => webBuilder diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj index 295231183c2..efab035ead8 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests.csproj @@ -3,9 +3,6 @@ Unit test project for Prometheus Exporter HttpListener for OpenTelemetry $(TargetFrameworksForTests) $(DefineConstants);PROMETHEUS_HTTP_LISTENER - - - disable diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusCollectionManagerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusCollectionManagerTests.cs index 32ee87e3de5..36f5e124e67 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusCollectionManagerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusCollectionManagerTests.cs @@ -31,7 +31,7 @@ public async Task EnterExitCollectTest(int scrapeResponseCacheDurationMillisecon #endif .Build()) { - if (!provider.TryFindExporter(out PrometheusExporter exporter)) + if (!provider.TryFindExporter(out PrometheusExporter? exporter)) { throw new InvalidOperationException("PrometheusExporter could not be found on MeterProvider."); } @@ -40,7 +40,7 @@ public async Task EnterExitCollectTest(int scrapeResponseCacheDurationMillisecon var collectFunc = exporter.Collect; exporter.Collect = (timeout) => { - bool result = collectFunc(timeout); + bool result = collectFunc!(timeout); runningCollectCount++; Thread.Sleep(5000); return result; @@ -156,6 +156,6 @@ private class Response { public PrometheusCollectionManager.CollectionResponse CollectionResponse; - public byte[] ViewPayload; + public byte[]? ViewPayload; } } diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index a132cf17753..07ee28beb23 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -38,7 +38,7 @@ public void UriPrefixesNull() { Assert.Throws(() => { - TestPrometheusHttpListenerUriPrefixOptions(null); + TestPrometheusHttpListenerUriPrefixOptions(null!); }); } @@ -90,10 +90,10 @@ public void PrometheusHttpListenerThrowsOnStart() Random random = new Random(); int retryAttempts = 5; int port = 0; - string address = null; + string? address = null; - PrometheusExporter exporter = null; - PrometheusHttpListener listener = null; + PrometheusExporter? exporter = null; + PrometheusHttpListener? listener = null; // Step 1: Start a listener on a random port. while (retryAttempts-- != 0) @@ -134,7 +134,7 @@ public void PrometheusHttpListenerThrowsOnStart() exporter, new() { - UriPrefixes = new string[] { address }, + UriPrefixes = new string[] { address! }, }); listener.Start(); @@ -199,8 +199,8 @@ private static MeterProvider BuildMeterProvider(Meter meter, IEnumerable[] + var tags = new KeyValuePair[] { - new KeyValuePair("key1", "value1"), - new KeyValuePair("key2", "value2"), + new("key1", "value1"), + new("key2", "value2"), }; var counter = meter.CreateCounter("counter_double", unit: "By"); @@ -273,11 +273,11 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri if (requestOpenMetrics) { - Assert.Equal("application/openmetrics-text; version=1.0.0; charset=utf-8", response.Content.Headers.ContentType.ToString()); + Assert.Equal("application/openmetrics-text; version=1.0.0; charset=utf-8", response.Content.Headers.ContentType!.ToString()); } else { - Assert.Equal("text/plain; charset=utf-8; version=0.0.4", response.Content.Headers.ContentType.ToString()); + Assert.Equal("text/plain; charset=utf-8; version=0.0.4", response.Content.Headers.ContentType!.ToString()); } var content = await response.Content.ReadAsStringAsync(); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 7c4a95b05f4..2864fe59b5c 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -129,7 +129,7 @@ public void GaugeOneDimension() meter.CreateObservableGauge( "test_gauge", - () => new Measurement(123, new KeyValuePair("tagKey", "tagValue"))); + () => new Measurement(123, new KeyValuePair("tagKey", "tagValue"))); provider.ForceFlush(); @@ -156,7 +156,7 @@ public void GaugeBoolDimension() meter.CreateObservableGauge( "test_gauge", - () => new Measurement(123, new KeyValuePair("tagKey", true))); + () => new Measurement(123, new KeyValuePair("tagKey", true))); provider.ForceFlush(); @@ -312,8 +312,8 @@ public void HistogramOneDimension() .Build(); var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18, new KeyValuePair("x", "1")); - histogram.Record(100, new KeyValuePair("x", "1")); + histogram.Record(18, new KeyValuePair("x", "1")); + histogram.Record(100, new KeyValuePair("x", "1")); provider.ForceFlush(); @@ -539,8 +539,8 @@ public void HistogramOneDimensionWithOpenMetricsFormat() .Build(); var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18, new KeyValuePair("x", "1")); - histogram.Record(100, new KeyValuePair("x", "1")); + histogram.Record(18, new KeyValuePair("x", "1")); + histogram.Record(100, new KeyValuePair("x", "1")); provider.ForceFlush(); @@ -622,8 +622,8 @@ public void HistogramOneDimensionWithScopeVersion() .Build(); var histogram = meter.CreateHistogram("test_histogram"); - histogram.Record(18, new KeyValuePair("x", "1")); - histogram.Record(100, new KeyValuePair("x", "1")); + histogram.Record(18, new KeyValuePair("x", "1")); + histogram.Record(100, new KeyValuePair("x", "1")); provider.ForceFlush();