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()
{