diff --git a/src/OpenTelemetry.Api/CHANGELOG.md b/src/OpenTelemetry.Api/CHANGELOG.md index e2e84e03a8d..4d83e895873 100644 --- a/src/OpenTelemetry.Api/CHANGELOG.md +++ b/src/OpenTelemetry.Api/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Improved wildcard support for `AddSource`, `AddMeter` to cover `?` (which + matches exactly one character). + ([#2875](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2875)) + ## 1.2.0-rc2 Released 2022-Feb-02 diff --git a/src/OpenTelemetry/Internal/WildcardHelper.cs b/src/OpenTelemetry/Internal/WildcardHelper.cs new file mode 100644 index 00000000000..778d2d883b8 --- /dev/null +++ b/src/OpenTelemetry/Internal/WildcardHelper.cs @@ -0,0 +1,47 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace OpenTelemetry; + +internal static class WildcardHelper +{ + public static bool ContainsWildcard(string value) + { + if (value == null) + { + return false; + } + + return value.Contains('*') || value.Contains('?'); + } + + public static Regex GetWildcardRegex(IEnumerable patterns = default) + { + if (patterns == null) + { + return null; + } + + var convertedPattern = string.Join( + "|", + from p in patterns select "(?:" + Regex.Escape(p).Replace("\\*", ".*").Replace("\\?", ".") + ')'); + return new Regex('^' + convertedPattern + '$', RegexOptions.Compiled | RegexOptions.IgnoreCase); + } +} diff --git a/src/OpenTelemetry/Metrics/MeterProviderSdk.cs b/src/OpenTelemetry/Metrics/MeterProviderSdk.cs index 6d7cd776b61..dab408f3e22 100644 --- a/src/OpenTelemetry/Metrics/MeterProviderSdk.cs +++ b/src/OpenTelemetry/Metrics/MeterProviderSdk.cs @@ -19,7 +19,6 @@ using System.Diagnostics; using System.Diagnostics.Metrics; using System.Linq; -using System.Text.RegularExpressions; using OpenTelemetry.Internal; using OpenTelemetry.Resources; @@ -82,9 +81,9 @@ internal MeterProviderSdk( // Setup Listener Func shouldListenTo = instrument => false; - if (meterSources.Any(s => s.Contains('*'))) + if (meterSources.Any(s => WildcardHelper.ContainsWildcard(s))) { - var regex = GetWildcardRegex(meterSources); + var regex = WildcardHelper.GetWildcardRegex(meterSources); shouldListenTo = instrument => regex.IsMatch(instrument.Meter.Name); } else if (meterSources.Any()) @@ -235,12 +234,6 @@ internal MeterProviderSdk( } this.listener.Start(); - - static Regex GetWildcardRegex(IEnumerable collection) - { - var pattern = '^' + string.Join("|", from name in collection select "(?:" + Regex.Escape(name).Replace("\\*", ".*") + ')') + '$'; - return new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); - } } internal Resource Resource { get; } diff --git a/src/OpenTelemetry/Trace/TracerProviderSdk.cs b/src/OpenTelemetry/Trace/TracerProviderSdk.cs index a43658810c9..973a1c450de 100644 --- a/src/OpenTelemetry/Trace/TracerProviderSdk.cs +++ b/src/OpenTelemetry/Trace/TracerProviderSdk.cs @@ -19,7 +19,6 @@ using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; -using System.Text.RegularExpressions; using OpenTelemetry.Internal; using OpenTelemetry.Resources; @@ -50,13 +49,13 @@ internal TracerProviderSdk( this.supportLegacyActivity = legacyActivityOperationNames.Count > 0; bool legacyActivityWildcardMode = false; - Regex legacyActivityWildcardModeRegex = null; + var legacyActivityWildcardModeRegex = WildcardHelper.GetWildcardRegex(); foreach (var legacyName in legacyActivityOperationNames) { - if (legacyName.Contains('*')) + if (WildcardHelper.ContainsWildcard(legacyName)) { legacyActivityWildcardMode = true; - legacyActivityWildcardModeRegex = GetWildcardRegex(legacyActivityOperationNames); + legacyActivityWildcardModeRegex = WildcardHelper.GetWildcardRegex(legacyActivityOperationNames); break; } } @@ -211,27 +210,15 @@ internal TracerProviderSdk( this.getRequestedDataAction = this.RunGetRequestedDataOtherSampler; } + // Sources can be null. This happens when user + // is only interested in InstrumentationLibraries + // which do not depend on ActivitySources. if (sources.Any()) { - // Sources can be null. This happens when user - // is only interested in InstrumentationLibraries - // which do not depend on ActivitySources. - - var wildcardMode = false; - // Validation of source name is already done in builder. - foreach (var name in sources) - { - if (name.Contains('*')) - { - wildcardMode = true; - break; - } - } - - if (wildcardMode) + if (sources.Any(s => WildcardHelper.ContainsWildcard(s))) { - var regex = GetWildcardRegex(sources); + var regex = WildcardHelper.GetWildcardRegex(sources); // Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to // or not. @@ -264,12 +251,6 @@ internal TracerProviderSdk( ActivitySource.AddActivityListener(listener); this.listener = listener; - - Regex GetWildcardRegex(IEnumerable collection) - { - var pattern = '^' + string.Join("|", from name in collection select "(?:" + Regex.Escape(name).Replace("\\*", ".*") + ')') + '$'; - return new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); - } } internal Resource Resource { get; } diff --git a/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs b/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs index 223c00c401e..cb5d1447fef 100644 --- a/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs +++ b/test/OpenTelemetry.Tests/Metrics/MetricAPITest.cs @@ -249,7 +249,7 @@ public void MeterSourcesWildcardSupportMatchTest(bool hasView) var exportedItems = new List(); var meterProviderBuilder = Sdk.CreateMeterProviderBuilder() - .AddMeter("AbcCompany.XyzProduct.*") + .AddMeter("AbcCompany.XyzProduct.Component?") .AddMeter("DefCompany.*.ComponentC") .AddMeter("GhiCompany.qweProduct.ComponentN") // Mixing of non-wildcard meter name and wildcard meter name. .AddInMemoryExporter(exportedItems); diff --git a/test/OpenTelemetry.Tests/Trace/TracerProviderSdkTest.cs b/test/OpenTelemetry.Tests/Trace/TracerProviderSdkTest.cs index 2fcf829cd34..cd76037f32e 100644 --- a/test/OpenTelemetry.Tests/Trace/TracerProviderSdkTest.cs +++ b/test/OpenTelemetry.Tests/Trace/TracerProviderSdkTest.cs @@ -34,6 +34,86 @@ public TracerProviderSdkTest() Activity.DefaultIdFormat = ActivityIdFormat.W3C; } + [Fact] + public void TracerProviderSdkAddSource() + { + using var source1 = new ActivitySource($"{Utils.GetCurrentMethodName()}.1"); + using var source2 = new ActivitySource($"{Utils.GetCurrentMethodName()}.2"); + + using var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource(source1.Name) + .Build(); + + using (var activity = source1.StartActivity("test")) + { + Assert.NotNull(activity); + } + + using (var activity = source2.StartActivity("test")) + { + Assert.Null(activity); + } + } + + [Fact] + public void TracerProviderSdkAddSourceWithWildcards() + { + using var source1 = new ActivitySource($"{Utils.GetCurrentMethodName()}.A"); + using var source2 = new ActivitySource($"{Utils.GetCurrentMethodName()}.Ab"); + using var source3 = new ActivitySource($"{Utils.GetCurrentMethodName()}.Abc"); + using var source4 = new ActivitySource($"{Utils.GetCurrentMethodName()}.B"); + + using (var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource($"{Utils.GetCurrentMethodName()}.*") + .Build()) + { + using (var activity = source1.StartActivity("test")) + { + Assert.NotNull(activity); + } + + using (var activity = source2.StartActivity("test")) + { + Assert.NotNull(activity); + } + + using (var activity = source3.StartActivity("test")) + { + Assert.NotNull(activity); + } + + using (var activity = source4.StartActivity("test")) + { + Assert.NotNull(activity); + } + } + + using (var tracerProvider = Sdk.CreateTracerProviderBuilder() + .AddSource($"{Utils.GetCurrentMethodName()}.?") + .Build()) + { + using (var activity = source1.StartActivity("test")) + { + Assert.NotNull(activity); + } + + using (var activity = source2.StartActivity("test")) + { + Assert.Null(activity); + } + + using (var activity = source3.StartActivity("test")) + { + Assert.Null(activity); + } + + using (var activity = source4.StartActivity("test")) + { + Assert.NotNull(activity); + } + } + } + [Fact] public void TracerProviderSdkInvokesSamplingWithCorrectParameters() {