C#中如何为未注册DI的类获取ILogger<TCategoryName>实例
问题背景
假设有一个未通过AddSingleton等方式注册到依赖注入(DI)框架的Person类:
public class Person { public Person(string name, int age); ... }
若要为该类获取ILogger<Person>实例,目前尝试了两种方案但均存在问题:
方案1:在类内直接创建日志实例
public class Person { private ILogger<Person> logger = LoggerFactory.Create(builder => builder .AddConsole() .SetMinimumLevel(LogLevel.Information) .AddFilter("Microsoft", LogLevel.Warning) ... ); public Person(string name, int age); ... }
问题:每个类都需重复配置日志,且日志级别无法通过appsettings.json控制。
方案2:从已注册DI的外部服务传入日志实例
public class SchoolService: ISchoolService { // 用于SchoolService内部日志 private ILogger<SchoolService> _logger; // 用于Person内部日志 private ILogger<Person> _personLogger; public SchoolService(ILogger<SchoolService> logger, ILogger<Person> personLogger) { _logger = logger; _personLogger = personLogger; } public void DoSomething() { _logger.LogInformation("Creating student"); Person student = new Person("Ben", 15, _personLogger); ... } }
问题:层级调用时需传递大量不同日志实例,且不想为此修改类的构造函数或方法。
请问是否存在更优雅的方式为未注册DI的类创建日志实例?
解决方案
方法1:注入ILoggerFactory动态生成日志实例
在已注册DI的服务(比如SchoolService)中注入ILoggerFactory,而非预先注入所有可能用到的ILogger<T>。当需要创建Person时,用工厂直接生成ILogger<Person>再传递——如果允许修改Person构造函数,这是最简洁的方式:
public class SchoolService: ISchoolService { private readonly ILogger<SchoolService> _logger; private readonly ILoggerFactory _loggerFactory; public SchoolService(ILogger<SchoolService> logger, ILoggerFactory loggerFactory) { _logger = logger; _loggerFactory = loggerFactory; } public void DoSomething() { _logger.LogInformation("Creating student"); var personLogger = _loggerFactory.CreateLogger<Person>(); Person student = new Person("Ben", 15, personLogger); ... } }
如果不想修改Person构造函数,可以给Person加个静态方法初始化日志:
public class Person { private static ILogger<Person> _logger; public static void SetLogger(ILogger<Person> logger) => _logger = logger; public Person(string name, int age) { _logger?.LogInformation("创建Person实例:{Name}, {Age}", name, age); } } // 在应用启动时从DI获取并设置 var personLogger = serviceProvider.GetRequiredService<ILogger<Person>>(); Person.SetLogger(personLogger);
方法2:静态日志访问器(无构造函数修改需求)
创建一个静态访问器类,在应用启动时用DI容器初始化,后续未注册DI的类直接通过它获取日志实例:
public static class LoggerAccessor { private static ILoggerFactory _loggerFactory; public static void Initialize(ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; } public static ILogger<T> GetLogger<T>() { if (_loggerFactory == null) throw new InvalidOperationException("LoggerAccessor未初始化"); return _loggerFactory.CreateLogger<T>(); } } // 在Program.cs中初始化 var builder = WebApplication.CreateBuilder(args); // ... 其他服务注册 var app = builder.Build(); LoggerAccessor.Initialize(app.Services.GetRequiredService<ILoggerFactory>()); app.Run(); // Person类中直接使用 public class Person { private readonly ILogger<Person> _logger = LoggerAccessor.GetLogger<Person>(); public Person(string name, int age) { _logger.LogInformation("创建Person实例:{Name}, {Age}", name, age); } }
这种方式复用了DI中配置好的日志规则(比如从appsettings.json读取级别),既不用改构造函数,也不用层层传递日志实例。
方法3:将Person注册到DI(推荐)
如果没有特殊限制,最符合DI设计理念的方式是把Person注册到容器,用ActivatorUtilities处理混合参数(DI注入+自定义参数):
// Program.cs中注册Person builder.Services.AddTransient<Person>(); // 修改Person构造函数注入日志 public class Person { private readonly ILogger<Person> _logger; public Person(string name, int age, ILogger<Person> logger) { _logger = logger; } } // SchoolService中创建Person实例 public class SchoolService: ISchoolService { private readonly ILogger<SchoolService> _logger; private readonly IServiceProvider _serviceProvider; public SchoolService(ILogger<SchoolService> logger, IServiceProvider serviceProvider) { _logger = logger; _serviceProvider = serviceProvider; } public void DoSomething() { _logger.LogInformation("Creating student"); // 自动注入ILogger<Person>,同时传入name和age参数 Person student = ActivatorUtilities.CreateInstance<Person>(_serviceProvider, "Ben", 15); ... } }
内容的提问来源于stack exchange,提问作者kzfid
相关产品推荐
相关产品推荐

