Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddSource with predicate #5353

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/OpenTelemetry.Api/Metrics/MeterProviderBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public abstract MeterProviderBuilder AddInstrumentation<TInstrumentation>(

/// <summary>
/// Adds given Meter names to the list of subscribed meters.
/// <remarks>
/// When multiple matchers are added for the same meter name, the <see cref="MeterProvider"/>
/// will enable a meter if its name matches at least one of the provided names.
/// </remarks>
/// </summary>
/// <param name="names">Meter names.</param>
/// <returns>Returns <see cref="MeterProviderBuilder"/> for chaining.</returns>
Expand Down
5 changes: 5 additions & 0 deletions src/OpenTelemetry.Api/Trace/TracerProviderBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public abstract TracerProviderBuilder AddInstrumentation<TInstrumentation>(

/// <summary>
/// Adds the given <see cref="ActivitySource"/> names to the list of subscribed sources.
///
/// <remarks>
/// When multiple matchers are added for the same source name, the <see cref="TracerProvider"/>
/// will enable an ActivitySource if its name matches at least one of the provided names.
/// </remarks>
/// </summary>
/// <param name="names">Activity source names.</param>
/// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@ static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.Log
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.LoggerProviderBuilder!>! configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.LoggerProviderBuilder!>? configureBuilder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configureOptions) -> Microsoft.Extensions.Logging.ILoggingBuilder!
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddSource(this OpenTelemetry.Trace.TracerProviderBuilder! tracerProviderBuilder, System.Predicate<System.Diagnostics.ActivitySource!>! sourcePredicate) -> OpenTelemetry.Trace.TracerProviderBuilder!
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddMeter(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder, System.Predicate<System.Diagnostics.Metrics.Meter!>! meterPredicate) -> OpenTelemetry.Metrics.MeterProviderBuilder!
30 changes: 30 additions & 0 deletions src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,24 @@ public void LoggerProcessStateSkipped<TState>()
}
}

[NonEvent]
public void ActivitySourcePredicateException(string sourceName, Exception ex)
{
if (this.IsEnabled(EventLevel.Warning, EventKeywords.All))
{
this.ActivitySourcePredicateException(sourceName, ex.ToInvariantString());
}
}

[NonEvent]
public void MeterPredicateException(string meterName, Exception ex)
{
if (this.IsEnabled(EventLevel.Warning, EventKeywords.All))
{
this.MeterPredicateException(meterName, ex.ToInvariantString());
}
}

[Event(4, Message = "Unknown error in SpanProcessor event '{0}': '{1}'.", Level = EventLevel.Error)]
public void SpanProcessorException(string evnt, string ex)
{
Expand Down Expand Up @@ -346,6 +364,18 @@ public void MetricInstrumentRemoved(string instrumentName, string meterName)
this.WriteEvent(53, instrumentName, meterName);
}

[Event(54, Message = "Exception thrown when invoking ActivitySource predicate for source '{0}', ignoring it. Exception: '{1}'", Level = EventLevel.Warning)]
public void ActivitySourcePredicateException(string sourceName, string error)
{
this.WriteEvent(54, sourceName, error);
}

[Event(55, Message = "Exception thrown when invoking Meter predicate for meter name '{0}', ignoring it. Exception: '{1}'", Level = EventLevel.Warning)]
public void MeterPredicateException(string meterName, string error)
{
this.WriteEvent(55, meterName, error);
}

#if DEBUG
public class OpenTelemetryEventListener : EventListener
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,4 +368,36 @@ static MeterProviderBuilder SetExemplarFilter(

return meterProviderBuilder;
}

/// <summary>
/// Adds a predicate for <see cref="Meter"/> that is used to determine
/// if <see cref="MeterProvider"/> should subscribe to the meter.
///
/// <remarks>
/// When multiple predicates are added using this method or via <see cref="MeterProviderBuilder.AddMeter(string[])"/>,
/// the <see cref="MeterProvider"/> will enable a meter if at least one of the predicates returns true for it, or
/// if the meter name matches one provided in the <see cref="MeterProviderBuilder.AddMeter(string[])"/> overload.
/// </remarks>
/// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <param name="meterPredicate">Meter predicate - if it returns true, OpenTelemetry subscribes to the corresponding meter.</param>
/// <returns>Returns <see cref="MeterProviderBuilder"/> for chaining.</returns>
#if EXPOSE_EXPERIMENTAL_FEATURES
public
#else
internal
#endif
static MeterProviderBuilder AddMeter(this MeterProviderBuilder meterProviderBuilder, Predicate<Meter> meterPredicate)
{
Guard.ThrowIfNull(meterProviderBuilder);
meterProviderBuilder.ConfigureBuilder((_, builder) =>
{
if (builder is MeterProviderBuilderSdk meterProviderBuilderSdk)
{
meterProviderBuilderSdk.AddMeter(meterPredicate);
}
});

return meterProviderBuilder;
}
}
10 changes: 10 additions & 0 deletions src/OpenTelemetry/Metrics/Builder/MeterProviderBuilderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public MeterProviderBuilderSdk(IServiceProvider serviceProvider)

public List<string> MeterSources { get; } = new();

public List<Predicate<Meter>> MeterSelectionPredicates { get; } = new();

public List<Func<Instrument, MetricStreamConfiguration?>> ViewConfigs { get; } = new();

public int MetricLimit { get; private set; } = DefaultMetricLimit;
Expand Down Expand Up @@ -166,6 +168,14 @@ public override MeterProviderBuilder AddMeter(params string[] names)
return this;
}

public MeterProviderBuilder AddMeter(Predicate<Meter> meterPredicate)
{
Guard.ThrowIfNull(meterPredicate);
this.MeterSelectionPredicates.Add(meterPredicate!);

return this;
}

public MeterProviderBuilder AddReader(MetricReader reader)
{
Debug.Assert(reader != null, "reader was null");
Expand Down
58 changes: 45 additions & 13 deletions src/OpenTelemetry/Metrics/MeterProviderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,7 @@ internal MeterProviderSdk(
}

// Setup Listener
if (state.MeterSources.Any(s => WildcardHelper.ContainsWildcard(s)))
{
var regex = WildcardHelper.GetWildcardRegex(state.MeterSources);
this.shouldListenTo = instrument => regex.IsMatch(instrument.Meter.Name);
}
else if (state.MeterSources.Any())
{
var meterSourcesToSubscribe = new HashSet<string>(state.MeterSources, StringComparer.OrdinalIgnoreCase);
this.shouldListenTo = instrument => meterSourcesToSubscribe.Contains(instrument.Meter.Name);
}

OpenTelemetrySdkEventSource.Log.MeterProviderSdkEvent($"Listening to following meters = \"{string.Join(";", state.MeterSources)}\".");

this.shouldListenTo = this.GetPredicate(state);
this.listener = new MeterListener();
var viewConfigCount = this.viewConfigs.Count;

Expand Down Expand Up @@ -531,4 +519,48 @@ private void ApplySpecificationConfigurationKeys(IConfiguration configuration)
}
#endif
}

private Func<Instrument, bool> GetPredicate(MeterProviderBuilderSdk state)
{
List<Predicate<Meter>> predicates = new List<Predicate<Meter>>();

if (state.MeterSources.Any())
{
predicates.Add(this.GetNamePredicate(state));
}

predicates.AddRange(state.MeterSelectionPredicates);

return (instrument) =>
{
bool shouldListen = false;
for (int i = 0; i < predicates.Count && !shouldListen; i++)
{
try
{
shouldListen |= predicates[i](instrument.Meter);
}
catch (Exception ex)
{
OpenTelemetrySdkEventSource.Log.MeterPredicateException(instrument.Meter.Name, ex);
}
}

return shouldListen;
};
}

private Predicate<Meter> GetNamePredicate(MeterProviderBuilderSdk state)
{
Debug.Assert(state.MeterSources.Any(), "Should only be called when there are name-based source predicates.");

if (state.MeterSources.Any(s => WildcardHelper.ContainsWildcard(s)))
{
var regex = WildcardHelper.GetWildcardRegex(state.MeterSources);
return meter => regex.IsMatch(meter.Name);
}

var meterSourcesToSubscribe = new HashSet<string>(state.MeterSources, StringComparer.OrdinalIgnoreCase);
return meter => meterSourcesToSubscribe.Contains(meter.Name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,36 @@ public static TracerProvider Build(this TracerProviderBuilder tracerProviderBuil

throw new NotSupportedException($"Build is not supported on '{tracerProviderBuilder?.GetType().FullName ?? "null"}' instances.");
}

/// <summary>
/// Adds a predicate for <see cref="ActivitySource"/> that is used to determine
/// if <see cref="TracerProvider"/> should subscribe to the source.
///
/// <remarks>
/// When multiple predicates are added using this method or via <see cref="TracerProviderBuilder.AddSource(string[])"/>,
/// the <see cref="TracerProvider"/> will enable an ActivitySource if at least one of the predicates returns true for it, or
/// if the ActivitySource name matches one provided in the <see cref="TracerProviderBuilder.AddSource(string[])"/> overload.
/// </remarks>
/// </summary>
/// <param name="tracerProviderBuilder"><see cref="TracerProviderBuilder"/>.</param>
/// <param name="sourcePredicate">Activity Source predicate - if it returns true, OpenTelemetry subscribes to the corresponding source.</param>
/// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns>
#if EXPOSE_EXPERIMENTAL_FEATURES
public
#else
internal
#endif
static TracerProviderBuilder AddSource(this TracerProviderBuilder tracerProviderBuilder, Predicate<ActivitySource> sourcePredicate)
{
Guard.ThrowIfNull(sourcePredicate);
tracerProviderBuilder.ConfigureBuilder((_, builder) =>
{
if (builder is TracerProviderBuilderSdk tracerProviderBuilderSdk)
{
tracerProviderBuilderSdk.AddSource(sourcePredicate);
}
});

return tracerProviderBuilder;
}
}
9 changes: 9 additions & 0 deletions src/OpenTelemetry/Trace/Builder/TracerProviderBuilderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public TracerProviderBuilderSdk(IServiceProvider serviceProvider)

public List<string> Sources { get; } = new();

public List<Predicate<ActivitySource>> SourceSelectionPredicates { get; } = new();

public HashSet<string> LegacyActivityOperationNames { get; } = new(StringComparer.OrdinalIgnoreCase);

public Sampler? Sampler { get; private set; }
Expand Down Expand Up @@ -123,6 +125,13 @@ public override TracerProviderBuilder AddSource(params string[] names)
return this;
}

public TracerProviderBuilder AddSource(Predicate<ActivitySource> sourcePredicate)
{
Guard.ThrowIfNull(sourcePredicate);
this.SourceSelectionPredicates.Add(sourcePredicate!);
return this;
}

public TracerProviderBuilder AddProcessor(BaseProcessor<Activity> processor)
{
Debug.Assert(processor != null, "processor was null");
Expand Down
93 changes: 54 additions & 39 deletions src/OpenTelemetry/Trace/TracerProviderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,45 +244,7 @@ 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 (state.Sources.Any())
{
// Validation of source name is already done in builder.
if (state.Sources.Any(s => WildcardHelper.ContainsWildcard(s)))
{
var regex = WildcardHelper.GetWildcardRegex(state.Sources);

// Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to
// or not.
listener.ShouldListenTo = (activitySource) =>
this.supportLegacyActivity ?
string.IsNullOrEmpty(activitySource.Name) || regex.IsMatch(activitySource.Name) :
regex.IsMatch(activitySource.Name);
}
else
{
var activitySources = new HashSet<string>(state.Sources, StringComparer.OrdinalIgnoreCase);

if (this.supportLegacyActivity)
{
activitySources.Add(string.Empty);
}

// Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to
// or not.
listener.ShouldListenTo = (activitySource) => activitySources.Contains(activitySource.Name);
}
}
else
{
if (this.supportLegacyActivity)
{
listener.ShouldListenTo = (activitySource) => string.IsNullOrEmpty(activitySource.Name);
}
}

listener.ShouldListenTo = this.GetPredicate(state);
ActivitySource.AddActivityListener(listener);
this.listener = listener;
OpenTelemetrySdkEventSource.Log.TracerProviderSdkEvent("TracerProvider built successfully.");
Expand Down Expand Up @@ -464,6 +426,59 @@ private static ActivitySamplingResult PropagateOrIgnoreData(in ActivityContext p
: ActivitySamplingResult.None;
}

private Func<ActivitySource, bool> GetPredicate(TracerProviderBuilderSdk state)
{
List<Predicate<ActivitySource>> predicates = new List<Predicate<ActivitySource>>();

// Sources can be empty. This happens when user
// is only interested in InstrumentationLibraries
// which do not depend on ActivitySources.
if (state.Sources.Any())
{
predicates.Add(this.GetNamePredicate(state));
}

predicates.AddRange(state.SourceSelectionPredicates);

if (this.supportLegacyActivity)
{
predicates.Add((activitySource) => string.IsNullOrEmpty(activitySource.Name));
}

return (activitySource) =>
{
bool shouldListen = false;
for (int i = 0; i < predicates.Count && !shouldListen; i++)
{
try
{
shouldListen |= predicates[i](activitySource);
}
catch (Exception ex)
{
OpenTelemetrySdkEventSource.Log.ActivitySourcePredicateException(activitySource.Name, ex);
}
}

return shouldListen;
};
}

private Predicate<ActivitySource> GetNamePredicate(TracerProviderBuilderSdk state)
{
Debug.Assert(state.Sources.Any(), "Should only be called when there are name-based source predicates.");

// Validation of source name is already done in builder.
if (state.Sources.Any(s => WildcardHelper.ContainsWildcard(s)))
{
var regex = WildcardHelper.GetWildcardRegex(state.Sources);
return (activitySource) => regex.IsMatch(activitySource.Name);
}

var activitySources = new HashSet<string>(state.Sources, StringComparer.OrdinalIgnoreCase);
return (activitySource) => activitySources.Contains(activitySource.Name);
}

private void RunGetRequestedDataAlwaysOnSampler(Activity activity)
{
activity.IsAllDataRequested = true;
Expand Down
Loading
Loading