Skip to content

Commit 1449465

Browse files
Add logging sampling documentation (#45948)
* Add logging sampling docs * Update toc * PR Comments * Fix links * Apply suggestions from code review Minor simplification of host builder variable name, to be consistent with fundamentals docs. --------- Co-authored-by: David Pine <david.pine@microsoft.com>
1 parent 88784e8 commit 1449465

File tree

17 files changed

+468
-0
lines changed

17 files changed

+468
-0
lines changed

docs/core/extensions/log-sampling.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
---
2+
title: Log sampling
3+
description: Learn how to fine-tune the volume of logs emitted by your application using log sampling.
4+
ms.date: 04/29/2025
5+
---
6+
7+
# Log sampling in .NET
8+
9+
.NET provides log sampling capabilities that allow you to control the volume of logs your application emits without losing important information. The following sampling strategies are available:
10+
11+
- Trace-based sampling: Sample logs based on the sampling decision of the current trace.
12+
- Random probabilistic sampling: Sample logs based on configured probability rules.
13+
- Custom sampling: Implement your own custom sampling strategy. For more information, see [Implement custom sampling](#implement-custom-sampling).
14+
15+
> [!NOTE]
16+
> Only one sampler can be used at a time. If you register multiple samplers, the last one is used.
17+
18+
Log sampling extends [filtering capabilities](logging.md#configure-logging-with-code) by giving you more fine-grained control over which logs are emitted by your application. Instead of simply enabling or disabling logs, you can configure sampling to emit only a fraction of them.
19+
20+
For example, while filtering typically uses probabilities like `0` (emit no logs) or `1` (emit all logs), sampling lets you choose any value in between—such as `0.1` to emit 10% of logs, or `0.25` to emit 25%.
21+
22+
## Get started
23+
24+
To get started, install the [📦 Microsoft.Extensions.Telemetry](https://www.nuget.org/packages/Microsoft.Extensions.Telemetry) NuGet package:
25+
26+
### [.NET CLI](#tab/dotnet-cli)
27+
28+
```dotnetcli
29+
dotnet add package Microsoft.Extensions.Telemetry
30+
```
31+
32+
### [PackageReference](#tab/package-reference)
33+
34+
```xml
35+
<ItemGroup>
36+
<PackageReference Include="Microsoft.Extensions.Telemetry"
37+
Version="*" />
38+
</ItemGroup>
39+
```
40+
41+
---
42+
43+
For more information, see [dotnet add package](../tools/dotnet-package-add.md) or [Manage package dependencies in .NET applications](../tools/dependencies.md).
44+
45+
## Configure trace-based sampling
46+
47+
Trace-based sampling ensures that logs are sampled consistently with the underlying <xref:System.Diagnostics.Activity>. This is useful when you want to maintain correlation between traces and logs. You can enable trace sampling (as described in the [guide](../diagnostics/distributed-tracing-concepts.md#sampling)), and then configure trace-based log sampling accordingly:
48+
49+
:::code language="csharp" source="snippets/logging/log-sampling/trace-based/Program.cs" range="20":::
50+
51+
When trace-based sampling is enabled, logs will only be emitted if the underlying <xref:System.Diagnostics.Activity> is sampled. The sampling decision comes from the current <xref:System.Diagnostics.Activity.Recorded> value.
52+
53+
## Configure random probabilistic sampling
54+
55+
Random probabilistic sampling allows you to sample logs based on configured probability rules. You can define rules specific to:
56+
57+
- Log category
58+
- Log level
59+
- Event ID
60+
61+
There are several ways to configure random probabilistic sampling with its rules:
62+
63+
### File-based configuration
64+
65+
Create a configuration section in your _appsettings.json_, for example:
66+
67+
:::code language="json" source="snippets/logging/log-sampling/file-config/appsettings.json" :::
68+
69+
The preceding configuration:
70+
71+
- Samples 10% of logs from categories starting with `System.` of all levels.
72+
- Samples 25% of logs from categories starting with `Microsoft.AspNetCore.` of the <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType>.
73+
- Samples 5% of logs with event ID 1001 of all categories and levels.
74+
- Samples 100% of all other logs.
75+
76+
> [!IMPORTANT]
77+
> The <xref:Microsoft.Extensions.Diagnostics.Sampling.RandomProbabilisticSamplerFilterRule.Probability> value represents probability with values from 0 to 1. For example, 0.25 means 25% of logs will be sampled. 0 means no logs will be sampled, and 1 means all logs will be sampled. Those cases with 0 and 1 can be used to effectively disable or enable all logs for a specific rule. Probability cannot be less than 0 or greater than 1, and if this occurs in the application, an exception is thrown.
78+
79+
To register the sampler with the configuration, consider the following code:
80+
81+
:::code language="csharp" source="snippets/logging/log-sampling/file-config/Program.cs" range="16":::
82+
83+
#### Change sampling rules in a running app
84+
85+
Random probabilistic sampling supports runtime configuration updates via the <xref:Microsoft.Extensions.Options.IOptionsMonitor%601> interface. If you're using a configuration provider that supports reloads—such as the [File Configuration Provider](configuration-providers.md#file-configuration-provider)—you can update sampling rules at runtime without restarting the application.
86+
87+
For example, you can start your application with the following _appsettings.json_, which effectively acts as a no-op:
88+
89+
:::code language="json" source="snippets/logging/log-sampling/appsettings.noop.json" :::
90+
91+
While the app is running, you can update the _appsettings.json_ with the following configuration:
92+
93+
:::code language="json" source="snippets/logging/log-sampling/appsettings.updated.json" :::
94+
95+
The new rules will be applied automatically, for instance, with the preceding configuration, 1% of logs with the <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> are sampled.
96+
97+
#### How sampling rules are applied
98+
99+
The algorithm is very similar to [log filtering](logging.md#how-filtering-rules-are-applied), yet there are some differences.
100+
101+
Log sampling rules evaluation is performed on each log record, however, there are performance optimizations in place, such as caching. The following algorithm is used for each log record for a given category:
102+
103+
- Select rules with `LogLevel` equal to or higher than the log level of the logger.
104+
- Select rules with `EventId` not defined or defined and equal to the log event ID.
105+
- Select rules with longest matching category prefix. If no match is found, select all rules that don't specify a category.
106+
- If multiple rules are selected, take the **last** one.
107+
- If no rules are selected, sampling is not applied, e.g. the log record is emitted as usual.
108+
109+
### Inline code configuration
110+
111+
:::code language="csharp" source="snippets/logging/log-sampling/code-config/Program.cs" range="16-22":::
112+
113+
The preceding configuration:
114+
115+
- Samples 5% of logs with event ID 1001 of all categories and levels.
116+
- Samples 100% of all other logs.
117+
118+
### Simple probability configuration
119+
120+
For basic scenarios, you can configure a single probability value that applies to all logs at or below a specified level:
121+
122+
:::code language="csharp" source="snippets/logging/log-sampling/Program.cs" range="14-15":::
123+
124+
The code above registers the sampler which would sample 10% of <xref:Microsoft.Extensions.Logging.LogLevel.Warning> logs and 1% of <xref:Microsoft.Extensions.Logging.LogLevel.Information> (and below) logs.
125+
If the configuration did not have the rule for <xref:Microsoft.Extensions.Logging.LogLevel.Information>, it would have sampled 10% of <xref:Microsoft.Extensions.Logging.LogLevel.Warning> logs and all levels below, including <xref:Microsoft.Extensions.Logging.LogLevel.Information>.
126+
127+
## Implement custom sampling
128+
129+
You can create a custom sampling strategy by deriving from the <xref:Microsoft.Extensions.Logging.LoggingSampler> abstract class and overriding its abstract members. This allows you to tailor the sampling behavior to your specific requirements. For example, a custom sampler could:
130+
131+
- Make sampling decisions based on the presence and value of specific key/value pairs in the log state.
132+
- Apply rate-limiting logic, such as emitting logs only if the number of logs within a predefined time interval stays below a certain threshold.
133+
134+
To implement a custom sampler, follow these steps:
135+
136+
1. Create a class that inherits from <xref:Microsoft.Extensions.Logging.LoggingSampler>.
137+
1. Override the <xref:Microsoft.Extensions.Logging.LoggingSampler.ShouldSample*?displayProperty=nameWithType> method to define your custom sampling logic.
138+
1. Register your custom sampler in the logging pipeline using the <xref:Microsoft.Extensions.Logging.SamplingLoggerBuilderExtensions.AddSampler%2A> extension method.
139+
140+
For each log record that isn't filtered out, the <xref:Microsoft.Extensions.Logging.LoggingSampler.ShouldSample*?displayProperty=nameWithType> method is called exactly once. Its return value determines whether the log record should be emitted.
141+
142+
## Performance considerations
143+
144+
Log sampling is designed to reduce storage costs, with a trade-off of slightly increased CPU usage. If your application generates a high volume of logs that are expensive to store, sampling can help reduce that volume. When configured appropriately, sampling can lower storage costs without losing information that's critical for diagnosing incidents.
145+
146+
For the built-in sampling, see the benchmarks [here](https://github.com/dotnet/extensions/blob/main/bench/Libraries/Microsoft.Extensions.Telemetry.PerformanceTests/README.md).
147+
148+
## Log level guidance on when to use sampling
149+
150+
| Log level | Recommendation |
151+
|--|--|
152+
| <xref:Microsoft.Extensions.Logging.LogLevel.Trace> | Don't apply sampling, because normally you disable these logs in production |
153+
| <xref:Microsoft.Extensions.Logging.LogLevel.Debug> | Don't apply sampling, because normally you disable these logs in production |
154+
| <xref:Microsoft.Extensions.Logging.LogLevel.Information> | Do apply sampling |
155+
| <xref:Microsoft.Extensions.Logging.LogLevel.Warning> | Consider applying sampling |
156+
| <xref:Microsoft.Extensions.Logging.LogLevel.Error> | Don't apply sampling |
157+
| <xref:Microsoft.Extensions.Logging.LogLevel.Critical> | Don't apply sampling |
158+
159+
## Best practices
160+
161+
- Begin with higher sampling rates and adjust them downwards as necessary.
162+
- Use category-based rules to target specific components.
163+
- If you're using distributed tracing, consider implementing trace-based sampling.
164+
- Monitor the effectiveness of your sampling rules collectively.
165+
- Find the right balance for your application—too low a sampling rate can reduce observability, while too high a rate can increase costs.
166+
167+
## See also
168+
169+
- [Logging in .NET](logging.md)
170+
- [High-performance logging in .NET](high-performance-logging.md)
171+
- [OpenTelemetry Tracing Sampling](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Diagnostics.Sampling;
3+
using Microsoft.Extensions.Hosting;
4+
using Microsoft.Extensions.Logging;
5+
6+
var builder = Host.CreateApplicationBuilder();
7+
builder.Logging.AddSimpleConsole(options =>
8+
{
9+
options.SingleLine = true;
10+
options.TimestampFormat = "hh:mm:ss";
11+
});
12+
13+
// Add the Random probabilistic sampler to the logging pipeline.
14+
builder.Logging.AddRandomProbabilisticSampler(0.01, LogLevel.Information);
15+
builder.Logging.AddRandomProbabilisticSampler(0.1, LogLevel.Warning);
16+
17+
using var app = builder.Build();
18+
19+
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
20+
var logger = loggerFactory.CreateLogger("SamplingDemo");
21+
22+
while (true)
23+
{
24+
Log.EnteredWhileLoop(logger);
25+
Log.NoisyLogMessage(logger);
26+
Log.LeftWhileLoop(logger);
27+
28+
await Task.Delay(100);
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Debug"
5+
}
6+
},
7+
"RandomProbabilisticSampler": {
8+
"Rules": [
9+
{
10+
"CategoryName": "Microsoft.AspNetCore.*",
11+
"Probability": 0.25,
12+
"LogLevel": "Information"
13+
},
14+
{
15+
"CategoryName": "System.*",
16+
"Probability": 0.1
17+
},
18+
{
19+
"EventId": 1001,
20+
"Probability": 0.05
21+
}
22+
]
23+
}
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Logging": {
3+
"RandomProbabilisticSampler": {
4+
"Rules": [
5+
{
6+
"Probability": 1
7+
}
8+
]
9+
}
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"Logging": {
3+
"RandomProbabilisticSampler": {
4+
"Rules": [
5+
{
6+
"Probability": 0.01,
7+
"LogLevel": "Information"
8+
}
9+
]
10+
}
11+
}
12+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace LogSamplingCodeConfig;
4+
5+
internal static partial class Log
6+
{
7+
[LoggerMessage(EventId = 1001, Level = LogLevel.Information, Message = "Noisy log message in my application.")]
8+
public static partial void NoisyMessage(this ILogger logger);
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log sampling feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
11+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.4" />
12+
<PackageReference Include="Microsoft.Extensions.Telemetry" Version="9.4.0" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using LogSamplingCodeConfig;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Diagnostics.Sampling;
4+
using Microsoft.Extensions.Hosting;
5+
using Microsoft.Extensions.Logging;
6+
7+
var builder = Host.CreateApplicationBuilder(args);
8+
9+
builder.Logging.AddSimpleConsole(options =>
10+
{
11+
options.SingleLine = true;
12+
options.TimestampFormat = "hh:mm:ss";
13+
});
14+
15+
// Add the Random probabilistic sampler to the logging pipeline.
16+
builder.Logging.AddRandomProbabilisticSampler(options =>
17+
{
18+
options.Rules.Add(
19+
new RandomProbabilisticSamplerFilterRule(
20+
probability: 0.05d,
21+
eventId : 1001));
22+
});
23+
24+
using var app = builder.Build();
25+
26+
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
27+
var logger = loggerFactory.CreateLogger("SamplingDemo");
28+
29+
for (int i = 0; i < 1_000_000; i++)
30+
{
31+
logger.NoisyMessage();
32+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace LogSamplingFileConfig;
4+
5+
internal static partial class Log
6+
{
7+
[LoggerMessage(Level = LogLevel.Debug, Message = "Entered While loop in my application.")]
8+
public static partial void EnteredWhileLoop(this ILogger logger);
9+
10+
[LoggerMessage(Level = LogLevel.Debug, Message = "Left While loop in my application.")]
11+
public static partial void LeftWhileLoop(this ILogger logger);
12+
13+
[LoggerMessage(EventId = 1001, Level = LogLevel.Information, Message = "Noisy log message in my application.")]
14+
public static partial void NoisyMessage(this ILogger logger);
15+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log sampling feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
11+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.4" />
12+
<PackageReference Include="Microsoft.Extensions.Telemetry" Version="9.4.0" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<None Update="appsettings.json">
17+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
18+
</None>
19+
</ItemGroup>
20+
21+
</Project>

0 commit comments

Comments
 (0)