Skip to content

Commit 0d95e86

Browse files
authoredMay 24, 2025··
Add log buffering docs (#46232)
1 parent 6420577 commit 0d95e86

30 files changed

+940
-1
lines changed
 

‎docs/core/extensions/log-buffering.md

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
---
2+
title: Log buffering
3+
description: Learn how to delay log emission using log buffering in .NET applications.
4+
ms.date: 05/16/2025
5+
---
6+
7+
# Log buffering in .NET
8+
9+
.NET provides log buffering capabilities that allow you to delay the emission of logs until certain conditions are met. Log buffering is useful in scenarios where you want to:
10+
11+
- Collect all logs from a specific operation before deciding whether to emit them.
12+
- Prevent logs from being emitted during normal operation, but emit them when errors occur.
13+
- Optimize performance by reducing the number of logs written to storage.
14+
15+
Buffered logs are stored in temporary circular buffers in process memory, and the following conditions apply:
16+
17+
- If the buffer is full, the oldest logs are dropped and never emitted.
18+
- If you want to emit the buffered logs, you can call <xref:Microsoft.Extensions.Diagnostics.Buffering.LogBuffer.Flush> on the <xref:Microsoft.Extensions.Diagnostics.Buffering.GlobalLogBuffer> or <xref:Microsoft.Extensions.Diagnostics.Buffering.PerRequestLogBuffer> class.
19+
- If you never flush the buffers, the buffered logs will eventually be dropped as the application runs, so it effectively behaves like those logs are disabled.
20+
21+
There are two buffering strategies available:
22+
23+
- Global buffering: Buffers logs across the entire application.
24+
- Per-request buffering: Buffers logs for each individual HTTP request if available; otherwise, buffers to the global buffer.
25+
26+
> [!NOTE]
27+
> Log buffering is available in .NET 9 and later versions.
28+
29+
Log buffering works with all logging providers. If a logging provider you use does not implement the <xref:Microsoft.Extensions.Logging.Abstractions.IBufferedLogger> interface, log buffering will call log methods directly on each buffered log record when flushing the buffer.
30+
31+
Log buffering extends [filtering capabilities](logging.md#configure-logging-with-code) by allowing you to capture and store logs temporarily. Rather than making an immediate emit-or-discard decision, buffering lets you hold logs in memory and decide later whether to emit them.
32+
33+
## Get started
34+
35+
To get started, install the [📦 Microsoft.Extensions.Telemetry](https://www.nuget.org/packages/Microsoft.Extensions.Telemetry) NuGet package for [global buffering](#global-buffering).
36+
Or, install the [📦 Microsoft.AspNetCore.Diagnostics.Middleware](https://www.nuget.org/packages/Microsoft.AspNetCore.Diagnostics.Middleware) NuGet package for [per-request buffering](#per-request-buffering).
37+
38+
### [.NET CLI](#tab/dotnet-cli)
39+
40+
```dotnetcli
41+
dotnet add package Microsoft.Extensions.Telemetry
42+
dotnet add package Microsoft.AspNetCore.Diagnostics.Middleware
43+
```
44+
45+
### [PackageReference](#tab/package-reference)
46+
47+
```xml
48+
<ItemGroup>
49+
<PackageReference Include="Microsoft.Extensions.Telemetry"
50+
Version="*" />
51+
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Middleware"
52+
Version="*" />
53+
</ItemGroup>
54+
```
55+
56+
---
57+
58+
For more information about adding packages, see [dotnet add package](../tools/dotnet-package-add.md) or [Manage package dependencies in .NET applications](../tools/dependencies.md).
59+
60+
## Global buffering
61+
62+
Global buffering allows you to buffer logs across your entire application. You can configure which logs to buffer using filter rules, and then flush the buffer as needed to emit those logs.
63+
64+
### Simple configuration
65+
66+
To enable global buffering at or below a specific log level, specify that level:
67+
68+
:::code language="csharp" source="snippets/logging/log-buffering/global/basic/Program.cs" range="18-19":::
69+
70+
The preceding configuration enables buffering logs with level <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> and below.
71+
72+
### File-based configuration
73+
74+
Create a configuration section in your _appsettings.json_, for example:
75+
76+
:::code language="json" source="snippets/logging/log-buffering/global/file-based/appsettings.json" range="1-22" highlight="7-20" :::
77+
78+
The preceding configuration:
79+
80+
- Buffers logs from categories starting with `BufferingDemo` with level <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> and below.
81+
- Buffers all logs with event ID 1001.
82+
- Sets the maximum buffer size to approximately 100 MB.
83+
- Sets the maximum log record size to 50 KB.
84+
- Sets an auto-flush duration of 30 seconds after manual flushing.
85+
86+
To register the log buffering with the configuration, consider the following code:
87+
88+
:::code language="csharp" source="snippets/logging/log-buffering/global/file-based/Program.cs" range="21-22":::
89+
90+
### Inline code configuration
91+
92+
:::code language="csharp" source="snippets/logging/log-buffering/global/code-based/Program.cs" range="18-28" :::
93+
94+
The preceding configuration:
95+
96+
- Buffers logs from categories starting with `BufferingDemo` with level <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> and below.
97+
- Buffers all logs with event ID 1001.
98+
- Sets the maximum buffer size to approximately 100 MB.
99+
- Sets the maximum log record size to 50 KB.
100+
- Sets an auto-flush duration of 30 seconds after manual flushing.
101+
102+
### Flushing the buffer
103+
104+
To flush the buffered logs, inject the `GlobalLogBuffer` abstract class and call the `Flush()` method:
105+
106+
:::code language="cs" source="snippets/logging/log-buffering/global/basic/myservice.cs" range="6-22" highlight="5,12" :::
107+
108+
## Per-request buffering
109+
110+
Per-request buffering is specific to ASP.NET Core applications and allows you to buffer logs independently for each HTTP request. The
111+
buffer for each respective request is created when the request starts and disposed when the request ends, so if you don't flush the buffer, the logs will be lost when the request ends. This way, it is useful to only flush buffers when you really need to, such as when an error occurs.
112+
113+
Per-request buffering is tightly coupled with [global buffering](#global-buffering). If a log entry is supposed to be buffered to a per-request buffer, but there is no active HTTP context at the moment
114+
of buffering attempt, it will be buffered to the global buffer instead. If buffer flush is triggered, the per-request buffer will be flushed first, followed by the global buffer.
115+
116+
### Simple configuration
117+
118+
To buffer only logs at or below a specific log level:
119+
120+
:::code language="cs" source="snippets/logging/log-buffering/per-request/basic/program.cs" range="16" :::
121+
122+
### File-based configuration
123+
124+
Create a configuration section in your _appsettings.json_:
125+
126+
:::code language="json" source="snippets/logging/log-buffering/per-request/file-based/appsettings.json" range="1-18" highlight="8-16":::
127+
128+
The preceding configuration:
129+
130+
- Buffers logs from categories starting with `PerRequestLogBufferingFileBased.` with level <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> and below.
131+
- Sets an auto-flush duration of 5 seconds after manual flushing.
132+
133+
To register the log buffering with the configuration, consider the following code:
134+
135+
:::code language="cs" source="snippets/logging/log-buffering/per-request/file-based/program.cs" range="16" :::
136+
137+
### Inline code configuration
138+
139+
:::code language="cs" source="snippets/logging/log-buffering/per-request/code-based/program.cs" range="16-20" :::
140+
141+
The preceding configuration:
142+
143+
- Buffers logs from categories starting with `PerRequestLogBufferingFileBased.` with level <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> and below.
144+
- Sets an auto-flush duration of 5 seconds after manual flushing.
145+
146+
### Flushing the per-request buffer
147+
148+
To flush the buffered logs for the current request, inject the `PerRequestLogBuffer` abstract class and call its `Flush()` method:
149+
150+
:::code language="cs" source="snippets/logging/log-buffering/per-request/basic/homecontroller.cs" range="8-48" highlight="8,11,34" :::
151+
152+
> [!NOTE]
153+
> Flushing the per-request buffer also flushes the global buffer.
154+
155+
## How buffering rules are applied
156+
157+
Log buffering rules evaluation is performed on each log record. The following algorithm is used for each log record:
158+
159+
1. If a log entry matches any rule, it is buffered instead of being emitted immediately.
160+
1. If a log entry does not match any rule, it is emitted normally.
161+
1. If the buffer size limit is reached, the oldest buffered log entries are dropped (not emitted!) to make room for new ones.
162+
1. If a log entry size is greater than the maximum log record size, it will not be buffered and is emitted normally.
163+
164+
For each log record, the algorithm checks:
165+
166+
- If the log level matches (is equal to or lower than) the rule's log level.
167+
- If the category name starts with the rule's `CategoryName` prefix.
168+
- If the event ID matches the rule's `EventId`.
169+
- If the event name matches the rule's `EventName`.
170+
- If any attributes match the rule's `Attributes`.
171+
172+
### Change buffer filtering rules in a running app
173+
174+
Both [global buffering](#global-buffering) and [per-request buffering](#per-request-buffering) support run-time 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 filtering rules at run time without restarting the application.
175+
176+
For example, you can start your application with the following _appsettings.json_, which enables log buffering for logs with the <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> level and category starting with `PerRequestLogBufferingFileBased.`:
177+
178+
:::code language="json" source="snippets/logging/log-buffering/per-request/file-based/appsettings.json" range="1-19" :::
179+
180+
While the app is running, you can update the _appsettings.json_ with the following configuration:
181+
182+
:::code language="json" source="snippets/logging/log-buffering/per-request/file-based/appsettingsUpdated.json" range="1-17" highlight="9-13" :::
183+
184+
The new rules are applied automatically. For example, with the preceding configuration, all logs with the <xref:Microsoft.Extensions.Logging.LogLevel.Information?displayProperty=nameWithType> level will be buffered.
185+
186+
## Performance considerations
187+
188+
Log buffering offers a trade-off between memory usage and log storage costs. Buffering logs in memory allows you to:
189+
190+
1. Selectively emit logs based on run-time conditions.
191+
1. Drop unnecessary logs without writing them to storage.
192+
193+
However, be mindful of the memory consumption, especially in high-throughput applications. Configure appropriate buffer size limits to prevent excessive memory usage.
194+
195+
## Best practices
196+
197+
- Set appropriate buffer size limits based on your application's memory constraints.
198+
- Use per-request buffering for web applications to isolate logs by request.
199+
- Configure auto-flush duration carefully to balance memory usage and log availability.
200+
- Implement explicit flush triggers for important events (such as errors and warnings).
201+
- Monitor buffer memory usage in production to ensure it remains within acceptable limits.
202+
203+
## Limitations
204+
205+
- Log buffering is not supported in .NET 8 and earlier versions.
206+
- The order of logs is not guaranteed to be preserved. However, original timestamps are preserved.
207+
- Custom configuration per each logging provider is not supported. The same configuration is used for all providers.
208+
- Log scopes are not supported. This means that if you use the <xref:Microsoft.Extensions.Logging.ILogger.BeginScope%2A> method, the buffered log records will not be associated with the scope.
209+
- Not all information of the original log record is preserved. Log buffering internally uses <xref:Microsoft.Extensions.Logging.Abstractions.BufferedLogRecord> class when flushing, and the following of its properties are always empty:
210+
- <xref:Microsoft.Extensions.Logging.Abstractions.BufferedLogRecord.ActivitySpanId>
211+
- <xref:Microsoft.Extensions.Logging.Abstractions.BufferedLogRecord.ActivityTraceId>
212+
- <xref:Microsoft.Extensions.Logging.Abstractions.BufferedLogRecord.ManagedThreadId>
213+
- <xref:Microsoft.Extensions.Logging.Abstractions.BufferedLogRecord.MessageTemplate>
214+
215+
## See also
216+
217+
- [Log sampling](log-sampling.md)
218+
- [Logging in .NET](logging.md)
219+
- [High-performance logging in .NET](high-performance-logging.md)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log buffering feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
<TargetFrameworks>$(LatestTargetFramework)</TargetFrameworks>
8+
<RootNamespace>GlobalLogBufferingBasic</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
14+
<PackageReference Include="Microsoft.Extensions.Telemetry" Version="9.5.0" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace GlobalLogBufferingBasic;
4+
5+
internal static partial class Log
6+
{
7+
[LoggerMessage(Level = LogLevel.Error, Message = "ERROR log message in my application. {message}")]
8+
public static partial void ErrorMessage(this ILogger logger, string message);
9+
10+
[LoggerMessage(Level = LogLevel.Information, Message = "INFORMATION log message in my application.")]
11+
public static partial void InformationMessage(this ILogger logger);
12+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using Microsoft.Extensions.Diagnostics.Buffering;
3+
4+
namespace GlobalLogBufferingBasic;
5+
6+
public class MyService
7+
{
8+
private readonly GlobalLogBuffer _buffer;
9+
10+
public MyService(GlobalLogBuffer buffer)
11+
{
12+
_buffer = buffer;
13+
}
14+
15+
public void HandleException(Exception ex)
16+
{
17+
_buffer.Flush();
18+
19+
// After flushing, log buffering will be temporarily suspended (= all logs will be emitted immediately)
20+
// for the duration specified by AutoFlushDuration.
21+
}
22+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Diagnostics.Buffering;
5+
using Microsoft.Extensions.Hosting;
6+
using Microsoft.Extensions.Logging;
7+
using Log = GlobalLogBufferingBasic.Log;
8+
9+
var hostBuilder = Host.CreateApplicationBuilder();
10+
11+
hostBuilder.Logging.AddSimpleConsole(options =>
12+
{
13+
options.SingleLine = true;
14+
options.TimestampFormat = "hh:mm:ss";
15+
options.UseUtcTimestamp = true;
16+
});
17+
18+
// Add the Global buffer to the logging pipeline.
19+
hostBuilder.Logging.AddGlobalBuffer(LogLevel.Information);
20+
21+
using var app = hostBuilder.Build();
22+
23+
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
24+
var logger = loggerFactory.CreateLogger("BufferingDemo");
25+
var buffer = app.Services.GetRequiredService<GlobalLogBuffer>();
26+
27+
for (int i = 1; i < 21; i++)
28+
{
29+
try
30+
{
31+
Log.InformationMessage(logger);
32+
33+
if(i % 10 == 0)
34+
{
35+
throw new Exception("Simulated exception");
36+
}
37+
}
38+
catch (Exception ex)
39+
{
40+
Log.ErrorMessage(logger, ex.Message);
41+
buffer.Flush();
42+
}
43+
44+
await Task.Delay(1000).ConfigureAwait(false);
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log buffering feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
<TargetFrameworks>$(LatestTargetFramework)</TargetFrameworks>
8+
<RootNamespace>GlobalLogBufferingCodeBased</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
14+
<PackageReference Include="Microsoft.Extensions.Telemetry" Version="9.5.0" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace GlobalLogBufferingCodeBased;
4+
5+
internal static partial class Log
6+
{
7+
[LoggerMessage(Level = LogLevel.Error, Message = "ERROR log message in my application. {message}")]
8+
public static partial void ErrorMessage(this ILogger logger, string message);
9+
10+
[LoggerMessage(Level = LogLevel.Information, Message = "INFORMATION log message in my application.")]
11+
public static partial void InformationMessage(this ILogger logger);
12+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Diagnostics.Buffering;
5+
using Microsoft.Extensions.Hosting;
6+
using Microsoft.Extensions.Logging;
7+
using Log = GlobalLogBufferingCodeBased.Log;
8+
9+
var hostBuilder = Host.CreateApplicationBuilder();
10+
11+
hostBuilder.Logging.AddSimpleConsole(options =>
12+
{
13+
options.SingleLine = true;
14+
options.TimestampFormat = "hh:mm:ss";
15+
options.UseUtcTimestamp = true;
16+
});
17+
18+
// Add the Global buffer to the logging pipeline.
19+
hostBuilder.Logging.AddGlobalBuffer(options =>
20+
{
21+
options.MaxBufferSizeInBytes = 104857600; // 100 MB
22+
options.MaxLogRecordSizeInBytes = 51200; // 50 KB
23+
options.AutoFlushDuration = TimeSpan.FromSeconds(30);
24+
options.Rules.Add(new LogBufferingFilterRule(
25+
categoryName: "BufferingDemo",
26+
logLevel: LogLevel.Information));
27+
options.Rules.Add(new LogBufferingFilterRule(eventId: 1001));
28+
});
29+
30+
using var app = hostBuilder.Build();
31+
32+
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
33+
var logger = loggerFactory.CreateLogger("BufferingDemo");
34+
var buffer = app.Services.GetRequiredService<GlobalLogBuffer>();
35+
36+
for (int i = 1; i < 21; i++)
37+
{
38+
try
39+
{
40+
Log.InformationMessage(logger);
41+
42+
if(i % 10 == 0)
43+
{
44+
throw new Exception("Simulated exception");
45+
}
46+
}
47+
catch (Exception ex)
48+
{
49+
Log.ErrorMessage(logger, ex.Message);
50+
buffer.Flush();
51+
}
52+
53+
await Task.Delay(1000).ConfigureAwait(false);
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log buffering feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
<TargetFrameworks>$(LatestTargetFramework)</TargetFrameworks>
8+
<RootNamespace>GlobalLogBufferingFileBased</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
14+
<PackageReference Include="Microsoft.Extensions.Telemetry" Version="9.5.0" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<None Update="appsettings.json">
19+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
20+
</None>
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace GlobalLogBufferingFileBased;
7+
8+
internal static partial class Log
9+
{
10+
[LoggerMessage(Level = LogLevel.Error, Message = "ERROR log message in my application. {message}")]
11+
public static partial void ErrorMessage(this ILogger logger, string message);
12+
13+
[LoggerMessage(Level = LogLevel.Information, Message = "INFORMATION log message in my application.")]
14+
public static partial void InformationMessage(this ILogger logger);
15+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Microsoft.Extensions.Diagnostics.Buffering;
8+
using Microsoft.Extensions.Hosting;
9+
using Microsoft.Extensions.Logging;
10+
using Log = GlobalLogBufferingFileBased.Log;
11+
12+
var hostBuilder = Host.CreateApplicationBuilder();
13+
14+
hostBuilder.Logging.AddSimpleConsole(options =>
15+
{
16+
options.SingleLine = true;
17+
options.TimestampFormat = "hh:mm:ss";
18+
options.UseUtcTimestamp = true;
19+
});
20+
21+
// Add the Global buffer to the logging pipeline.
22+
hostBuilder.Logging.AddGlobalBuffer(hostBuilder.Configuration.GetSection("Logging"));
23+
24+
using var app = hostBuilder.Build();
25+
26+
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
27+
var logger = loggerFactory.CreateLogger("BufferingDemo");
28+
var buffer = app.Services.GetRequiredService<GlobalLogBuffer>();
29+
30+
for (int i = 1; i < 21; i++)
31+
{
32+
try
33+
{
34+
Log.InformationMessage(logger);
35+
36+
if(i % 10 == 0)
37+
{
38+
throw new Exception("Simulated exception");
39+
}
40+
}
41+
catch (Exception ex)
42+
{
43+
Log.ErrorMessage(logger, ex.Message);
44+
buffer.Flush();
45+
}
46+
47+
await Task.Delay(1000).ConfigureAwait(false);
48+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information"
5+
},
6+
7+
"GlobalLogBuffering": {
8+
"MaxBufferSizeInBytes": 104857600,
9+
"MaxLogRecordSizeInBytes": 51200,
10+
"AutoFlushDuration": "00:00:30",
11+
"Rules": [
12+
{
13+
"CategoryName": "BufferingDemo",
14+
"LogLevel": "Information"
15+
},
16+
{
17+
"EventId": 1001
18+
}
19+
]
20+
}
21+
}
22+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Extensions.Diagnostics.Buffering;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace PerRequestLogBufferingBasic;
7+
8+
[ApiController]
9+
[Route("[controller]")]
10+
public class HomeController : ControllerBase
11+
{
12+
private readonly ILogger<HomeController> _logger;
13+
private readonly PerRequestLogBuffer _buffer;
14+
15+
public HomeController(ILogger<HomeController> logger, PerRequestLogBuffer buffer)
16+
{
17+
_logger = logger;
18+
_buffer = buffer;
19+
}
20+
21+
[HttpGet("index/{id}")]
22+
public IActionResult Index(int id)
23+
{
24+
try
25+
{
26+
_logger.RequestStarted(id);
27+
28+
// Simulate exception every 10th request
29+
if (id % 10 == 0)
30+
{
31+
throw new Exception("Simulated exception in controller");
32+
}
33+
34+
_logger.RequestEnded(id);
35+
36+
return Ok();
37+
}
38+
catch
39+
{
40+
_logger.ErrorMessage(id);
41+
_buffer.Flush();
42+
43+
_logger.ExceptionHandlingFinished(id);
44+
45+
return StatusCode(500, "An error occurred.");
46+
}
47+
}
48+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace PerRequestLogBufferingBasic;
4+
5+
internal static partial class Log
6+
{
7+
[LoggerMessage(Level = LogLevel.Error, Message = "Request {id} failed")]
8+
public static partial void ErrorMessage(this ILogger logger, int id);
9+
10+
[LoggerMessage(Level = LogLevel.Information, Message = "Request {id} started.")]
11+
public static partial void RequestStarted(this ILogger logger, int id);
12+
13+
[LoggerMessage(Level = LogLevel.Information, Message = "Request {id} ended.")]
14+
public static partial void RequestEnded(this ILogger logger, int id);
15+
16+
[LoggerMessage(Level = LogLevel.Information, Message = "Exception handling finished for request {id}.")]
17+
public static partial void ExceptionHandlingFinished(this ILogger logger, int id);
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log buffering feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
<TargetFrameworks>$(LatestTargetFramework)</TargetFrameworks>
8+
<RootNamespace>PerRequestLogBufferingBasic</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
14+
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Middleware" Version="9.5.0" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<None Update="appsettings.json">
19+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
20+
</None>
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Logging;
7+
8+
var builder = WebApplication.CreateBuilder(args);
9+
builder.Services.AddControllers();
10+
builder.Logging.AddSimpleConsole(options =>
11+
{
12+
options.SingleLine = true;
13+
options.TimestampFormat = "hh:mm:ss:fff";
14+
options.UseUtcTimestamp = true;
15+
});
16+
builder.Logging.AddPerIncomingRequestBuffer(LogLevel.Information);
17+
18+
var app = builder.Build();
19+
app.MapControllers();
20+
var serverTask = app.RunAsync();
21+
22+
using var httpClient = new HttpClient();
23+
var baseUrl = "http://localhost:5000";
24+
httpClient.BaseAddress = new Uri(baseUrl);
25+
26+
var logger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("Client");
27+
logger.LogInformation("Starting to send requests to the controller...");
28+
29+
for (var i = 1; i < 21; i++)
30+
{
31+
_ = await httpClient.GetAsync($"home/index/{i}").ConfigureAwait(false);
32+
33+
await Task.Delay(1000).ConfigureAwait(false);
34+
}
35+
36+
logger.LogInformation("All requests completed");
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.*": "None"
6+
}
7+
}
8+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Extensions.Diagnostics.Buffering;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace PerRequestLogBufferingCodeBased;
7+
8+
[ApiController]
9+
[Route("[controller]")]
10+
public class HomeController : ControllerBase
11+
{
12+
private readonly ILogger<HomeController> _logger;
13+
private readonly PerRequestLogBuffer _buffer;
14+
15+
public HomeController(ILogger<HomeController> logger, PerRequestLogBuffer buffer)
16+
{
17+
_logger = logger;
18+
_buffer = buffer;
19+
}
20+
21+
[HttpGet("index/{id}")]
22+
public IActionResult Index(int id)
23+
{
24+
try
25+
{
26+
_logger.RequestStarted(id);
27+
28+
// Simulate exception every 10th request
29+
if (id % 10 == 0)
30+
{
31+
throw new Exception("Simulated exception in controller");
32+
}
33+
34+
_logger.RequestEnded(id);
35+
36+
return Ok();
37+
}
38+
catch
39+
{
40+
_logger.ErrorMessage(id);
41+
_buffer.Flush();
42+
43+
_logger.ExceptionHandlingFinished(id);
44+
45+
return StatusCode(500, "An error occurred.");
46+
}
47+
}
48+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace PerRequestLogBufferingCodeBased;
4+
5+
internal static partial class Log
6+
{
7+
[LoggerMessage(Level = LogLevel.Error, Message = "Request {id} failed")]
8+
public static partial void ErrorMessage(this ILogger logger, int id);
9+
10+
[LoggerMessage(Level = LogLevel.Information, Message = "Request {id} started.")]
11+
public static partial void RequestStarted(this ILogger logger, int id);
12+
13+
[LoggerMessage(Level = LogLevel.Information, Message = "Request {id} ended.")]
14+
public static partial void RequestEnded(this ILogger logger, int id);
15+
16+
[LoggerMessage(Level = LogLevel.Information, Message = "Exception handling finished for request {id}.")]
17+
public static partial void ExceptionHandlingFinished(this ILogger logger, int id);
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log buffering feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
<TargetFrameworks>$(LatestTargetFramework)</TargetFrameworks>
8+
<RootNamespace>PerRequestLogBufferingCodeBased</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
14+
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Middleware" Version="9.5.0" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<None Update="appsettings.json">
19+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
20+
</None>
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Logging;
7+
8+
var builder = WebApplication.CreateBuilder(args);
9+
builder.Services.AddControllers();
10+
builder.Logging.AddSimpleConsole(options =>
11+
{
12+
options.SingleLine = true;
13+
options.TimestampFormat = "hh:mm:ss:fff";
14+
options.UseUtcTimestamp = true;
15+
});
16+
builder.Logging.AddPerIncomingRequestBuffer(options =>
17+
{
18+
options.AutoFlushDuration = TimeSpan.FromSeconds(5);
19+
options.Rules.Add(new Microsoft.Extensions.Diagnostics.Buffering.LogBufferingFilterRule("PerRequestLogBufferingCodeBased.*", LogLevel.Information));
20+
});
21+
22+
var app = builder.Build();
23+
app.MapControllers();
24+
var serverTask = app.RunAsync();
25+
26+
using var httpClient = new HttpClient();
27+
var baseUrl = "http://localhost:5000";
28+
httpClient.BaseAddress = new Uri(baseUrl);
29+
30+
var logger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("Client");
31+
logger.LogInformation("Starting to send requests to the controller...");
32+
33+
for (var i = 1; i < 21; i++)
34+
{
35+
_ = await httpClient.GetAsync($"home/index/{i}").ConfigureAwait(false);
36+
37+
await Task.Delay(1000).ConfigureAwait(false);
38+
}
39+
40+
logger.LogInformation("All requests completed");
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.*": "None"
6+
}
7+
}
8+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Extensions.Diagnostics.Buffering;
4+
using Microsoft.Extensions.Logging;
5+
6+
namespace PerRequestLogBufferingFileBased;
7+
8+
[ApiController]
9+
[Route("[controller]")]
10+
public class HomeController : ControllerBase
11+
{
12+
private readonly ILogger<HomeController> _logger;
13+
private readonly PerRequestLogBuffer _buffer;
14+
15+
public HomeController(ILogger<HomeController> logger, PerRequestLogBuffer buffer)
16+
{
17+
_logger = logger;
18+
_buffer = buffer;
19+
}
20+
21+
[HttpGet("index/{id}")]
22+
public IActionResult Index(int id)
23+
{
24+
try
25+
{
26+
_logger.RequestStarted(id);
27+
28+
// Simulate exception every 10th request
29+
if (id % 10 == 0)
30+
{
31+
throw new Exception("Simulated exception in controller");
32+
}
33+
34+
_logger.RequestEnded(id);
35+
36+
return Ok();
37+
}
38+
catch
39+
{
40+
_logger.ErrorMessage(id);
41+
_buffer.Flush();
42+
43+
_logger.ExceptionHandlingFinished(id);
44+
45+
return StatusCode(500, "An error occurred.");
46+
}
47+
}
48+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace PerRequestLogBufferingFileBased;
4+
5+
internal static partial class Log
6+
{
7+
[LoggerMessage(Level = LogLevel.Error, Message = "Request {id} failed")]
8+
public static partial void ErrorMessage(this ILogger logger, int id);
9+
10+
[LoggerMessage(Level = LogLevel.Information, Message = "Request {id} started.")]
11+
public static partial void RequestStarted(this ILogger logger, int id);
12+
13+
[LoggerMessage(Level = LogLevel.Information, Message = "Request {id} ended.")]
14+
public static partial void RequestEnded(this ILogger logger, int id);
15+
16+
[LoggerMessage(Level = LogLevel.Information, Message = "Exception handling finished for request {id}.")]
17+
public static partial void ExceptionHandlingFinished(this ILogger logger, int id);
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Demonstrates how to use log buffering feature.</Description>
5+
<OutputType>Exe</OutputType>
6+
<NoWarn>$(NoWarn);EXTEXP0003</NoWarn>
7+
<TargetFrameworks>$(LatestTargetFramework)</TargetFrameworks>
8+
<RootNamespace>PerRequestLogBufferingFileBased</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
13+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.5" />
14+
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.Middleware" Version="9.5.0" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<None Update="appsettings.json">
19+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
20+
</None>
21+
</ItemGroup>
22+
23+
</Project>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Logging;
7+
8+
var builder = WebApplication.CreateBuilder(args);
9+
builder.Services.AddControllers();
10+
builder.Logging.AddSimpleConsole(options =>
11+
{
12+
options.SingleLine = true;
13+
options.TimestampFormat = "hh:mm:ss:fff";
14+
options.UseUtcTimestamp = true;
15+
});
16+
builder.Logging.AddPerIncomingRequestBuffer(builder.Configuration.GetSection("Logging"));
17+
18+
var app = builder.Build();
19+
app.MapControllers();
20+
var serverTask = app.RunAsync();
21+
22+
using var httpClient = new HttpClient();
23+
var baseUrl = "http://localhost:5000";
24+
httpClient.BaseAddress = new Uri(baseUrl);
25+
26+
var logger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("Client");
27+
logger.LogInformation("Starting to send requests to the controller...");
28+
29+
for (var i = 1; i < 21; i++)
30+
{
31+
_ = await httpClient.GetAsync($"home/index/{i}").ConfigureAwait(false);
32+
33+
await Task.Delay(1000).ConfigureAwait(false);
34+
}
35+
36+
logger.LogInformation("All requests completed");
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.*": "None"
6+
},
7+
8+
"PerIncomingRequestLogBuffering": {
9+
"AutoFlushDuration": "00:00:05",
10+
"Rules": [
11+
{
12+
"CategoryName": "PerRequestLogBufferingFileBased.*",
13+
"LogLevel": "Information"
14+
}
15+
]
16+
}
17+
}
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.*": "None"
6+
},
7+
8+
"PerIncomingRequestLogBuffering": {
9+
"Rules": [
10+
{
11+
"LogLevel": "Information"
12+
}
13+
]
14+
}
15+
}
16+
}

‎docs/fundamentals/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,8 @@ items:
10981098
displayName: high-performance logging,high-performance log,high-performance logging provider,high-performance log provider
10991099
- name: Log Sampling
11001100
href: ../core/extensions/log-sampling.md
1101+
- name: Log Buffering
1102+
href: ../core/extensions/log-buffering.md
11011103
- name: Console log formatting
11021104
href: ../core/extensions/console-log-formatter.md
11031105
displayName: console log formatting,console log formatter,console log formatting provider,console log formatter provider

‎docs/navigate/tools-diagnostics/toc.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,10 @@ items:
363363
href: ../../core/diagnostics/logging-tracing.md
364364
- name: ILogger Logging
365365
href: ../../core/extensions/logging.md
366-
- name: Log Sampling
366+
- name: Log sampling
367367
href: ../../core/extensions/log-sampling.md
368+
- name: Log buffering
369+
href: ../../core/extensions/log-buffering.md
368370
- name: Observability with OpenTelemetry
369371
items:
370372
- name: Overview

0 commit comments

Comments
 (0)
Please sign in to comment.