Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,41 @@ public class MetricsOptions
public IList<InstrumentRule> Rules { get; } = null!;
}
}
namespace Microsoft.Extensions.Diagnostics.Tracing
{
public interface ITracingBuilder
{
Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }
}
public class TracingRule
{
public TracingRule(string? sourceName, string? operationName, string? listenerName, ActivitySourceScopes scopes, bool enable) { }
public string? SourceName { get; }
public string? OperationName { get; }
public string? ListenerName { get; }
public ActivitySourceScopes Scopes { get; }
public bool Enable { get; }
}
[Flags]
public enum ActivitySourceScopes
{
None = 0,
Global = 1,
Local = 2
}
public static partial class TracingBuilderExtensions
{
public static ITracingBuilder AddListener(this ITracingBuilder builder, Func<IServiceProvider, System.Diagnostics.ActivityListener> factory) { throw null!; }
public static ITracingBuilder ClearListeners(this ITracingBuilder builder) { throw null!; }

public static ITracingBuilder EnableTracing(this ITracingBuilder builder, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local) => throw null!;
public static TracingOptions EnableTracing(this TracingOptions options, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local) => throw null!;

public static ITracingBuilder DisableTracing(this ITracingBuilder builder, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local) => throw null!;
public static TracingOptions DisableTracing(this TracingOptions options, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local) => throw null!;
}
public class TracingOptions
{
public List<TracingRule> Rules { get; } = null!;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<ProjectReference Include="$(LibrariesProjectRoot)System.Collections\ref\System.Collections.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.ComponentModel\ref\System.ComponentModel.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\ref\System.Runtime.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)System.Diagnostics.DiagnosticSource\ref\System.Diagnostics.DiagnosticSource.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Extensions.Diagnostics.Tracing
{
/// <summary>
/// Represents scopes used by <see cref="TracingRule"/> to distinguish between activity sources created directly
/// via <see cref="System.Diagnostics.ActivitySource"/> constructors (<see cref="Global"/>) and those created via
/// dependency injection with <see cref="System.Diagnostics.ActivitySourceFactory.Create(System.Diagnostics.ActivitySourceOptions)"/> (<see cref="Local"/>).
/// </summary>
[Flags]
public enum ActivitySourceScopes
{
/// <summary>
/// No scope is specified. This field should not be used.
/// </summary>
None = 0,

/// <summary>
/// Indicates <see cref="System.Diagnostics.ActivitySource"/> instances created via constructors.
/// </summary>
Global = 1,

/// <summary>
/// Indicates <see cref="System.Diagnostics.ActivitySource"/> instances created via <see cref="System.Diagnostics.ActivitySourceFactory"/>.
/// </summary>
Local = 2
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.Extensions.Diagnostics.Tracing
{
/// <summary>
/// Configures the tracing system by registering <see cref="System.Diagnostics.ActivityListener"/> instances and using
/// rules to determine which <see cref="System.Diagnostics.ActivitySource"/> and <see cref="System.Diagnostics.Activity"/>
/// instances are enabled.
/// </summary>
public interface ITracingBuilder
{
/// <summary>
/// Gets the application service collection that's used by extension methods to register services.
/// </summary>
IServiceCollection Services { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.Diagnostics.Tracing
{
/// <summary>
/// Extension methods for <see cref="ITracingBuilder"/> to add or clear <see cref="ActivityListener"/> registrations.
/// </summary>
public static partial class TracingBuilderExtensions
{
/// <summary>
/// Registers a new <see cref="ActivityListener"/> using the supplied factory to materialise the instance from the service provider.
/// </summary>
/// <param name="builder">The <see cref="ITracingBuilder"/>.</param>
/// <param name="factory">A factory function that produces the listener instance.</param>
/// <returns>Returns the original <see cref="ITracingBuilder"/> for chaining.</returns>
/// <remarks>
/// The tracing builder takes ownership of the listener returned by <paramref name="factory"/>
/// and wraps it to apply the rules described by the bound <see cref="TracingOptions"/>. Callers
/// should not retain a reference to the returned instance, dispose it directly, or call
/// <see cref="ActivityListener.RefreshSources"/> on it: only the wrapper is registered with
/// <see cref="ActivitySource"/>, so calling <see cref="ActivityListener.RefreshSources"/> on
/// the user-supplied instance has no effect. The builder re-evaluates the listener
/// automatically when <see cref="TracingOptions"/> change.
/// </remarks>
public static ITracingBuilder AddListener(this ITracingBuilder builder, Func<IServiceProvider, ActivityListener> factory)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(factory);
builder.Services.AddSingleton<ActivityListener>(factory);
return builder;
}

/// <summary>
/// Removes all <see cref="ActivityListener"/> registrations from the dependency injection container.
/// </summary>
/// <param name="builder">The <see cref="ITracingBuilder"/>.</param>
/// <returns>Returns the original <see cref="ITracingBuilder"/> for chaining.</returns>
public static ITracingBuilder ClearListeners(this ITracingBuilder builder)
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.RemoveAll<ActivityListener>();
return builder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.Extensions.Diagnostics.Tracing
{
/// <summary>
/// Extension methods for <see cref="ITracingBuilder"/> to configure tracing rules.
/// </summary>
public static partial class TracingBuilderExtensions
{
/// <summary>
/// Enables all activities for the given source, operation, listener, and scopes.
/// </summary>
/// <param name="builder">The <see cref="ITracingBuilder"/>.</param>
/// <param name="sourceName">The <see cref="ActivitySource.Name"/> or prefix. A null value matches all activity sources.</param>
/// <param name="operationName">The <see cref="Activity.OperationName"/>, exact match. A null or empty value matches all activities within the matching sources.</param>
/// <param name="listenerName">The <see cref="ActivityListener.Name"/>. A null or empty value matches all listeners.</param>
/// <param name="scopes">A bitwise combination of the enumeration values that specifies the scopes to consider. Defaults to all scopes.</param>
/// <returns>The original <see cref="ITracingBuilder"/> for chaining.</returns>
public static ITracingBuilder EnableTracing(this ITracingBuilder builder, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local)
=> builder.ConfigureRule(options => options.EnableTracing(sourceName, operationName, listenerName, scopes));

/// <summary>
/// Enables all activities for the given source, operation, listener, and scopes.
/// </summary>
/// <param name="options">The <see cref="TracingOptions"/>.</param>
/// <param name="sourceName">The <see cref="ActivitySource.Name"/> or prefix. A null value matches all activity sources.</param>
/// <param name="operationName">The <see cref="Activity.OperationName"/>, exact match. A null or empty value matches all activities within the matching sources.</param>
/// <param name="listenerName">The <see cref="ActivityListener.Name"/>. A null or empty value matches all listeners.</param>
/// <param name="scopes">A bitwise combination of the enumeration values that specifies the scopes to consider. Defaults to all scopes.</param>
/// <returns>The original <see cref="TracingOptions"/> for chaining.</returns>
public static TracingOptions EnableTracing(this TracingOptions options, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local)
=> options.AddRule(sourceName, operationName, listenerName, scopes, enable: true);

/// <summary>
/// Disables all activities for the given source, operation, listener, and scopes.
/// </summary>
/// <param name="builder">The <see cref="ITracingBuilder"/>.</param>
/// <param name="sourceName">The <see cref="ActivitySource.Name"/> or prefix. A null value matches all activity sources.</param>
/// <param name="operationName">The <see cref="Activity.OperationName"/>, exact match. A null or empty value matches all activities within the matching sources.</param>
/// <param name="listenerName">The <see cref="ActivityListener.Name"/>. A null or empty value matches all listeners.</param>
/// <param name="scopes">A bitwise combination of the enumeration values that specifies the scopes to consider. Defaults to all scopes.</param>
/// <returns>The original <see cref="ITracingBuilder"/> for chaining.</returns>
public static ITracingBuilder DisableTracing(this ITracingBuilder builder, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local)
=> builder.ConfigureRule(options => options.DisableTracing(sourceName, operationName, listenerName, scopes));

/// <summary>
/// Disables all activities for the given source, operation, listener, and scopes.
/// </summary>
/// <param name="options">The <see cref="TracingOptions"/>.</param>
/// <param name="sourceName">The <see cref="ActivitySource.Name"/> or prefix. A null value matches all activity sources.</param>
/// <param name="operationName">The <see cref="Activity.OperationName"/>, exact match. A null or empty value matches all activities within the matching sources.</param>
/// <param name="listenerName">The <see cref="ActivityListener.Name"/>. A null or empty value matches all listeners.</param>
/// <param name="scopes">A bitwise combination of the enumeration values that specifies the scopes to consider. Defaults to all scopes.</param>
/// <returns>The original <see cref="TracingOptions"/> for chaining.</returns>
public static TracingOptions DisableTracing(this TracingOptions options, string? sourceName = null, string? operationName = null, string? listenerName = null, ActivitySourceScopes scopes = ActivitySourceScopes.Global | ActivitySourceScopes.Local)
=> options.AddRule(sourceName, operationName, listenerName, scopes, enable: false);

private static ITracingBuilder ConfigureRule(this ITracingBuilder builder, Action<TracingOptions> configureOptions)
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.Configure(configureOptions);
return builder;
}

private static TracingOptions AddRule(this TracingOptions options, string? sourceName, string? operationName, string? listenerName, ActivitySourceScopes scopes, bool enable)
{
ArgumentNullException.ThrowIfNull(options);
options.Rules.Add(new TracingRule(sourceName, operationName, listenerName, scopes, enable));
return options;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;

namespace Microsoft.Extensions.Diagnostics.Tracing
{
/// <summary>
/// Represents options for configuring the tracing system.
/// </summary>
public class TracingOptions
{
/// <summary>
/// Gets a list of activity rules that identifies which activity sources, activities, and listeners are enabled.
/// </summary>
public List<TracingRule> Rules { get; } = new List<TracingRule>();
}
Comment on lines +11 to +17
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;

namespace Microsoft.Extensions.Diagnostics.Tracing
{
/// <summary>
/// Contains a set of parameters used to determine which activities are enabled for which listeners.
/// An unspecified <see cref="SourceName"/> matches all activity sources, an unspecified
/// <see cref="OperationName"/> matches all activities within the matching sources, and an unspecified
/// <see cref="ListenerName"/> matches all listeners.
/// </summary>
/// <remarks>
/// <para>The most specific rule that matches a given activity will be used. The priority of parameters is as follows:</para>
/// <para>- ListenerName, an exact match. See <see cref="ActivityListener.Name"/>.</para>
/// <para>- SourceName, either an exact match, the longest prefix match, or a wildcard pattern using a single <c>*</c>. See <see cref="ActivitySource.Name"/>.</para>
/// <para>- OperationName, an exact match. See <see cref="Activity.OperationName"/>.</para>
/// <para>- Scopes, where a more constrained scope is preferred over <c>Global | Local</c>.</para>
/// </remarks>
public class TracingRule
{
/// <summary>
/// Initializes a new instance of the <see cref="TracingRule"/> class.
/// </summary>
/// <param name="sourceName">The <see cref="ActivitySource.Name"/>, prefix, or pattern with a single <c>*</c> wildcard. A <see langword="null"/> or empty value matches all activity sources.</param>
/// <param name="operationName">The <see cref="Activity.OperationName"/>, exact match. A <see langword="null"/> or empty value matches all activities within the matching sources.</param>
/// <param name="listenerName">The <see cref="ActivityListener.Name"/>. A <see langword="null"/> or empty value matches all listeners.</param>
/// <param name="scopes">A bitwise combination of the enumeration values that specifies the scopes to consider.</param>
/// <param name="enable"><see langword="true"/> to enable matched activities for this listener; otherwise, <see langword="false"/>.</param>
/// <exception cref="ArgumentException"><paramref name="sourceName"/> contains more than one <c>*</c> wildcard.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="scopes"/> is <see cref="ActivitySourceScopes.None"/>.</exception>
public TracingRule(string? sourceName, string? operationName, string? listenerName, ActivitySourceScopes scopes, bool enable)
{
// Validate the wildcard pattern eagerly. The equivalent rule type in Microsoft.Extensions.Diagnostics
// for metrics defers this validation to the per-event matching path, so a malformed rule introduced
// via IOptionsMonitor reload throws out of arbitrary instrument operations later. We diverge from
// that here so configuration mistakes surface at bind time (or programmatic-construction call site)
// and never reach the StartActivity hot path. The metrics behaviour is shipped public surface and
// can't change without a breaking-change process; tracing is new, so we get the cleaner shape now.
if (!string.IsNullOrEmpty(sourceName))
{
int firstWildcard = sourceName.IndexOf('*');
if (firstWildcard >= 0 && sourceName.IndexOf('*', firstWildcard + 1) >= 0)
{
throw new ArgumentException("Only one '*' wildcard is allowed in an activity source name pattern.", nameof(sourceName));
}
}

SourceName = sourceName;
OperationName = operationName;
ListenerName = listenerName;
Scopes = scopes == ActivitySourceScopes.None
? throw new ArgumentOutOfRangeException(nameof(scopes), scopes, "The ActivitySourceScopes must be Global, Local, or both.")
: scopes;
Enable = enable;
}

/// <summary>
/// Gets the <see cref="ActivitySource.Name"/>, either an exact match, the longest prefix match, or a wildcard pattern using a single <c>*</c>.
/// </summary>
/// <value>
/// The activity source name. If <see langword="null"/> or empty, all activity sources are matched.
/// </value>
public string? SourceName { get; }

/// <summary>
/// Gets the <see cref="Activity.OperationName"/>, an exact match.
/// </summary>
/// <value>
/// The operation name. If <see langword="null"/> or empty, all activities within the matching sources are matched.
/// </value>
public string? OperationName { get; }

/// <summary>
/// Gets the <see cref="ActivityListener.Name"/>, an exact match.
/// </summary>
/// <value>
/// The listener name. If <see langword="null"/> or empty, all listeners are matched.
/// </value>
public string? ListenerName { get; }

/// <summary>
/// Gets the <see cref="ActivitySourceScopes"/>.
/// </summary>
public ActivitySourceScopes Scopes { get; }

/// <summary>
/// Gets a value that indicates whether matched activities are enabled for this listener.
/// </summary>
public bool Enable { get; }
}
}
Loading
Loading