Browse Source

Refactor logging mechanism and enhance orphan module warning tests

pull/25223/head
maliming 6 months ago
parent
commit
36691ec106
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 15
      framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs
  2. 11
      framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs
  3. 2
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs
  4. 1
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLogger.cs
  5. 22
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLoggerFactory.cs
  6. 8
      framework/src/Volo.Abp.Core/Volo/Abp/Logging/IInitLoggerFactory.cs
  7. 24
      framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/WarnForOrphanedAbpModules_Tests.cs

15
framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacRegistration.cs

@ -396,21 +396,30 @@ public static class AutofacRegistration
return; return;
} }
var logger = services.GetInitLogger<AbpAutofacServiceProviderFactory>(); var logger = services.GetInitLogger<AbpAutofacModule>();
var loadedModuleTypes = new HashSet<Type>( var loadedModuleTypes = new HashSet<Type>(
moduleContainer.Modules.Select(m => m.Type)); moduleContainer.Modules.Select(m => m.Type));
// Only assemblies that directly reference Volo.Abp.Core can contain AbpModule subclasses.
// This skips framework/third-party assemblies (Microsoft.Extensions.*, etc.) cheaply.
var abpCoreAssemblyName = typeof(AbpModule).Assembly.GetName().Name;
foreach (var assembly in nonModuleAssemblies) foreach (var assembly in nonModuleAssemblies)
{ {
if (!assembly.GetReferencedAssemblies().Any(r => r.Name == abpCoreAssemblyName))
{
continue;
}
Type[] types; Type[] types;
try try
{ {
types = assembly.GetTypes(); types = assembly.GetTypes();
} }
catch (ReflectionTypeLoadException ex) catch (Exception)
{ {
types = ex.Types.Where(t => t != null).ToArray()!; continue;
} }
foreach (var type in types) foreach (var type in types)

11
framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs

@ -128,20 +128,21 @@ public abstract class AbpApplicationBase : IAbpApplication
protected virtual void WriteInitLogs(IServiceProvider serviceProvider) protected virtual void WriteInitLogs(IServiceProvider serviceProvider)
{ {
var logger = serviceProvider.GetService<ILogger<AbpApplicationBase>>(); var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
if (logger == null) if (loggerFactory == null)
{ {
return; return;
} }
var initLogger = serviceProvider.GetRequiredService<IInitLoggerFactory>().Create<AbpApplicationBase>(); var initLoggerFactory = serviceProvider.GetRequiredService<IInitLoggerFactory>();
foreach (var entry in initLogger.Entries) foreach (var entry in initLoggerFactory.GetAllEntries())
{ {
var logger = loggerFactory.CreateLogger(entry.CategoryName);
logger.Log(entry.LogLevel, entry.EventId, entry.State, entry.Exception, entry.Formatter); logger.Log(entry.LogLevel, entry.EventId, entry.State, entry.Exception, entry.Formatter);
} }
initLogger.Entries.Clear(); initLoggerFactory.ClearAllEntries();
} }
protected virtual IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options) protected virtual IReadOnlyList<IAbpModuleDescriptor> LoadModules(IServiceCollection services, AbpApplicationCreationOptions options)

2
framework/src/Volo.Abp.Core/Volo/Abp/Logging/AbpInitLogEntry.cs

@ -5,6 +5,8 @@ namespace Volo.Abp.Logging;
public class AbpInitLogEntry public class AbpInitLogEntry
{ {
public string CategoryName { get; set; } = default!;
public LogLevel LogLevel { get; set; } public LogLevel LogLevel { get; set; }
public EventId EventId { get; set; } public EventId EventId { get; set; }

1
framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLogger.cs

@ -17,6 +17,7 @@ public class DefaultInitLogger<T> : IInitLogger<T>
{ {
Entries.Add(new AbpInitLogEntry Entries.Add(new AbpInitLogEntry
{ {
CategoryName = typeof(T).FullName!,
LogLevel = logLevel, LogLevel = logLevel,
EventId = eventId, EventId = eventId,
State = state!, State = state!,

22
framework/src/Volo.Abp.Core/Volo/Abp/Logging/DefaultInitLoggerFactory.cs

@ -1,14 +1,34 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Logging; namespace Volo.Abp.Logging;
public class DefaultInitLoggerFactory : IInitLoggerFactory public class DefaultInitLoggerFactory : IInitLoggerFactory
{ {
private readonly Dictionary<Type, object> _cache = []; private readonly Dictionary<Type, object> _cache = [];
private readonly List<List<AbpInitLogEntry>> _entryLists = [];
public virtual IInitLogger<T> Create<T>() public virtual IInitLogger<T> Create<T>()
{ {
return (IInitLogger<T>)_cache.GetOrAdd(typeof(T), () => new DefaultInitLogger<T>()); return (IInitLogger<T>)_cache.GetOrAdd(typeof(T), () =>
{
var logger = new DefaultInitLogger<T>();
_entryLists.Add(logger.Entries);
return logger;
});
}
public virtual List<AbpInitLogEntry> GetAllEntries()
{
return _entryLists.SelectMany(l => l).ToList();
}
public virtual void ClearAllEntries()
{
foreach (var list in _entryLists)
{
list.Clear();
}
} }
} }

8
framework/src/Volo.Abp.Core/Volo/Abp/Logging/IInitLoggerFactory.cs

@ -1,6 +1,12 @@
namespace Volo.Abp.Logging; using System.Collections.Generic;
namespace Volo.Abp.Logging;
public interface IInitLoggerFactory public interface IInitLoggerFactory
{ {
IInitLogger<T> Create<T>(); IInitLogger<T> Create<T>();
List<AbpInitLogEntry> GetAllEntries();
void ClearAllEntries();
} }

24
framework/test/Volo.Abp.Autofac.Tests/Volo/Abp/Autofac/WarnForOrphanedAbpModules_Tests.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -13,19 +14,27 @@ namespace Volo.Abp.Autofac;
public class WarnForOrphanedAbpModules_Tests : AbpIntegratedTest<WarnForOrphanedAbpModules_Tests.TestModule> public class WarnForOrphanedAbpModules_Tests : AbpIntegratedTest<WarnForOrphanedAbpModules_Tests.TestModule>
{ {
private static readonly Type OrphanModuleType = typeof(AbpTestModule); private static readonly Type OrphanModuleType = typeof(AbpTestModule);
private List<AbpInitLogEntry> _initLogEntries = [];
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{ {
options.UseAutofac(); options.UseAutofac();
} }
protected override IServiceProvider CreateServiceProvider(IServiceCollection services)
{
var serviceProvider = base.CreateServiceProvider(services);
// Capture init log entries after Autofac Populate/Register but before WriteInitLogs clears them.
_initLogEntries = serviceProvider.GetRequiredService<IInitLoggerFactory>().GetAllEntries();
return serviceProvider;
}
[Fact] [Fact]
public void Should_Warn_For_Orphaned_Abp_Modules() public void Should_Warn_For_Orphaned_Abp_Modules()
{ {
var initLoggerFactory = GetRequiredService<IInitLoggerFactory>(); _initLogEntries
var logger = initLoggerFactory.Create<AbpAutofacServiceProviderFactory>();
logger.Entries
.Where(e => e.LogLevel == LogLevel.Warning) .Where(e => e.LogLevel == LogLevel.Warning)
.ShouldContain(e => e.Message.Contains(OrphanModuleType.FullName!), .ShouldContain(e => e.Message.Contains(OrphanModuleType.FullName!),
$"Expected a warning for orphaned module '{OrphanModuleType.FullName}'."); $"Expected a warning for orphaned module '{OrphanModuleType.FullName}'.");
@ -34,11 +43,7 @@ public class WarnForOrphanedAbpModules_Tests : AbpIntegratedTest<WarnForOrphaned
[Fact] [Fact]
public void Should_Not_Warn_For_Loaded_Modules() public void Should_Not_Warn_For_Loaded_Modules()
{ {
var initLoggerFactory = GetRequiredService<IInitLoggerFactory>(); _initLogEntries
var logger = initLoggerFactory.Create<AbpAutofacServiceProviderFactory>();
// AbpAutofacModule IS in the DependsOn chain, so it should NOT trigger a warning.
logger.Entries
.Where(e => e.LogLevel == LogLevel.Warning) .Where(e => e.LogLevel == LogLevel.Warning)
.ShouldNotContain(e => e.Message.Contains(typeof(AbpAutofacModule).FullName!), .ShouldNotContain(e => e.Message.Contains(typeof(AbpAutofacModule).FullName!),
"Modules in the [DependsOn] chain should not be reported as orphaned."); "Modules in the [DependsOn] chain should not be reported as orphaned.");
@ -51,7 +56,6 @@ public class WarnForOrphanedAbpModules_Tests : AbpIntegratedTest<WarnForOrphaned
{ {
// Simulate ASP.NET Core's AddControllersAsServices() registering a type // Simulate ASP.NET Core's AddControllersAsServices() registering a type
// from an assembly whose ABP module is NOT in the [DependsOn] chain. // from an assembly whose ABP module is NOT in the [DependsOn] chain.
// AbpTestModule lives in the Volo.Abp.Core.Tests assembly which is not depended on.
context.Services.AddTransient<AbpTestModule>(); context.Services.AddTransient<AbpTestModule>();
} }
} }

Loading…
Cancel
Save