From 1bdd2082b56986e6eb65ddacce37cf64d0a3e662 Mon Sep 17 00:00:00 2001 From: cijothomas Date: Fri, 15 May 2026 06:42:49 -0700 Subject: [PATCH 1/3] Stop validating View-provided stream Name against instrument name syntax --- src/OpenTelemetry/CHANGELOG.md | 13 +++++ .../Builder/MeterProviderBuilderExtensions.cs | 7 +-- .../Builder/MeterProviderBuilderSdk.cs | 9 --- .../Metrics/Reader/MetricReaderExt.cs | 10 +++- .../Metrics/View/MetricStreamConfiguration.cs | 19 ++----- .../Metrics/MetricViewTests.cs | 55 +++++++++++++------ 6 files changed, 66 insertions(+), 47 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 4ee7bea15a7..1a3377daf6f 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -6,6 +6,19 @@ Notes](../../RELEASENOTES.md). ## Unreleased +* The View-provided metric stream `Name` (set via + `MetricStreamConfiguration.Name` or the `AddView(instrumentName, name)` + overload) is no longer validated against the + [instrument name syntax](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-name-syntax). + Previously, supplying a name that did not match the syntax threw + `ArgumentException` (or, when set inside a Func-based view, caused the + metric to be silently ignored). With this change, View-provided names are + accepted as-is, allowing Views to be used to rename instruments to names + that fall outside the syntax (e.g. legacy/third-party names, OS-level + counter names). Aligns with OpenTelemetry specification clarification + [#5094](https://github.com/open-telemetry/opentelemetry-specification/pull/5094). + Direct instrument creation via `Meter` continues to enforce the syntax. + * Fix incorrect validation of `OTEL_BSP_*` and `OTEL_BLRP_*` environment variables. ([#7187](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7187)) diff --git a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs index ee112149708..892c0212249 100644 --- a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs @@ -105,10 +105,9 @@ public static MeterProviderBuilder AddView(this MeterProviderBuilder meterProvid { Guard.ThrowIfNull(instrumentName); - if (!MeterProviderBuilderSdk.IsValidInstrumentName(name)) - { - throw new ArgumentException($"Custom view name {name} is invalid.", nameof(name)); - } + // Per the OpenTelemetry specification, the View-provided stream name + // is not subject to the instrument name syntax and the SDK MUST NOT + // validate it against that syntax. #pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1 #if NET || NETSTANDARD2_1_OR_GREATER diff --git a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderSdk.cs b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderSdk.cs index 2381d8f8a76..9f65365cabf 100644 --- a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderSdk.cs +++ b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderSdk.cs @@ -62,15 +62,6 @@ public MeterProviderBuilderSdk(IServiceProvider serviceProvider) public static bool IsValidInstrumentName(string instrumentName) => !string.IsNullOrWhiteSpace(instrumentName) && InstrumentNameRegex.IsMatch(instrumentName); - /// - /// Returns whether the given custom view name is valid according to the specification. - /// - /// See specification: . - /// The view name. - /// Boolean indicating if the instrument is valid. - public static bool IsValidViewName(string customViewName) => - customViewName == null || InstrumentNameRegex.IsMatch(customViewName); // Only validate the view name in case it's not null. In case it's null, the view name will be the instrument name as per the spec. - public void RegisterProvider(MeterProviderSdk meterProvider) { Debug.Assert(meterProvider != null, "meterProvider was null"); diff --git a/src/OpenTelemetry/Metrics/Reader/MetricReaderExt.cs b/src/OpenTelemetry/Metrics/Reader/MetricReaderExt.cs index 2735ea3c5fe..a1bb7c3d8f7 100644 --- a/src/OpenTelemetry/Metrics/Reader/MetricReaderExt.cs +++ b/src/OpenTelemetry/Metrics/Reader/MetricReaderExt.cs @@ -131,12 +131,18 @@ internal virtual List AddMetricWithViews(Instrument instrument, List /// - /// Note: If not provided the instrument name will be used. + /// Note: If not provided the instrument name will be used. + /// Per the OpenTelemetry specification, the View-provided stream + /// name is not subject to the instrument name syntax and the SDK + /// MUST NOT validate it against that syntax. /// - public string? Name - { - get; - set - { - if (value != null && !MeterProviderBuilderSdk.IsValidViewName(value)) - { - throw new ArgumentException($"Custom view name {value} is invalid.", nameof(value)); - } - - field = value; - } - } + public string? Name { get; set; } /// /// Gets or sets the optional description of the metric stream. diff --git a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs index d4ef2e7636e..969cd100d07 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricViewTests.cs @@ -34,25 +34,45 @@ public void ViewToRenameMetric() [Theory] [MemberData(nameof(MetricTestData.InvalidInstrumentNames), MemberType = typeof(MetricTestData))] - public void AddViewWithInvalidNameThrowsArgumentException(string viewNewName) + public void AddViewWithNonInstrumentSyntaxNameAccepted_StringOverload(string viewNewName) { + // Per spec, View-provided stream names are NOT subject to the + // instrument name syntax. Names that would be invalid as instrument + // names (and previously threw ArgumentException) MUST now be accepted. var exportedItems = new List(); - using var meter1 = new Meter("AddViewWithInvalidNameThrowsArgumentException"); - - var ex = Assert.Throws(() => BuildMeterProvider(out var meterProvider, builder => builder - .AddMeter(meter1.Name) + using var meter = new Meter("AddViewWithNonInstrumentSyntaxNameAccepted_StringOverload"); + using var container = BuildMeterProvider(out var meterProvider, builder => builder + .AddMeter(meter.Name) .AddView("name1", viewNewName) - .AddInMemoryExporter(exportedItems))); + .AddInMemoryExporter(exportedItems)); - Assert.Contains($"Custom view name {viewNewName} is invalid.", ex.Message, StringComparison.Ordinal); + var counter = meter.CreateCounter("name1"); + counter.Add(10); + meterProvider.ForceFlush(MaxTimeToAllowForFlush); - ex = Assert.Throws(() => BuildMeterProvider(out var meterProvider, builder => builder - .AddMeter(meter1.Name) + Assert.Single(exportedItems); + Assert.Equal(viewNewName, exportedItems[0].Name); + } + + [Theory] + [MemberData(nameof(MetricTestData.InvalidInstrumentNames), MemberType = typeof(MetricTestData))] + public void AddViewWithNonInstrumentSyntaxNameAccepted_ConfigOverload(string viewNewName) + { + var exportedItems = new List(); + + using var meter = new Meter("AddViewWithNonInstrumentSyntaxNameAccepted_ConfigOverload"); + using var container = BuildMeterProvider(out var meterProvider, builder => builder + .AddMeter(meter.Name) .AddView("name1", new MetricStreamConfiguration() { Name = viewNewName }) - .AddInMemoryExporter(exportedItems))); + .AddInMemoryExporter(exportedItems)); - Assert.Contains($"Custom view name {viewNewName} is invalid.", ex.Message, StringComparison.Ordinal); + var counter = meter.CreateCounter("name1"); + counter.Add(10); + meterProvider.ForceFlush(MaxTimeToAllowForFlush); + + Assert.Single(exportedItems); + Assert.Equal(viewNewName, exportedItems[0].Name); } [Fact] @@ -295,21 +315,20 @@ public void ViewToRenameMetricConditionally() [Theory] [MemberData(nameof(MetricTestData.InvalidInstrumentNames), MemberType = typeof(MetricTestData))] - public void ViewWithInvalidNameIgnoredConditionally(string viewNewName) + public void ViewWithNonInstrumentSyntaxNameAcceptedConditionally(string viewNewName) { + // Per spec, View-provided stream names are NOT subject to the + // instrument name syntax. Names that would be invalid as instrument + // names MUST still be accepted when supplied via a View. using var meter1 = new Meter("ViewToRenameMetricConditionallyTest"); var exportedItems = new List(); using var container = BuildMeterProvider(out var meterProvider, builder => builder .AddMeter(meter1.Name) - - // since here it's a func, we can't validate the name right away - // so the view is allowed to be added, but upon instrument creation it's going to be ignored. .AddView((instrument) => { if (instrument.Meter.Name.Equals(meter1.Name, StringComparison.OrdinalIgnoreCase) && instrument.Name.Equals("name1", StringComparison.OrdinalIgnoreCase)) { - // invalid instrument name as per the spec return new MetricStreamConfiguration() { Name = viewNewName, Description = "new description" }; } else @@ -319,14 +338,14 @@ public void ViewWithInvalidNameIgnoredConditionally(string viewNewName) }) .AddInMemoryExporter(exportedItems)); - // Because the MetricStreamName passed is invalid, the view is ignored, - // and default aggregation is used. var counter1 = meter1.CreateCounter("name1", "unit", "original_description"); counter1.Add(10); meterProvider.ForceFlush(MaxTimeToAllowForFlush); Assert.Single(exportedItems); + Assert.Equal(viewNewName, exportedItems[0].Name); + Assert.Equal("new description", exportedItems[0].Description); } [Theory] From 930412fc91ea36d624003bd97ea048da35804ce7 Mon Sep 17 00:00:00 2001 From: cijothomas Date: Fri, 15 May 2026 07:12:47 -0700 Subject: [PATCH 2/3] Reword changelog and MetricStreamConfiguration.Name doc --- src/OpenTelemetry/CHANGELOG.md | 11 ++++++----- .../Metrics/View/MetricStreamConfiguration.cs | 8 +++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 1a3377daf6f..3e9be321264 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -11,11 +11,12 @@ Notes](../../RELEASENOTES.md). overload) is no longer validated against the [instrument name syntax](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-name-syntax). Previously, supplying a name that did not match the syntax threw - `ArgumentException` (or, when set inside a Func-based view, caused the - metric to be silently ignored). With this change, View-provided names are - accepted as-is, allowing Views to be used to rename instruments to names - that fall outside the syntax (e.g. legacy/third-party names, OS-level - counter names). Aligns with OpenTelemetry specification clarification + `ArgumentException` (or, when set inside a Func-based view, caused the View + to be silently ignored and the instrument to be exported under its original + name). With this change, View-provided names are accepted as-is, allowing + Views to be used to rename instruments to names that fall outside the + syntax (e.g. legacy/third-party names, OS-level counter names). Aligns + with OpenTelemetry specification clarification [#5094](https://github.com/open-telemetry/opentelemetry-specification/pull/5094). Direct instrument creation via `Meter` continues to enforce the syntax. diff --git a/src/OpenTelemetry/Metrics/View/MetricStreamConfiguration.cs b/src/OpenTelemetry/Metrics/View/MetricStreamConfiguration.cs index 32f9294d56e..b9a579b3499 100644 --- a/src/OpenTelemetry/Metrics/View/MetricStreamConfiguration.cs +++ b/src/OpenTelemetry/Metrics/View/MetricStreamConfiguration.cs @@ -27,9 +27,11 @@ public class MetricStreamConfiguration /// /// /// Note: If not provided the instrument name will be used. - /// Per the OpenTelemetry specification, the View-provided stream - /// name is not subject to the instrument name syntax and the SDK - /// MUST NOT validate it against that syntax. + /// The name supplied here is used as-is; it is not required to + /// follow the instrument name syntax. This lets you rename instruments + /// to names that the OpenTelemetry instrument naming rules would + /// otherwise reject (for example, legacy or third-party names, or + /// OS-level counter names). /// public string? Name { get; set; } From a3e5d2afb44a5667eae9af3fa1b7fdf440019871 Mon Sep 17 00:00:00 2001 From: cijothomas Date: Tue, 19 May 2026 21:46:55 -0700 Subject: [PATCH 3/3] Address review: shorten CHANGELOG entry; link spec in code comment --- src/OpenTelemetry/CHANGELOG.md | 17 ++++------------- .../Builder/MeterProviderBuilderExtensions.cs | 3 ++- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index e86f1dc567b..7554815ed68 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -6,19 +6,10 @@ Notes](../../RELEASENOTES.md). ## Unreleased -* The View-provided metric stream `Name` (set via - `MetricStreamConfiguration.Name` or the `AddView(instrumentName, name)` - overload) is no longer validated against the - [instrument name syntax](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#instrument-name-syntax). - Previously, supplying a name that did not match the syntax threw - `ArgumentException` (or, when set inside a Func-based view, caused the View - to be silently ignored and the instrument to be exported under its original - name). With this change, View-provided names are accepted as-is, allowing - Views to be used to rename instruments to names that fall outside the - syntax (e.g. legacy/third-party names, OS-level counter names). Aligns - with OpenTelemetry specification clarification - [#5094](https://github.com/open-telemetry/opentelemetry-specification/pull/5094). - Direct instrument creation via `Meter` continues to enforce the syntax. +* Stop validating View-provided metric stream `Name` against the instrument + name syntax, per + [spec clarification](https://github.com/open-telemetry/opentelemetry-specification/pull/5094). + ([#7300](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7300)) * Fix incorrect validation of `OTEL_BSP_*` and `OTEL_BLRP_*` environment variables. diff --git a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs index 892c0212249..bcad6d8c5a4 100644 --- a/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs +++ b/src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderExtensions.cs @@ -107,7 +107,8 @@ public static MeterProviderBuilder AddView(this MeterProviderBuilder meterProvid // Per the OpenTelemetry specification, the View-provided stream name // is not subject to the instrument name syntax and the SDK MUST NOT - // validate it against that syntax. + // validate it against that syntax. See: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#view #pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1 #if NET || NETSTANDARD2_1_OR_GREATER