Skip to content

Commit d523140

Browse files
committed
Skip null loggers when creating loggers in Logger factory.
1 parent 9e5e6aa commit d523140

2 files changed

Lines changed: 64 additions & 10 deletions

File tree

src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Diagnostics.CodeAnalysis;
99
using System.Linq;
1010
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.Logging.Abstractions;
1112
using Microsoft.Extensions.Options;
1213

1314
namespace Microsoft.Extensions.Logging
@@ -211,12 +212,19 @@ private void AddProviderRegistration(ILoggerProvider provider, bool dispose)
211212

212213
private LoggerInformation[] CreateLoggers(string categoryName)
213214
{
214-
var loggers = new LoggerInformation[_providerRegistrations.Count];
215+
var loggers = new List<LoggerInformation>(_providerRegistrations.Count);
215216
for (int i = 0; i < _providerRegistrations.Count; i++)
216217
{
217-
loggers[i] = new LoggerInformation(_providerRegistrations[i].Provider, categoryName);
218+
var loggerInformation = new LoggerInformation(_providerRegistrations[i].Provider, categoryName);
219+
220+
// We do not need to check for NullLogger<T>.Instance as no provider would reasonably return it (the <T> handling is at
221+
// outer loggers level, not inner level loggers in Logger/LoggerProvider).
222+
if (loggerInformation.Logger != NullLogger.Instance)
223+
{
224+
loggers.Add(loggerInformation);
225+
}
218226
}
219-
return loggers;
227+
return loggers.ToArray();
220228
}
221229

222230
private (MessageLogger[] MessageLoggers, ScopeLogger[]? ScopeLoggers) ApplyFilters(LoggerInformation[] loggers)

src/libraries/Microsoft.Extensions.Logging/tests/Common/LoggerFactoryTest.cs

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics;
88
using System.Collections.Generic;
99
using Microsoft.Extensions.DependencyInjection;
10+
using Microsoft.Extensions.Logging.Abstractions;
1011
using Moq;
1112
using Xunit;
1213

@@ -20,7 +21,7 @@ public void AddProvider_ThrowsAfterDisposed()
2021
var factory = new LoggerFactory();
2122
factory.Dispose();
2223

23-
Assert.Throws<ObjectDisposedException>(() => ((ILoggerFactory) factory).AddProvider(CreateProvider()));
24+
Assert.Throws<ObjectDisposedException>(() => ((ILoggerFactory)factory).AddProvider(CreateProvider()));
2425
}
2526

2627
[Fact]
@@ -214,7 +215,7 @@ public void TestActivityIds(ActivityTrackingOptions options)
214215
public void TestInvalidActivityTrackingOptions()
215216
{
216217
Assert.Throws<ArgumentException>(() =>
217-
LoggerFactory.Create(builder => { builder.Configure(o => o.ActivityTrackingOptions = (ActivityTrackingOptions) 0xFF00);})
218+
LoggerFactory.Create(builder => { builder.Configure(o => o.ActivityTrackingOptions = (ActivityTrackingOptions)0xFF00); })
218219
);
219220
}
220221

@@ -434,7 +435,7 @@ public void BaggageFormattedOutput()
434435
}
435436
activity.Stop();
436437

437-
string [] loggerOutput = new string[]
438+
string[] loggerOutput = new string[]
438439
{
439440
$"Inside Scope Info!",
440441
$"[TraceId, {activity.GetTraceId()}]",
@@ -451,7 +452,7 @@ public void BaggageFormattedOutput()
451452
public void CallsSetScopeProvider_OnSupportedProviders()
452453
{
453454
var loggerProvider = new ExternalScopeLoggerProvider();
454-
var loggerFactory = new LoggerFactory(new [] { loggerProvider });
455+
var loggerFactory = new LoggerFactory(new[] { loggerProvider });
455456

456457
var logger = loggerFactory.CreateLogger("Logger");
457458

@@ -480,7 +481,7 @@ public void CallsSetScopeProvider_OnSupportedProviders()
480481
public void BeginScope_ReturnsExternalSourceTokenDirectly()
481482
{
482483
var loggerProvider = new ExternalScopeLoggerProvider();
483-
var loggerFactory = new LoggerFactory(new [] { loggerProvider });
484+
var loggerFactory = new LoggerFactory(new[] { loggerProvider });
484485

485486
var logger = loggerFactory.CreateLogger("Logger");
486487

@@ -503,7 +504,7 @@ public void BeginScope_ReturnsCompositeToken_ForMultipleLoggers()
503504
{
504505
var loggerProvider = new ExternalScopeLoggerProvider();
505506
var loggerProvider2 = new InternalScopeLoggerProvider();
506-
var loggerFactory = new LoggerFactory(new ILoggerProvider[] { loggerProvider, loggerProvider2});
507+
var loggerFactory = new LoggerFactory(new ILoggerProvider[] { loggerProvider, loggerProvider2 });
507508

508509
var logger = loggerFactory.CreateLogger("Logger");
509510

@@ -543,12 +544,57 @@ public void CreateDisposeDisposesInnerServiceProvider()
543544
var provider = new Mock<ILoggerProvider>();
544545
provider.Setup(p => p.Dispose()).Callback(() => disposed = true);
545546

546-
var factory = LoggerFactory.Create(builder => builder.Services.AddSingleton(_=> provider.Object));
547+
var factory = LoggerFactory.Create(builder => builder.Services.AddSingleton(_ => provider.Object));
547548
factory.Dispose();
548549

549550
Assert.True(disposed);
550551
}
551552

553+
// Moq heavily utilizes RefEmit, which does not work on most aot workloads
554+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
555+
public void TestCreateLoggers_NullLoggerIsIgnoredWhenReturnedByProvider()
556+
{
557+
// We test this via checking if Scope optimisaion (ie not return scope wrapper but the
558+
// returned scope directly only one logger) is applied.
559+
var nullProvider = new Mock<ILoggerProvider>();
560+
nullProvider.Setup(p => p.CreateLogger(It.IsAny<string>())).Returns(NullLogger.Instance);
561+
562+
var validProvider = new CustomScopeLoggerProvider();
563+
564+
var factory = LoggerFactory.Create(builder =>
565+
{
566+
builder.AddProvider(nullProvider.Object);
567+
builder.AddProvider(validProvider);
568+
});
569+
var logger = factory.CreateLogger("TestLogger");
570+
571+
var scope = logger.BeginScope("TestScope");
572+
Assert.IsType<CustomScopeLoggerProvider.CustomScope>(scope);
573+
574+
logger.LogInformation("Test message");
575+
Assert.Equal(1, validProvider.LogText.Count);
576+
}
577+
578+
private class CustomScopeLoggerProvider : ILoggerProvider, ILogger
579+
{
580+
public List<string> LogText { get; set; } = new List<string>();
581+
582+
public void Dispose() { }
583+
584+
public ILogger CreateLogger(string categoryName) => this;
585+
586+
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) => LogText.Add(formatter(state, exception));
587+
588+
public bool IsEnabled(LogLevel logLevel) => true;
589+
590+
public IDisposable BeginScope<TState>(TState state) => new CustomScope();
591+
592+
internal class CustomScope : IDisposable
593+
{
594+
public void Dispose() { }
595+
}
596+
}
597+
552598
private class InternalScopeLoggerProvider : ILoggerProvider, ILogger
553599
{
554600
private IExternalScopeProvider _scopeProvider = new LoggerExternalScopeProvider();

0 commit comments

Comments
 (0)