From ae20ec83905bf71f7ef844141d07b5e5de11c64b Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 2 Mar 2024 21:48:30 +1100 Subject: [PATCH 01/18] Include resource attributes as metrics tags --- .../.publicApi/PublicAPI.Unshipped.txt | 2 + .../CHANGELOG.md | 3 ++ ...etry.Exporter.Prometheus.AspNetCore.csproj | 1 + .../PrometheusAspNetCoreOptions.cs | 9 ++++ .../.publicApi/PublicAPI.Unshipped.txt | 2 + .../CHANGELOG.md | 2 + .../Internal/PrometheusCollectionManager.cs | 5 +++ .../Internal/PrometheusExporter.cs | 7 +++ .../Internal/PrometheusExporterOptions.cs | 5 +++ .../PrometheusResourceTagCollection.cs | 34 +++++++++++++++ .../Internal/PrometheusSerializer.cs | 43 ++++++++++++++++++- .../Internal/PrometheusSerializerExt.cs | 10 ++--- ...pListenerMeterProviderBuilderExtensions.cs | 1 + .../PrometheusHttpListenerOptions.cs | 5 +++ .../PrometheusExporterMiddlewareTests.cs | 40 +++++++++++++---- .../PrometheusHttpListenerTests.cs | 31 +++++++++++-- .../PrometheusSerializerTests.cs | 35 ++++++++++++++- 17 files changed, 214 insertions(+), 21 deletions(-) create mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusResourceTagCollection.cs diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt index 99d1e9e4939..2890f7cf95d 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.AllowedResourceAttributesFilter.get -> System.Predicate +OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.AllowedResourceAttributesFilter.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/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 56749e55689..2625504607b 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,10 +2,13 @@ ## Unreleased +* Added `target_info` and resource attributes to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3087)) + * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) * Export OpenMetrics format from Prometheus exporters ([#5107](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5107)) + * For requests with OpenMetrics format, scope info is automatically added ([#5086](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5086) [#5182](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5182)) 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 a94c7fdb1ae..870dbd258c3 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj @@ -24,6 +24,7 @@ + diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs index 75acb565175..c44f2602d89 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 the allowed resource attributes filter. Default value: No resource attributes allowed. + /// + public Predicate AllowedResourceAttributesFilter + { + get => this.ExporterOptions.AllowedResourceAttributesFilter; + set => this.ExporterOptions.AllowedResourceAttributesFilter = 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 d05f12424ea..555fa8f37d1 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.AllowedResourceAttributesFilter.get -> System.Predicate +OpenTelemetry.Exporter.PrometheusHttpListenerOptions.AllowedResourceAttributesFilter.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/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 8deaf5e9a56..081c983269f 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +* Added `target_info` and resource attributes to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3087)) +* * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index d92c48f1f2d..2a262129e2c 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -172,8 +172,12 @@ private ExportResult OnCollect(Batch metrics) try { + var resourceTags = new PrometheusResourceTagCollection(this.exporter.Resource, this.exporter.AllowedResourceAttributesFilter); + if (this.exporter.OpenMetricsRequested) { + cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, cursor, this.exporter.Resource); + this.scopes.Clear(); foreach (var metric in metrics) @@ -222,6 +226,7 @@ private ExportResult OnCollect(Batch metrics) cursor, metric, this.GetPrometheusMetric(metric), + resourceTags, this.exporter.OpenMetricsRequested); break; diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs index 578d0329ada..a211fcb5896 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs @@ -3,6 +3,7 @@ using OpenTelemetry.Internal; using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; namespace OpenTelemetry.Exporter.Prometheus; @@ -14,6 +15,7 @@ internal sealed class PrometheusExporter : BaseExporter, IPullMetricExpo { private Func funcCollect; private Func, ExportResult> funcExport; + private Resource resource; private bool disposed; /// @@ -26,6 +28,7 @@ public PrometheusExporter(PrometheusExporterOptions options) this.ScrapeResponseCacheDurationMilliseconds = options.ScrapeResponseCacheDurationMilliseconds; this.DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters; + this.AllowedResourceAttributesFilter = options.AllowedResourceAttributesFilter; this.CollectionManager = new PrometheusCollectionManager(this); } @@ -55,6 +58,10 @@ internal Func, ExportResult> OnExport internal bool OpenMetricsRequested { get; set; } + internal Predicate AllowedResourceAttributesFilter { get; set; } + + internal Resource Resource => this.resource ??= this.ParentProvider.GetResource(); + /// public override ExportResult Export(in Batch metrics) { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs index ec14b88b182..92c3650e2f1 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs @@ -29,6 +29,11 @@ public int ScrapeResponseCacheDurationMilliseconds } } + /// + /// Gets or sets the allowed resource attributes filter. Default value: No resource attributes allowed. + /// + public Predicate AllowedResourceAttributesFilter { get; set; } + /// /// 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.HttpListener/Internal/PrometheusResourceTagCollection.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusResourceTagCollection.cs new file mode 100644 index 00000000000..64ddafb32b9 --- /dev/null +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusResourceTagCollection.cs @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using OpenTelemetry.Resources; + +namespace OpenTelemetry.Exporter.Prometheus; + +internal readonly struct PrometheusResourceTagCollection +{ + private readonly Resource resource; + private readonly Predicate resourceAttributeFilter; + + public PrometheusResourceTagCollection(Resource resource, Predicate resourceAttributeFilter = null) + { + this.resource = resource; + this.resourceAttributeFilter = resourceAttributeFilter; + } + + public IEnumerable> Attributes + { + get + { + if (this.resource == null || this.resourceAttributeFilter == null) + { + return Enumerable.Empty>(); + } + + var attributeFilter = this.resourceAttributeFilter; + + return this.resource?.Attributes + .Where(attribute => attributeFilter(attribute.Key)); + } + } +} diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs index 69365d4e0ff..ede963c510a 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using OpenTelemetry.Internal; using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; namespace OpenTelemetry.Exporter.Prometheus; @@ -366,13 +367,19 @@ public static int WriteTimestamp(byte[] buffer, int cursor, long value, bool use } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTagCollection tags, bool writeEnclosingBraces = true) + public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTagCollection tags, PrometheusResourceTagCollection resourceTags = default, bool writeEnclosingBraces = true) { if (writeEnclosingBraces) { buffer[cursor++] = unchecked((byte)'{'); } + foreach (var resourceAttribute in resourceTags.Attributes) + { + cursor = WriteLabel(buffer, cursor, resourceAttribute.Key, resourceAttribute.Value); + buffer[cursor++] = unchecked((byte)','); + } + cursor = WriteLabel(buffer, cursor, "otel_scope_name", metric.MeterName); buffer[cursor++] = unchecked((byte)','); @@ -396,6 +403,40 @@ public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTa return cursor; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int WriteTargetInfo(byte[] buffer, int cursor, Resource resource) + { + if (!resource.Attributes.Any()) + { + return cursor; + } + + cursor = WriteAsciiStringNoEscape(buffer, cursor, "# TYPE target info"); + buffer[cursor++] = ASCII_LINEFEED; + + cursor = WriteAsciiStringNoEscape(buffer, cursor, "# HELP target Target metadata"); + buffer[cursor++] = ASCII_LINEFEED; + + cursor = WriteAsciiStringNoEscape(buffer, cursor, "target_info"); + buffer[cursor++] = unchecked((byte)'{'); + + foreach (var attribute in resource.Attributes) + { + cursor = WriteLabel(buffer, cursor, attribute.Key, attribute.Value); + + buffer[cursor++] = unchecked((byte)','); + } + + cursor--; // Write over the last written comma + + buffer[cursor++] = unchecked((byte)'}'); + buffer[cursor++] = unchecked((byte)' '); + buffer[cursor++] = unchecked((byte)'1'); + buffer[cursor++] = ASCII_LINEFEED; + + return cursor; + } + private static string MapPrometheusType(PrometheusType type) { return type switch diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs index 1523ef7c160..eeb31329667 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, PrometheusResourceTagCollection resourceTags = default, bool openMetricsRequested = false) { cursor = WriteTypeMetadata(buffer, cursor, prometheusMetric); cursor = WriteUnitMetadata(buffer, cursor, prometheusMetric); @@ -36,7 +36,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Counter and Gauge cursor = WriteMetricName(buffer, cursor, prometheusMetric); - cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags, resourceTags); buffer[cursor++] = unchecked((byte)' '); @@ -87,7 +87,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_bucket{"); - cursor = WriteTags(buffer, cursor, metric, tags, writeEnclosingBraces: false); + cursor = WriteTags(buffer, cursor, metric, tags, resourceTags, writeEnclosingBraces: false); cursor = WriteAsciiStringNoEscape(buffer, cursor, "le=\""); @@ -113,7 +113,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Histogram sum cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_sum"); - cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags, resourceTags); buffer[cursor++] = unchecked((byte)' '); @@ -127,7 +127,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Histogram count cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_count"); - cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags, resourceTags); buffer[cursor++] = unchecked((byte)' '); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs index 929774a11f9..bdb146d4fc2 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs @@ -69,6 +69,7 @@ private static MetricReader BuildPrometheusHttpListenerMetricReader( { ScrapeResponseCacheDurationMilliseconds = 0, DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters, + AllowedResourceAttributesFilter = options.AllowedResourceAttributesFilter, }); var reader = new BaseExportingMetricReader(exporter) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index d0c6bd2edf0..acef71f3ee6 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -41,4 +41,9 @@ public IReadOnlyCollection UriPrefixes this.uriPrefixes = value; } } + + /// + /// Gets or sets the allowed resource attributes filter. Default value: No resource attributes allowed. + /// + public Predicate AllowedResourceAttributesFilter { get; set; } } diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index 7e1a6c80bae..a869937e5a7 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -12,6 +12,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; using OpenTelemetry.Tests; using Xunit; @@ -150,6 +151,7 @@ public async Task PrometheusExporterMiddlewareIntegration_MeterProvider() { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MeterName) + .ConfigureResource(x => x.Clear().AddService("my_service", serviceInstanceId: "id1")) .AddPrometheusExporter() .Build(); @@ -213,6 +215,7 @@ public async Task PrometheusExporterMiddlewareIntegration_MapEndpoint_WithMeterP { using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter(MeterName) + .ConfigureResource(x => x.Clear().AddService("my_service", serviceInstanceId: "id1")) .AddPrometheusExporter() .Build(); @@ -245,6 +248,16 @@ public Task PrometheusExporterMiddlewareIntegration_UseOpenMetricsVersionHeader( acceptHeader: "application/openmetrics-text; version=1.0.0"); } + [Fact] + public Task PrometheusExporterMiddlewareIntegration_AddResourceAttributesAsTags() + { + return RunPrometheusExporterMiddlewareIntegrationTest( + "/metrics", + app => app.UseOpenTelemetryPrometheusScrapingEndpoint(), + configureOptions: o => o.AllowedResourceAttributesFilter = s => s == "service.name", + addServiceNameResourceTag: true); + } + private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string path, Action configure, @@ -253,7 +266,8 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( bool registerMeterProvider = true, Action configureOptions = null, bool skipMetrics = false, - string acceptHeader = "application/openmetrics-text") + string acceptHeader = "application/openmetrics-text", + bool addServiceNameResourceTag = false) { var requestOpenMetrics = acceptHeader.StartsWith("application/openmetrics-text"); @@ -265,11 +279,12 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( if (registerMeterProvider) { services.AddOpenTelemetry().WithMetrics(builder => builder - .AddMeter(MeterName) - .AddPrometheusExporter(o => - { - configureOptions?.Invoke(o); - })); + .ConfigureResource(x => x.Clear().AddService("my_service", serviceInstanceId: "id1")) + .AddMeter(MeterName) + .AddPrometheusExporter(o => + { + configureOptions?.Invoke(o); + })); } configureServices?.Invoke(services); @@ -321,15 +336,22 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string content = await response.Content.ReadAsStringAsync(); + var resourceTagAttributes = addServiceNameResourceTag + ? "service_name='my_service'," + : string.Empty; + string expected = requestOpenMetrics - ? "# TYPE otel_scope_info info\n" + ? "# TYPE target info\n" + + "# HELP target Target metadata\n" + + "target_info{service_name='my_service',service_instance_id='id1'} 1\n" + + "# TYPE otel_scope_info info\n" + "# HELP otel_scope_info Scope metadata\n" + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + "# TYPE counter_double_total counter\n" - + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + "# EOF\n" : "# TYPE counter_double_total counter\n" - + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + "# EOF\n"; var matches = Regex.Matches(content, ("^" + expected + "$").Replace('\'', '"')); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index ce7286d8c43..9165c4e878a 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -7,6 +7,7 @@ using System.Net.Http; #endif using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; using OpenTelemetry.Tests; using Xunit; @@ -83,6 +84,12 @@ public async Task PrometheusExporterHttpServerIntegration_UseOpenMetricsVersionH await this.RunPrometheusExporterHttpServerIntegrationTest(acceptHeader: "application/openmetrics-text; version=1.0.0"); } + [Fact] + public async Task PrometheusExporterHttpServerIntegration_AddResourceAttributeAsTag() + { + await this.RunPrometheusExporterHttpServerIntegrationTest(addServiceNameResourceTag: true); + } + [Fact] public void PrometheusHttpListenerThrowsOnStart() { @@ -154,7 +161,10 @@ private static void TestPrometheusHttpListenerUriPrefixOptions(string[] uriPrefi }); } - private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetrics = false, string acceptHeader = "application/openmetrics-text") + private async Task RunPrometheusExporterHttpServerIntegrationTest( + bool skipMetrics = false, + string acceptHeader = "application/openmetrics-text", + bool addServiceNameResourceTag = false) { var requestOpenMetrics = acceptHeader.StartsWith("application/openmetrics-text"); @@ -175,9 +185,15 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri { provider = Sdk.CreateMeterProviderBuilder() .AddMeter(meter.Name) + .ConfigureResource(x => x.Clear().AddService("my_service", serviceInstanceId: "id1")) .AddPrometheusHttpListener(options => { options.UriPrefixes = new string[] { address }; + + if (addServiceNameResourceTag) + { + options.AllowedResourceAttributesFilter = s => s == "service.name"; + } }) .Build(); @@ -232,15 +248,22 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetri var content = await response.Content.ReadAsStringAsync(); + var resourceTagAttributes = addServiceNameResourceTag + ? "service_name='my_service'," + : string.Empty; + var expected = requestOpenMetrics - ? "# TYPE otel_scope_info info\n" + ? "# TYPE target info\n" + + "# HELP target Target metadata\n" + + "target_info{service_name='my_service',service_instance_id='id1'} 1\n" + + "# TYPE otel_scope_info info\n" + "# HELP otel_scope_info Scope metadata\n" + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + "# TYPE counter_double_total counter\n" - + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + "# EOF\n" : "# TYPE counter_double_total counter\n" - + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + "# EOF\n"; Assert.Matches(("^" + expected + "$").Replace('\'', '"'), content); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index 7c4a95b05f4..d23b63629b2 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -4,6 +4,7 @@ using System.Diagnostics.Metrics; using System.Text; using OpenTelemetry.Metrics; +using OpenTelemetry.Resources; using OpenTelemetry.Tests; using Xunit; @@ -526,6 +527,36 @@ public void SumWithOpenMetricsFormat() Encoding.UTF8.GetString(buffer, 0, cursor)); } + [Fact] + public void SumWithResourceAttributes() + { + var buffer = new byte[85000]; + var metrics = new List(); + + var resource = ResourceBuilder.CreateEmpty().AddService("my_service"); + + using var meter = new Meter(Utils.GetCurrentMethodName()); + using var provider = Sdk.CreateMeterProviderBuilder() + .AddMeter(meter.Name) + .AddInMemoryExporter(metrics) + .SetResourceBuilder(resource) + .Build(); + + var counter = meter.CreateUpDownCounter("test_updown_counter"); + counter.Add(10); + counter.Add(-11); + + provider.ForceFlush(); + + var cursor = WriteMetric(buffer, 0, metrics[0], true, new PrometheusResourceTagCollection(resource.Build(), s => s == "service.name")); + Assert.Matches( + ("^" + + "# TYPE test_updown_counter gauge\n" + + $"test_updown_counter{{service_name='my_service',otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1 \\d+\\.\\d{{3}}\n" + + "$").Replace('\'', '"'), + Encoding.UTF8.GetString(buffer, 0, cursor)); + } + [Fact] public void HistogramOneDimensionWithOpenMetricsFormat() { @@ -653,8 +684,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, PrometheusResourceTagCollection resourceTags = default) { - return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric, false), useOpenMetrics); + return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric, false), resourceTags, useOpenMetrics); } } From bfb94014b7bfb945d9708c021d85ecb8fad5ad35 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 2 Mar 2024 21:51:04 +1100 Subject: [PATCH 02/18] Tidy xmldoc --- .../PrometheusAspNetCoreOptions.cs | 2 +- .../Internal/PrometheusExporterOptions.cs | 4 ++-- .../PrometheusHttpListenerOptions.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs index c44f2602d89..a650bf79fce 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs @@ -39,7 +39,7 @@ public int ScrapeResponseCacheDurationMilliseconds } /// - /// Gets or sets the allowed resource attributes filter. Default value: No resource attributes allowed. + /// Gets or sets the allowed resource attributes filter. Default value: null (no attributes allowed). /// public Predicate AllowedResourceAttributesFilter { diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs index 92c3650e2f1..2c3194f7c06 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs @@ -30,9 +30,9 @@ public int ScrapeResponseCacheDurationMilliseconds } /// - /// Gets or sets the allowed resource attributes filter. Default value: No resource attributes allowed. + /// Gets or sets the allowed resource attributes filter. Default value: null (no attributes allowed). /// - public Predicate AllowedResourceAttributesFilter { get; set; } + public Predicate AllowedResourceAttributesFilter { get; set; } = null; /// /// 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.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index acef71f3ee6..3a7103fbb80 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -43,7 +43,7 @@ public IReadOnlyCollection UriPrefixes } /// - /// Gets or sets the allowed resource attributes filter. Default value: No resource attributes allowed. + /// Gets or sets the allowed resource attributes filter. Default value: null (no attributes allowed). /// - public Predicate AllowedResourceAttributesFilter { get; set; } + public Predicate AllowedResourceAttributesFilter { get; set; } = null; } From 0a0ef681eb8337177bd89852e49353e4c69e2ba5 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 2 Mar 2024 21:54:18 +1100 Subject: [PATCH 03/18] Fit lint error --- src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 081c983269f..525866ff642 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased * Added `target_info` and resource attributes to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3087)) -* + * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) From 5ddf85075b9a9934a7e55ca18e9dac97d16babe6 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Wed, 6 Mar 2024 16:35:04 +1100 Subject: [PATCH 04/18] Revert resource attribute labels --- .../.publicApi/PublicAPI.Unshipped.txt | 2 -- .../CHANGELOG.md | 2 +- ...etry.Exporter.Prometheus.AspNetCore.csproj | 1 - .../PrometheusAspNetCoreOptions.cs | 9 ----- .../.publicApi/PublicAPI.Unshipped.txt | 2 -- .../CHANGELOG.md | 2 +- .../Internal/PrometheusCollectionManager.cs | 3 -- .../Internal/PrometheusExporter.cs | 3 -- .../Internal/PrometheusExporterOptions.cs | 5 --- .../PrometheusResourceTagCollection.cs | 34 ------------------ .../Internal/PrometheusSerializer.cs | 8 +---- .../Internal/PrometheusSerializerExt.cs | 10 +++--- ...pListenerMeterProviderBuilderExtensions.cs | 1 - .../PrometheusHttpListenerOptions.cs | 5 --- .../PrometheusExporterMiddlewareTests.cs | 21 ++--------- .../PrometheusHttpListenerTests.cs | 22 ++---------- .../PrometheusSerializerTests.cs | 35 ++----------------- 17 files changed, 16 insertions(+), 149 deletions(-) delete mode 100644 src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusResourceTagCollection.cs diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt index 2890f7cf95d..99d1e9e4939 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/.publicApi/PublicAPI.Unshipped.txt @@ -1,8 +1,6 @@ Microsoft.AspNetCore.Builder.PrometheusExporterApplicationBuilderExtensions Microsoft.AspNetCore.Builder.PrometheusExporterEndpointRouteBuilderExtensions OpenTelemetry.Exporter.PrometheusAspNetCoreOptions -OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.AllowedResourceAttributesFilter.get -> System.Predicate -OpenTelemetry.Exporter.PrometheusAspNetCoreOptions.AllowedResourceAttributesFilter.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/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 2625504607b..3176d96a815 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` and resource attributes to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3087)) +* Added `target_info` to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) 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 870dbd258c3..a94c7fdb1ae 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/OpenTelemetry.Exporter.Prometheus.AspNetCore.csproj @@ -24,7 +24,6 @@ - diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs index a650bf79fce..75acb565175 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusAspNetCoreOptions.cs @@ -38,14 +38,5 @@ public int ScrapeResponseCacheDurationMilliseconds set => this.ExporterOptions.ScrapeResponseCacheDurationMilliseconds = value; } - /// - /// Gets or sets the allowed resource attributes filter. Default value: null (no attributes allowed). - /// - public Predicate AllowedResourceAttributesFilter - { - get => this.ExporterOptions.AllowedResourceAttributesFilter; - set => this.ExporterOptions.AllowedResourceAttributesFilter = 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 555fa8f37d1..d05f12424ea 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/.publicApi/PublicAPI.Unshipped.txt @@ -1,6 +1,4 @@ OpenTelemetry.Exporter.PrometheusHttpListenerOptions -OpenTelemetry.Exporter.PrometheusHttpListenerOptions.AllowedResourceAttributesFilter.get -> System.Predicate -OpenTelemetry.Exporter.PrometheusHttpListenerOptions.AllowedResourceAttributesFilter.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/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 525866ff642..271b6ee04e8 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` and resource attributes to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/issues/3087)) +* Added `target_info` to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index 2a262129e2c..017c9fe67a0 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -172,8 +172,6 @@ private ExportResult OnCollect(Batch metrics) try { - var resourceTags = new PrometheusResourceTagCollection(this.exporter.Resource, this.exporter.AllowedResourceAttributesFilter); - if (this.exporter.OpenMetricsRequested) { cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, cursor, this.exporter.Resource); @@ -226,7 +224,6 @@ private ExportResult OnCollect(Batch metrics) cursor, metric, this.GetPrometheusMetric(metric), - resourceTags, this.exporter.OpenMetricsRequested); break; diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs index a211fcb5896..292b0aa7c31 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporter.cs @@ -28,7 +28,6 @@ public PrometheusExporter(PrometheusExporterOptions options) this.ScrapeResponseCacheDurationMilliseconds = options.ScrapeResponseCacheDurationMilliseconds; this.DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters; - this.AllowedResourceAttributesFilter = options.AllowedResourceAttributesFilter; this.CollectionManager = new PrometheusCollectionManager(this); } @@ -58,8 +57,6 @@ internal Func, ExportResult> OnExport internal bool OpenMetricsRequested { get; set; } - internal Predicate AllowedResourceAttributesFilter { 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 2c3194f7c06..ec14b88b182 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusExporterOptions.cs @@ -29,11 +29,6 @@ public int ScrapeResponseCacheDurationMilliseconds } } - /// - /// Gets or sets the allowed resource attributes filter. Default value: null (no attributes allowed). - /// - public Predicate AllowedResourceAttributesFilter { get; set; } = null; - /// /// 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.HttpListener/Internal/PrometheusResourceTagCollection.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusResourceTagCollection.cs deleted file mode 100644 index 64ddafb32b9..00000000000 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusResourceTagCollection.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -using OpenTelemetry.Resources; - -namespace OpenTelemetry.Exporter.Prometheus; - -internal readonly struct PrometheusResourceTagCollection -{ - private readonly Resource resource; - private readonly Predicate resourceAttributeFilter; - - public PrometheusResourceTagCollection(Resource resource, Predicate resourceAttributeFilter = null) - { - this.resource = resource; - this.resourceAttributeFilter = resourceAttributeFilter; - } - - public IEnumerable> Attributes - { - get - { - if (this.resource == null || this.resourceAttributeFilter == null) - { - return Enumerable.Empty>(); - } - - var attributeFilter = this.resourceAttributeFilter; - - return this.resource?.Attributes - .Where(attribute => attributeFilter(attribute.Key)); - } - } -} diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs index ede963c510a..923f32dfe9d 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs @@ -367,19 +367,13 @@ public static int WriteTimestamp(byte[] buffer, int cursor, long value, bool use } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTagCollection tags, PrometheusResourceTagCollection resourceTags = default, bool writeEnclosingBraces = true) + public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTagCollection tags, bool writeEnclosingBraces = true) { if (writeEnclosingBraces) { buffer[cursor++] = unchecked((byte)'{'); } - foreach (var resourceAttribute in resourceTags.Attributes) - { - cursor = WriteLabel(buffer, cursor, resourceAttribute.Key, resourceAttribute.Value); - buffer[cursor++] = unchecked((byte)','); - } - cursor = WriteLabel(buffer, cursor, "otel_scope_name", metric.MeterName); buffer[cursor++] = unchecked((byte)','); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs index eeb31329667..1523ef7c160 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, PrometheusResourceTagCollection resourceTags = default, bool openMetricsRequested = false) + public static int WriteMetric(byte[] buffer, int cursor, Metric metric, PrometheusMetric prometheusMetric, bool openMetricsRequested = false) { cursor = WriteTypeMetadata(buffer, cursor, prometheusMetric); cursor = WriteUnitMetadata(buffer, cursor, prometheusMetric); @@ -36,7 +36,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Counter and Gauge cursor = WriteMetricName(buffer, cursor, prometheusMetric); - cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags, resourceTags); + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); buffer[cursor++] = unchecked((byte)' '); @@ -87,7 +87,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_bucket{"); - cursor = WriteTags(buffer, cursor, metric, tags, resourceTags, writeEnclosingBraces: false); + cursor = WriteTags(buffer, cursor, metric, tags, writeEnclosingBraces: false); cursor = WriteAsciiStringNoEscape(buffer, cursor, "le=\""); @@ -113,7 +113,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Histogram sum cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_sum"); - cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags, resourceTags); + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); buffer[cursor++] = unchecked((byte)' '); @@ -127,7 +127,7 @@ public static int WriteMetric(byte[] buffer, int cursor, Metric metric, Promethe // Histogram count cursor = WriteMetricName(buffer, cursor, prometheusMetric); cursor = WriteAsciiStringNoEscape(buffer, cursor, "_count"); - cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags, resourceTags); + cursor = WriteTags(buffer, cursor, metric, metricPoint.Tags); buffer[cursor++] = unchecked((byte)' '); diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs index bdb146d4fc2..929774a11f9 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerMeterProviderBuilderExtensions.cs @@ -69,7 +69,6 @@ private static MetricReader BuildPrometheusHttpListenerMetricReader( { ScrapeResponseCacheDurationMilliseconds = 0, DisableTotalNameSuffixForCounters = options.DisableTotalNameSuffixForCounters, - AllowedResourceAttributesFilter = options.AllowedResourceAttributesFilter, }); var reader = new BaseExportingMetricReader(exporter) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs index 3a7103fbb80..d0c6bd2edf0 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListenerOptions.cs @@ -41,9 +41,4 @@ public IReadOnlyCollection UriPrefixes this.uriPrefixes = value; } } - - /// - /// Gets or sets the allowed resource attributes filter. Default value: null (no attributes allowed). - /// - public Predicate AllowedResourceAttributesFilter { get; set; } = null; } diff --git a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs index a869937e5a7..46da01e6417 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs @@ -248,16 +248,6 @@ public Task PrometheusExporterMiddlewareIntegration_UseOpenMetricsVersionHeader( acceptHeader: "application/openmetrics-text; version=1.0.0"); } - [Fact] - public Task PrometheusExporterMiddlewareIntegration_AddResourceAttributesAsTags() - { - return RunPrometheusExporterMiddlewareIntegrationTest( - "/metrics", - app => app.UseOpenTelemetryPrometheusScrapingEndpoint(), - configureOptions: o => o.AllowedResourceAttributesFilter = s => s == "service.name", - addServiceNameResourceTag: true); - } - private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string path, Action configure, @@ -266,8 +256,7 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( bool registerMeterProvider = true, Action configureOptions = null, bool skipMetrics = false, - string acceptHeader = "application/openmetrics-text", - bool addServiceNameResourceTag = false) + string acceptHeader = "application/openmetrics-text") { var requestOpenMetrics = acceptHeader.StartsWith("application/openmetrics-text"); @@ -336,10 +325,6 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( string content = await response.Content.ReadAsStringAsync(); - var resourceTagAttributes = addServiceNameResourceTag - ? "service_name='my_service'," - : string.Empty; - string expected = requestOpenMetrics ? "# TYPE target info\n" + "# HELP target Target metadata\n" @@ -348,10 +333,10 @@ private static async Task RunPrometheusExporterMiddlewareIntegrationTest( + "# HELP otel_scope_info Scope metadata\n" + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + "# TYPE counter_double_total counter\n" - + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + "# EOF\n" : "# TYPE counter_double_total counter\n" - + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + "# EOF\n"; var matches = Regex.Matches(content, ("^" + expected + "$").Replace('\'', '"')); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index 9165c4e878a..32b32d92a98 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -84,12 +84,6 @@ public async Task PrometheusExporterHttpServerIntegration_UseOpenMetricsVersionH await this.RunPrometheusExporterHttpServerIntegrationTest(acceptHeader: "application/openmetrics-text; version=1.0.0"); } - [Fact] - public async Task PrometheusExporterHttpServerIntegration_AddResourceAttributeAsTag() - { - await this.RunPrometheusExporterHttpServerIntegrationTest(addServiceNameResourceTag: true); - } - [Fact] public void PrometheusHttpListenerThrowsOnStart() { @@ -163,8 +157,7 @@ private static void TestPrometheusHttpListenerUriPrefixOptions(string[] uriPrefi private async Task RunPrometheusExporterHttpServerIntegrationTest( bool skipMetrics = false, - string acceptHeader = "application/openmetrics-text", - bool addServiceNameResourceTag = false) + string acceptHeader = "application/openmetrics-text") { var requestOpenMetrics = acceptHeader.StartsWith("application/openmetrics-text"); @@ -189,11 +182,6 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest( .AddPrometheusHttpListener(options => { options.UriPrefixes = new string[] { address }; - - if (addServiceNameResourceTag) - { - options.AllowedResourceAttributesFilter = s => s == "service.name"; - } }) .Build(); @@ -248,10 +236,6 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest( var content = await response.Content.ReadAsStringAsync(); - var resourceTagAttributes = addServiceNameResourceTag - ? "service_name='my_service'," - : string.Empty; - var expected = requestOpenMetrics ? "# TYPE target info\n" + "# HELP target Target metadata\n" @@ -260,10 +244,10 @@ private async Task RunPrometheusExporterHttpServerIntegrationTest( + "# HELP otel_scope_info Scope metadata\n" + $"otel_scope_info{{otel_scope_name='{MeterName}'}} 1\n" + "# TYPE counter_double_total counter\n" - + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+\\.\\d{{3}})\n" + "# EOF\n" : "# TYPE counter_double_total counter\n" - + $"counter_double_total{{{resourceTagAttributes}otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + + $"counter_double_total{{otel_scope_name='{MeterName}',otel_scope_version='{MeterVersion}',key1='value1',key2='value2'}} 101.17 (\\d+)\n" + "# EOF\n"; Assert.Matches(("^" + expected + "$").Replace('\'', '"'), content); diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs index d23b63629b2..7c4a95b05f4 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs @@ -4,7 +4,6 @@ using System.Diagnostics.Metrics; using System.Text; using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; using OpenTelemetry.Tests; using Xunit; @@ -527,36 +526,6 @@ public void SumWithOpenMetricsFormat() Encoding.UTF8.GetString(buffer, 0, cursor)); } - [Fact] - public void SumWithResourceAttributes() - { - var buffer = new byte[85000]; - var metrics = new List(); - - var resource = ResourceBuilder.CreateEmpty().AddService("my_service"); - - using var meter = new Meter(Utils.GetCurrentMethodName()); - using var provider = Sdk.CreateMeterProviderBuilder() - .AddMeter(meter.Name) - .AddInMemoryExporter(metrics) - .SetResourceBuilder(resource) - .Build(); - - var counter = meter.CreateUpDownCounter("test_updown_counter"); - counter.Add(10); - counter.Add(-11); - - provider.ForceFlush(); - - var cursor = WriteMetric(buffer, 0, metrics[0], true, new PrometheusResourceTagCollection(resource.Build(), s => s == "service.name")); - Assert.Matches( - ("^" - + "# TYPE test_updown_counter gauge\n" - + $"test_updown_counter{{service_name='my_service',otel_scope_name='{Utils.GetCurrentMethodName()}'}} -1 \\d+\\.\\d{{3}}\n" - + "$").Replace('\'', '"'), - Encoding.UTF8.GetString(buffer, 0, cursor)); - } - [Fact] public void HistogramOneDimensionWithOpenMetricsFormat() { @@ -684,8 +653,8 @@ public void HistogramOneDimensionWithScopeVersion() Encoding.UTF8.GetString(buffer, 0, cursor)); } - private static int WriteMetric(byte[] buffer, int cursor, Metric metric, bool useOpenMetrics = false, PrometheusResourceTagCollection resourceTags = default) + private static int WriteMetric(byte[] buffer, int cursor, Metric metric, bool useOpenMetrics = false) { - return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric, false), resourceTags, useOpenMetrics); + return PrometheusSerializer.WriteMetric(buffer, cursor, metric, PrometheusMetric.Create(metric, false), useOpenMetrics); } } From 1fd739b2908a0450955b5a0e0eb6baf543c6c2f1 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Wed, 6 Mar 2024 16:37:19 +1100 Subject: [PATCH 05/18] Tidy PR diff --- .../PrometheusHttpListenerTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs index 32b32d92a98..3a4ca1c4153 100644 --- a/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs +++ b/test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs @@ -155,9 +155,7 @@ private static void TestPrometheusHttpListenerUriPrefixOptions(string[] uriPrefi }); } - private async Task RunPrometheusExporterHttpServerIntegrationTest( - bool skipMetrics = false, - string acceptHeader = "application/openmetrics-text") + private async Task RunPrometheusExporterHttpServerIntegrationTest(bool skipMetrics = false, string acceptHeader = "application/openmetrics-text") { var requestOpenMetrics = acceptHeader.StartsWith("application/openmetrics-text"); From f08dd85709749d5a3e5c6d63df3214ae5f6f08d6 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Thu, 7 Mar 2024 07:27:05 +1100 Subject: [PATCH 06/18] Handle buffer overflow --- .../Internal/PrometheusCollectionManager.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index 017c9fe67a0..3b10ab999c8 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -174,7 +174,17 @@ private ExportResult OnCollect(Batch metrics) { if (this.exporter.OpenMetricsRequested) { - cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, cursor, this.exporter.Resource); + try + { + cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, cursor, this.exporter.Resource); + } + catch (IndexOutOfRangeException) + { + if (!this.IncreaseBufferSize()) + { + throw; + } + } this.scopes.Clear(); From 24f298dc0dc876c7271494e79a3bd68291fe3c3d Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Thu, 7 Mar 2024 08:03:26 +1100 Subject: [PATCH 07/18] Fix changelog --- src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md | 2 +- src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 3176d96a815..b8d55eb203e 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 271b6ee04e8..2331c746992 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` to Prometheus exporters ([#3087](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) From 79202c8f817cbf2b2a1b8c9df03e20134b1723e9 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 9 Mar 2024 22:13:06 +1100 Subject: [PATCH 08/18] Cache target info buffer --- .../Internal/PrometheusCollectionManager.cs | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index 3b10ab999c8..26f9e4cf0f3 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -17,6 +17,7 @@ internal sealed class PrometheusCollectionManager private readonly HashSet scopes; private int metricsCacheCount; private byte[] buffer = new byte[85000]; // encourage the object to live in LOH (large object heap) + private byte[] targetInfoBuffer; private int globalLockState; private ArraySegment previousDataView; private DateTime? previousDataViewGeneratedAtUtc; @@ -174,25 +175,20 @@ private ExportResult OnCollect(Batch metrics) { if (this.exporter.OpenMetricsRequested) { - try - { - cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, cursor, this.exporter.Resource); - } - catch (IndexOutOfRangeException) - { - if (!this.IncreaseBufferSize()) - { - throw; - } - } + cursor = this.WriteTargetInfo(); this.scopes.Clear(); foreach (var metric in metrics) { - if (PrometheusSerializer.CanWriteMetric(metric)) + if (!PrometheusSerializer.CanWriteMetric(metric)) { - if (this.scopes.Add(metric.MeterName)) + continue; + } + + if (this.scopes.Add(metric.MeterName)) + { + while (true) { try { @@ -274,6 +270,40 @@ private ExportResult OnCollect(Batch metrics) } } + private int WriteTargetInfo() + { + if (this.targetInfoBuffer == null) + { + while (true) + { + try + { + var cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, 0, this.exporter.Resource); + + this.targetInfoBuffer = new byte[cursor]; + this.buffer.CopyTo(this.targetInfoBuffer, 0); + + break; + } + catch (IndexOutOfRangeException) + { + if (!this.IncreaseBufferSize()) + { + throw; + } + } + } + } + else + { + // Buffer will always be large enough for the target_info at this point because we've already + // previously written to the buffer (increasing it as needed) and copied out the target_info buffer. + this.targetInfoBuffer.CopyTo(this.buffer, 0); + } + + return this.targetInfoBuffer.Length; + } + private bool IncreaseBufferSize() { var newBufferSize = this.buffer.Length * 2; From 042ad0406079316f9299b3b59013ed71b0dbc071 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 9 Mar 2024 22:31:03 +1100 Subject: [PATCH 09/18] Fix array copy --- .../Internal/PrometheusCollectionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index 26f9e4cf0f3..af94d87a3af 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -281,7 +281,7 @@ private int WriteTargetInfo() var cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, 0, this.exporter.Resource); this.targetInfoBuffer = new byte[cursor]; - this.buffer.CopyTo(this.targetInfoBuffer, 0); + Array.Copy(this.buffer, 0, this.targetInfoBuffer, 0, cursor); break; } From c421b825098f9d13be2ed3b419c53acd8ecb342d Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Sat, 9 Mar 2024 22:31:27 +1100 Subject: [PATCH 10/18] Update changelog --- src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md | 2 +- src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index b8d55eb203e..197fd79820f 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` to Prometheus exporters ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters when using OpenMetrics format ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 2331c746992..85f5388cfdd 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` to Prometheus exporters ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters when using OpenMetrics format ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) * Added option to disable _total suffix addition to counter metrics ([#5305](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5305)) From 198576f22804712595d46f70ea6afc6c20c291a4 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Fri, 15 Mar 2024 18:29:04 +1100 Subject: [PATCH 11/18] Better write performance --- .../Internal/PrometheusCollectionManager.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs index af94d87a3af..27ab845164b 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs @@ -17,7 +17,7 @@ internal sealed class PrometheusCollectionManager private readonly HashSet scopes; private int metricsCacheCount; private byte[] buffer = new byte[85000]; // encourage the object to live in LOH (large object heap) - private byte[] targetInfoBuffer; + private int targetInfoBufferLength = -1; // zero or positive when target_info has been written for the first time private int globalLockState; private ArraySegment previousDataView; private DateTime? previousDataViewGeneratedAtUtc; @@ -272,16 +272,13 @@ private ExportResult OnCollect(Batch metrics) private int WriteTargetInfo() { - if (this.targetInfoBuffer == null) + if (this.targetInfoBufferLength < 0) { while (true) { try { - var cursor = PrometheusSerializer.WriteTargetInfo(this.buffer, 0, this.exporter.Resource); - - this.targetInfoBuffer = new byte[cursor]; - Array.Copy(this.buffer, 0, this.targetInfoBuffer, 0, cursor); + this.targetInfoBufferLength = PrometheusSerializer.WriteTargetInfo(this.buffer, 0, this.exporter.Resource); break; } @@ -294,14 +291,8 @@ private int WriteTargetInfo() } } } - else - { - // Buffer will always be large enough for the target_info at this point because we've already - // previously written to the buffer (increasing it as needed) and copied out the target_info buffer. - this.targetInfoBuffer.CopyTo(this.buffer, 0); - } - return this.targetInfoBuffer.Length; + return this.targetInfoBufferLength; } private bool IncreaseBufferSize() From 9f62f61cb7185a6e7407630c678402c2e245b5f9 Mon Sep 17 00:00:00 2001 From: Robert Coltheart <13191652+robertcoltheart@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:54:40 +1100 Subject: [PATCH 12/18] Update src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs Co-authored-by: Utkarsh Umesan Pillai <66651184+utpilla@users.noreply.github.com> --- .../Internal/PrometheusSerializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs index 923f32dfe9d..dbbf419a3a8 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs @@ -400,7 +400,7 @@ public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteTargetInfo(byte[] buffer, int cursor, Resource resource) { - if (!resource.Attributes.Any()) + if (resource != Resource.Empty) { return cursor; } From fec5a35d3438de34d8be37ad06394a6e5a8f6d74 Mon Sep 17 00:00:00 2001 From: Robert Coltheart <13191652+robertcoltheart@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:55:47 +1100 Subject: [PATCH 13/18] Update CHANGELOG.md --- src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 22d8a408d24..d900e8ab29d 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` to Prometheus exporters when using OpenMetrics format ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) ## 1.8.0-beta.1 From 8a298c3f1a23e560b89349561d93fe56f35658c4 Mon Sep 17 00:00:00 2001 From: Robert Coltheart <13191652+robertcoltheart@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:56:10 +1100 Subject: [PATCH 14/18] Update CHANGELOG.md --- src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 36bc9a373a8..f6692ecb0bb 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* Added `target_info` to Prometheus exporters when using OpenMetrics format ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) ## 1.8.0-beta.1 From 529335fba094c0fd4397b315e82c5fb1da312bce Mon Sep 17 00:00:00 2001 From: Robert Coltheart <13191652+robertcoltheart@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:02:06 +1100 Subject: [PATCH 15/18] Update CHANGELOG.md --- src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index d900e8ab29d..0c515500a4f 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,7 +2,8 @@ ## Unreleased -* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info + ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) ## 1.8.0-beta.1 From 1881c437dac3558f0f495764163583094f1db4ec Mon Sep 17 00:00:00 2001 From: Robert Coltheart <13191652+robertcoltheart@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:02:39 +1100 Subject: [PATCH 16/18] Update CHANGELOG.md --- .../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 f6692ecb0bb..0ef05e4d966 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,7 +2,8 @@ ## Unreleased -* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) +* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info + ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) ## 1.8.0-beta.1 From 44e9121a730d46db4e3f37a6d8cbd28426e5f343 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Fri, 22 Mar 2024 08:50:31 +1100 Subject: [PATCH 17/18] Fix wrong comparison --- .../Internal/PrometheusSerializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs index dbbf419a3a8..719f21a0c61 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs @@ -400,7 +400,7 @@ public static int WriteTags(byte[] buffer, int cursor, Metric metric, ReadOnlyTa [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int WriteTargetInfo(byte[] buffer, int cursor, Resource resource) { - if (resource != Resource.Empty) + if (resource == Resource.Empty) { return cursor; } From a5b09da9daf9c01abcd99bc57a803378d9c01569 Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Fri, 22 Mar 2024 09:08:18 +1100 Subject: [PATCH 18/18] Fix linter --- .../CHANGELOG.md | 5 ++++- .../CHANGELOG.md | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md index 0c515500a4f..8b35fd4c576 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md @@ -2,7 +2,10 @@ ## Unreleased -* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info +* Fix serializing scope_info when buffer overflows + ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) + +* Add `target_info` to Prometheus exporters when using OpenMetrics ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) ## 1.8.0-beta.1 diff --git a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md index 0ef05e4d966..7028f1b77ae 100644 --- a/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md +++ b/src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md @@ -2,7 +2,10 @@ ## Unreleased -* Added `target_info` to Prometheus exporters when using OpenMetrics format and fix serializing scope_info +* Fix serializing scope_info when buffer overflows + ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) + +* Add `target_info` to Prometheus exporters when using OpenMetrics ([#5407](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5407)) ## 1.8.0-beta.1