Skip to content
Draft
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
191 changes: 191 additions & 0 deletions docs/core/diagnostics/distributed-tracing-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
---
title: Configure distributed tracing in .NET
description: Learn how to register tracing listeners and configure activity rules in .NET.
ms.date: 08/27/2026
ai-usage: ai-assisted
dev_langs:
- "csharp"
- "vb"
---

# Configure distributed tracing in .NET

In .NET 11 and later, the `Microsoft.Extensions.Diagnostics.Tracing` APIs let you register <xref:System.Diagnostics.ActivityListener> instances and use rules to select the <xref:System.Diagnostics.ActivitySource> and <xref:System.Diagnostics.Activity> instances that each listener receives. Define rules in code or load them from <xref:Microsoft.Extensions.Configuration.IConfiguration>.

> [!IMPORTANT]
> Tracing rules affect only listeners registered through this tracing infrastructure. They don't affect unrelated `ActivityListener` instances, including listeners that telemetry exporters create and manage. Configure those listeners or exporters separately.

## Register tracing

The APIs are available in the [`Microsoft.Extensions.Diagnostics`](https://www.nuget.org/packages/Microsoft.Extensions.Diagnostics) package. Apps that reference the `Microsoft.AspNetCore.App` shared framework already include the assembly.

Call <xref:Microsoft.Extensions.DependencyInjection.TracingServiceExtensions.AddTracing*> on an <xref:Microsoft.Extensions.DependencyInjection.IServiceCollection>. In the callback:

- Call <xref:Microsoft.Extensions.Diagnostics.Tracing.TracingBuilderExtensions.AddListener*> to register each listener. The listener name identifies the listener in configuration rules.
- Configure the listener's sampling and notification delegates through <xref:Microsoft.Extensions.Diagnostics.Tracing.ActivityListenerBuilder>. The tracing infrastructure owns `ShouldListenTo`, source subscriptions, and the listener lifetime.
- Call <xref:Microsoft.Extensions.Diagnostics.Tracing.TracingBuilderConfigurationExtensions.AddConfiguration*> with the configuration section that contains the rules. For a section named `Tracing`, pass `configuration.GetSection("Tracing")`.

The [complete sample](#run-the-sample) shows all three calls. A .NET host activates tracing subscriptions when the host starts. If you use dependency injection without a host, resolve the registered <xref:System.Diagnostics.ActivitySourceFactory> to construct the listeners and activate their subscriptions. The complete sample resolves the factory directly.

## Configure tracing

Set tracing rules through configuration or in code. If no rule matches a listener, source, operation, and scope, tracing remains disabled for that combination.

### Configure tracing without code

For apps that use configuration, add a `Tracing` section to *appsettings.json* or another [configuration provider](../extensions/configuration-providers.md). Then pass that section to `AddConfiguration`:

```csharp
tracing.AddConfiguration(configuration.GetSection("Tracing"));
```

The following example shows the supported rule sections and rule levels:

:::code language="json" source="./snippets/distributed-tracing-configuration/appsettings.json":::

Add rules directly under an `EnabledTracing`, `EnabledGlobalTracing`, or `EnabledLocalTracing` section. Don't add a `Rules` level.

#### Rule sections

Choose a rule section based on the source scope and whether the rule targets one listener:

| Section | Matching listeners | Matching activity sources |
|---|---|---|
| `EnabledTracing` | All registered listeners. | Global and local sources. |
| `EnabledGlobalTracing` | All registered listeners. | Global sources only. |
| `EnabledLocalTracing` | All registered listeners. | Local sources only. |
| `{Listener}:EnabledTracing` | The named listener. | Global and local sources. |
| `{Listener}:EnabledGlobalTracing` | The named listener. | Global sources only. |
| `{Listener}:EnabledLocalTracing` | The named listener. | Local sources only. |

Listener names match the names passed to `AddListener`. Section names and listener names aren't case-sensitive.

#### Rule levels

Under any rule section, use one of the following key forms:

| Key form | Rule level | Match |
|---|---|---|
| `Default` | Default. | Every source and operation. |
| `{SourceName}` | Activity source. | Every operation from matching sources. |
| `{SourceName}:Default` | Activity source. | Every operation from matching sources. This form lets a source also contain operation rules. |
| `{SourceName}:{OperationName}` | Operation. | The named operation from matching sources. |

Set each leaf value to `true` to enable the match or `false` to disable it. For example, the preceding configuration:

- Disables tracing by default.
- Enables sources whose names start with `Contoso`.
- Enables `Contoso.Orders`, but disables its `HealthCheck` operation.
- Enables a source pattern that starts with `Contoso.` and ends with `.Storage`.
- Adds rules that apply only to global sources, local sources, or the listener named `Console`.

Use `Default` as a Boolean leaf. If `Default` contains operation children, the configuration treats `Default` as a literal activity-source name.

### Configure tracing with code

Call <xref:Microsoft.Extensions.Diagnostics.Tracing.TracingBuilderExtensions.EnableTracing*> and <xref:Microsoft.Extensions.Diagnostics.Tracing.TracingBuilderExtensions.DisableTracing*> on the <xref:Microsoft.Extensions.Diagnostics.Tracing.ITracingBuilder> passed to `AddTracing`.

## [C#](#tab/csharp)

```csharp
tracing.EnableTracing(sourceName: "Contoso");
tracing.DisableTracing(
sourceName: "Contoso.Orders",
operationName: "HealthCheck");
```

## [Visual Basic](#tab/visual-basic)

```vb
tracing.EnableTracing(sourceName:="Contoso")
tracing.DisableTracing(
sourceName:="Contoso.Orders",
operationName:="HealthCheck")
```

---

Omit `sourceName`, `operationName`, or `listenerName` to match all values at that level. By default, a programmatic rule applies to both global and local sources. Pass <xref:Microsoft.Extensions.Diagnostics.Tracing.ActivitySourceScopes.Global>, <xref:Microsoft.Extensions.Diagnostics.Tracing.ActivitySourceScopes.Local>, or both through the `scopes` argument to narrow the rule.

Configuration rules and programmatic rules form one ordered rule set. If equally specific rules conflict, the rule added last wins. The order of calls in the `AddTracing` callback therefore matters when `AddConfiguration`, `EnableTracing`, and `DisableTracing` add equally specific rules.

## Match activity-source names

Source names, operation names, and listener names use case-insensitive ordinal comparison. Source-name rules support these match forms:

- An empty source name matches every source.
- A source name without a wildcard matches by prefix. For example, `Contoso` matches `Contoso.Orders` and `ContosoPayments`; the matcher doesn't require a separator after the prefix.
- A source name with one `*` wildcard matches a prefix and suffix. For example, `Contoso.*.Storage` matches `Contoso.Orders.Storage`, but not `Contoso.Orders.Api`.
- A source name of `*` matches every source.

Operation names match exactly. They don't use prefix or wildcard matching.

## Apply rule precedence

For each listener, source, operation, and scope combination, the tracing infrastructure chooses one matching rule. It compares matching rules in this order:

- A listener-specific rule takes precedence over a rule for all listeners.
- A source rule takes precedence over a default rule. If two source expressions match, the longer expression takes precedence. The comparison uses the expression length, including a `*` wildcard.
- An operation rule takes precedence over a source-level rule.
- A global-only or local-only rule takes precedence over a rule that covers both scopes.
- The last rule takes precedence when all preceding factors are equal.

The comparison stops at the first difference. For example, a listener-specific default rule takes precedence over a source-specific rule that applies to all listeners because listener specificity is evaluated first.

Operation rules also control source subscriptions. A listener subscribes to a source that's disabled by default when an operation rule enables at least one operation from that source. The listener then returns <xref:System.Diagnostics.ActivitySamplingResult.None> and skips its callbacks for disabled operations.

## Choose global or local activity sources

The tracing infrastructure distinguishes two activity-source scopes:

- A *global* source is an `ActivitySource` created through its constructor. `EnabledGlobalTracing` rules apply to these sources.
- A *local* source is an `ActivitySource` created by the `ActivitySourceFactory` resolved from dependency injection. `EnabledLocalTracing` rules apply to sources created by that factory.

Use <xref:System.Diagnostics.ActivitySourceFactory.Create*> when you want the dependency injection container to manage the source and its tracing listeners. A factory's listeners ignore local sources created by a different factory. Rules in `EnabledTracing` apply to both source types.

These scopes restrict only the listeners that `AddTracing` registers. An independent `ActivityListener` uses its own `ShouldListenTo` and sampling callbacks, even for an activity source created by `ActivitySourceFactory`.

## Reload tracing configuration

`AddConfiguration` subscribes to the change token from the supplied configuration. When a [configuration provider](../extensions/configuration-providers.md) supports reload and reports a change, the tracing infrastructure:

- Rebuilds the rule set.
- Refreshes each registered listener's source subscriptions.
- Applies the updated rules to subsequent activities.

The app can keep using the same `ActivitySource` instances after a reload. Subsequent activities use the updated rules.

For a mutable provider that doesn't report changes automatically, update the configuration and call <xref:Microsoft.Extensions.Configuration.IConfigurationRoot.Reload*>. The complete sample uses this approach with the in-memory configuration provider.

## Handle invalid configuration

Tracing configuration parses values with <xref:System.Boolean.TryParse*>. It accepts `true` and `false` without regard to case and permits surrounding whitespace. It doesn't accept `1`, `0`, `yes`, or `on`.

The configuration loader silently ignores a leaf value that isn't a valid Boolean. It also ignores unknown or malformed sections that don't match the supported schema. Check key names carefully because these cases don't produce an exception.

A source expression can contain at most one `*` wildcard. A rule with more than one wildcard throws an <xref:System.ArgumentException> when the rule is materialised, either during app startup or after configuration reload. For programmatic rules, <xref:Microsoft.Extensions.Diagnostics.Tracing.ActivitySourceScopes.None> also causes an <xref:System.ArgumentOutOfRangeException>.

## Run the sample

The following .NET 11 console app creates one enabled operation and one disabled operation. It then updates the disabled operation's rule, calls `Reload`, and starts the operation again through the same local `ActivitySource`.

:::code language="csharp" source="./snippets/distributed-tracing-configuration/csharp/Program.cs" id="TracingConfiguration":::
:::code language="vb" source="./snippets/distributed-tracing-configuration/vb/Program.vb" id="TracingConfiguration":::

The C# and Visual Basic samples produce the same result:

```console
Before reload:
Listener started: EnabledOperation
EnabledOperation: enabled
DisabledOperation: disabled
After reload:
Listener started: DisabledOperation
DisabledOperation: enabled
```

## See also

- [Distributed tracing overview](distributed-tracing.md)
- [Configuration in .NET](../extensions/configuration.md)
- [Activity tracing configuration in What's new in .NET 11](../whats-new/dotnet-11/libraries.md#activity-tracing-configuration)
1 change: 1 addition & 0 deletions docs/core/diagnostics/distributed-tracing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ is a full-featured service provided by Microsoft. For more information, see [Col

For more information, see [Understand distributed tracing concepts](distributed-tracing-concepts.md) and the following guides:

- [Configure distributed tracing](distributed-tracing-configuration.md)
- [Collect distributed traces with custom logic](distributed-tracing-collection-walkthroughs.md#collect-traces-using-custom-logic)
- [Adding custom distributed trace instrumentation](distributed-tracing-instrumentation-walkthroughs.md)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"Tracing": {
"EnabledTracing": {
"Default": false,
"Contoso": true,
"Contoso.Orders": {
"Default": true,
"HealthCheck": false
},
"Contoso.*.Storage": true
},
"EnabledGlobalTracing": {
"System.Net.Http": true
},
"EnabledLocalTracing": {
"Contoso.Workers": true
},
"Console": {
"EnabledLocalTracing": {
"Contoso.Orders": {
"Submit": false
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// <TracingConfiguration>
using System.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.Tracing;

IConfigurationRoot configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Tracing:EnabledTracing:Default"] = "false",
["Tracing:EnabledTracing:Contoso.Orders:Default"] = "true",
["Tracing:EnabledTracing:Contoso.Orders:DisabledOperation"] = "false",
})
.Build();

ServiceCollection services = new();
services.AddTracing(tracing =>
{
tracing.AddListener("Console", listener =>
{
listener.Sample = static (ref ActivityCreationOptions<ActivityContext> _) =>
ActivitySamplingResult.AllData;
listener.SampleUsingParentId = static (ref ActivityCreationOptions<string> _) =>
ActivitySamplingResult.AllData;
listener.ActivityStarted = activity =>
Console.WriteLine($"Listener started: {activity.OperationName}");
});
tracing.AddConfiguration(configuration.GetSection("Tracing"));
});

using ServiceProvider serviceProvider = services.BuildServiceProvider();
ActivitySourceFactory factory =
serviceProvider.GetRequiredService<ActivitySourceFactory>();
using ActivitySource source = factory.Create("Contoso.Orders");

Console.WriteLine("Before reload:");
WriteResult(source, "EnabledOperation");
WriteResult(source, "DisabledOperation");

configuration["Tracing:EnabledTracing:Contoso.Orders:DisabledOperation"] = "true";
configuration.Reload();

Console.WriteLine("After reload:");
WriteResult(source, "DisabledOperation");

static void WriteResult(ActivitySource source, string operationName)
{
using Activity? activity = source.StartActivity(operationName);
string state = activity is null ? "disabled" : "enabled";
Console.WriteLine($"{operationName}: {state}");
}
// </TracingConfiguration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
' <TracingConfiguration>
Imports System.Diagnostics
Imports Microsoft.Extensions.Configuration
Imports Microsoft.Extensions.DependencyInjection
Imports Microsoft.Extensions.Diagnostics.Tracing

Module Program
Sub Main()
Dim initialData As New Dictionary(Of String, String) From {
{"Tracing:EnabledTracing:Default", "false"},
{"Tracing:EnabledTracing:Contoso.Orders:Default", "true"},
{"Tracing:EnabledTracing:Contoso.Orders:DisabledOperation", "false"}
}

Dim configuration As IConfigurationRoot =
New ConfigurationBuilder().AddInMemoryCollection(initialData).Build()

Dim services As New ServiceCollection()
services.AddTracing(
Sub(tracing)
tracing.AddListener(
"Console",
Sub(listener)
listener.Sample = AddressOf SampleActivity
listener.SampleUsingParentId = AddressOf SampleActivityUsingParentId
listener.ActivityStarted =
Sub(activity) Console.WriteLine(
$"Listener started: {activity.OperationName}")
End Sub)
tracing.AddConfiguration(configuration.GetSection("Tracing"))
End Sub)

Using serviceProvider As ServiceProvider = services.BuildServiceProvider()
Dim factory As ActivitySourceFactory =
serviceProvider.GetRequiredService(Of ActivitySourceFactory)()

Using source As ActivitySource = factory.Create("Contoso.Orders")
Console.WriteLine("Before reload:")
WriteResult(source, "EnabledOperation")
WriteResult(source, "DisabledOperation")

configuration(
"Tracing:EnabledTracing:Contoso.Orders:DisabledOperation") = "true"
configuration.Reload()

Console.WriteLine("After reload:")
WriteResult(source, "DisabledOperation")
End Using
End Using
End Sub

Private Function SampleActivity(
ByRef options As ActivityCreationOptions(Of ActivityContext)
) As ActivitySamplingResult
Return ActivitySamplingResult.AllData
End Function

Private Function SampleActivityUsingParentId(
ByRef options As ActivityCreationOptions(Of String)
) As ActivitySamplingResult
Return ActivitySamplingResult.AllData
End Function

Private Sub WriteResult(source As ActivitySource, operationName As String)
Using activity As Activity = source.StartActivity(operationName)
Dim state As String = If(activity Is Nothing, "disabled", "enabled")
Console.WriteLine($"{operationName}: {state}")
End Using
End Sub
End Module
' </TracingConfiguration>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<RootNamespace>TracingConfiguration</RootNamespace>
<OptionExplicit>On</OptionExplicit>
<OptionInfer>On</OptionInfer>
<OptionStrict>On</OptionStrict>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

</Project>
2 changes: 2 additions & 0 deletions docs/navigate/tools-diagnostics/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ items:
- name: Overview
href: ../../core/diagnostics/distributed-tracing.md
displayName: distributed tracing
- name: Configuration
href: ../../core/diagnostics/distributed-tracing-configuration.md
- name: Concepts
href: ../../core/diagnostics/distributed-tracing-concepts.md
- name: Instrumentation
Expand Down
Loading