Skip to content

Commit a69aebb

Browse files
Copilotdavidfowl
andcommitted
Use PipelineLoggingOptions to centralize log level and exception detail configuration
Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
1 parent 41ea72d commit a69aebb

File tree

4 files changed

+59
-13
lines changed

4 files changed

+59
-13
lines changed

src/Aspire.Hosting/DistributedApplicationBuilder.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -446,13 +446,11 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options)
446446
_innerBuilder.Services.AddSingleton<PipelineActivityReporter>();
447447
_innerBuilder.Services.AddSingleton<IPipelineActivityReporter, PipelineActivityReporter>(sp => sp.GetRequiredService<PipelineActivityReporter>());
448448
_innerBuilder.Services.AddSingleton(Pipeline);
449-
_innerBuilder.Services.AddSingleton<ILoggerProvider, PipelineLoggerProvider>();
450449

451-
// Read this once from configuration and use it to filter logs from the pipeline
452-
LogLevel? minLevel = null;
453-
_innerBuilder.Logging.AddFilter<PipelineLoggerProvider>((level) =>
450+
// Configure pipeline logging options
451+
_innerBuilder.Services.Configure<PipelineLoggingOptions>(o =>
454452
{
455-
minLevel ??= _innerBuilder.Configuration["Publishing:LogLevel"]?.ToLowerInvariant() switch
453+
o.MinimumLogLevel = _innerBuilder.Configuration["Publishing:LogLevel"]?.ToLowerInvariant() switch
456454
{
457455
"trace" => LogLevel.Trace,
458456
"debug" => LogLevel.Debug,
@@ -462,8 +460,14 @@ public DistributedApplicationBuilder(DistributedApplicationOptions options)
462460
"crit" or "critical" => LogLevel.Critical,
463461
_ => LogLevel.Information
464462
};
463+
});
465464

466-
return level >= minLevel;
465+
_innerBuilder.Services.AddSingleton<ILoggerProvider, PipelineLoggerProvider>();
466+
467+
// Configure logging filter using the PipelineLoggingOptions
468+
_innerBuilder.Services.AddOptions<LoggerFilterOptions>().Configure<PipelineLoggingOptions>((filterLoggingOptions, pipelineLoggingOptions) =>
469+
{
470+
filterLoggingOptions.AddFilter<PipelineLoggerProvider>((level) => level >= pipelineLoggingOptions.MinimumLogLevel);
467471
});
468472

469473
// Register IDeploymentStateManager based on execution context

src/Aspire.Hosting/Pipelines/PipelineLoggerProvider.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using Microsoft.Extensions.Logging;
55
using Microsoft.Extensions.Logging.Abstractions;
6+
using Microsoft.Extensions.Options;
67

78
namespace Aspire.Hosting.Pipelines;
89

@@ -16,6 +17,16 @@ namespace Aspire.Hosting.Pipelines;
1617
internal sealed class PipelineLoggerProvider : ILoggerProvider
1718
{
1819
private static readonly AsyncLocal<StepLoggerHolder?> s_currentLogger = new();
20+
private readonly PipelineLoggingOptions _options;
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="PipelineLoggerProvider"/> class.
24+
/// </summary>
25+
/// <param name="options">The pipeline logging options.</param>
26+
public PipelineLoggerProvider(IOptions<PipelineLoggingOptions> options)
27+
{
28+
_options = options.Value;
29+
}
1930

2031
/// <summary>
2132
/// Gets or sets the current logger for the executing pipeline step.
@@ -39,7 +50,7 @@ public static ILogger CurrentLogger
3950

4051
/// <inheritdoc/>
4152
public ILogger CreateLogger(string categoryName) =>
42-
new PipelineLogger(() => CurrentLogger);
53+
new PipelineLogger(() => CurrentLogger, _options);
4354

4455
/// <inheritdoc/>
4556
public void Dispose()
@@ -61,9 +72,9 @@ private sealed class StepLoggerHolder
6172
/// <remarks>
6273
/// This logger acts as a proxy and dynamically resolves the current logger on each operation,
6374
/// allowing the target logger to change between calls.
64-
/// When logging exceptions, stack traces are only included when the log level is Debug or Trace.
75+
/// When logging exceptions, stack traces are only included when the configured minimum log level is Debug or Trace.
6576
/// </remarks>
66-
private sealed class PipelineLogger(Func<ILogger> currentLoggerAccessor) : ILogger
77+
private sealed class PipelineLogger(Func<ILogger> currentLoggerAccessor, PipelineLoggingOptions options) : ILogger
6778
{
6879
/// <inheritdoc/>
6980
public IDisposable? BeginScope<TState>(TState state) where TState : notnull =>
@@ -78,9 +89,9 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
7889
{
7990
var logger = currentLoggerAccessor();
8091

81-
// If there's an exception and the log level is not Debug or Trace, exclude the exception from the log
92+
// If there's an exception and we should not include exception details, exclude the exception from the log
8293
// to avoid cluttering production logs with stack traces
83-
if (exception is not null && logLevel >= LogLevel.Debug)
94+
if (exception is not null && !options.IncludeExceptionDetails)
8495
{
8596
logger.Log(logLevel, eventId, state, null, formatter);
8697
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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 Aspire.Hosting.Pipelines;
7+
8+
/// <summary>
9+
/// Options for controlling logging behavior in the pipeline.
10+
/// </summary>
11+
internal sealed class PipelineLoggingOptions
12+
{
13+
/// <summary>
14+
/// Gets or sets the minimum log level for pipeline logging.
15+
/// </summary>
16+
public LogLevel MinimumLogLevel { get; set; } = LogLevel.Information;
17+
18+
/// <summary>
19+
/// Gets a value indicating whether exception details (stack traces) should be included in logs.
20+
/// </summary>
21+
/// <remarks>
22+
/// Exception details are included when the minimum log level is Debug or Trace.
23+
/// </remarks>
24+
public bool IncludeExceptionDetails => MinimumLogLevel <= LogLevel.Debug;
25+
}

tests/Aspire.Hosting.Tests/Pipelines/PipelineLoggerProviderTests.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@
88
using Microsoft.Extensions.Logging;
99
using Microsoft.Extensions.Logging.Abstractions;
1010
using Microsoft.Extensions.Logging.Testing;
11+
using Microsoft.Extensions.Options;
1112

1213
namespace Aspire.Hosting.Tests.Pipelines;
1314

1415
public class PipelineLoggerProviderTests
1516
{
17+
private static PipelineLoggerProvider CreateProvider(LogLevel minimumLogLevel = LogLevel.Information)
18+
{
19+
var options = Options.Create(new PipelineLoggingOptions { MinimumLogLevel = minimumLogLevel });
20+
return new PipelineLoggerProvider(options);
21+
}
1622
[Fact]
1723
public void CurrentLogger_WhenNotSet_ReturnsNullLogger()
1824
{
@@ -72,7 +78,7 @@ public void CurrentLogger_WhenSetToNull_ReturnsNullLogger()
7278
public void CreateLogger_ReturnsValidLogger()
7379
{
7480
// Arrange
75-
var provider = new PipelineLoggerProvider();
81+
var provider = CreateProvider();
7682

7783
// Act
7884
var logger = provider.CreateLogger("TestCategory");
@@ -87,7 +93,7 @@ public async Task CurrentLogger_IsAsyncLocal_IsolatedBetweenTasks()
8793
// Arrange
8894
var fakeLogger1 = new FakeLogger();
8995
var fakeLogger2 = new FakeLogger();
90-
var provider = new PipelineLoggerProvider();
96+
var provider = CreateProvider();
9197

9298
// Act
9399
var task1 = Task.Run(async () =>

0 commit comments

Comments
 (0)