ASP.NET Core 5中注入不同配置EventLog日志记录器的方法
首先需要提前引入官方NuGet包Microsoft.Extensions.Logging.EventLog,所有涉及自定义编码的部分总代码量不超过50行,支持配置化扩展。
问题1:按注入的ILogger泛型类型自动设置EventLog的Source字段
默认的EventLog日志提供程序会使用统一的Source名称,要实现ILogger<X>对应Source为"X"、ILogger<Y>对应Source为"Y"的效果,只需要实现一个极轻量的自定义日志提供程序,核心逻辑是利用ILogger<T>自带的类别名(即泛型类型T的完全限定名)自动匹配Source:
public class PerTypeEventLogLoggerProvider : ILoggerProvider { private readonly ConcurrentDictionary<string, EventLogLogger> _loggerCache = new(); private readonly EventLogSettings _globalSettings; public PerTypeEventLogLoggerProvider(IOptions<EventLogSettings> globalSettings) { _globalSettings = globalSettings.Value; } public ILogger CreateLogger(string categoryName) { return _loggerCache.GetOrAdd(categoryName, cat => { // 取类型名(去掉命名空间)作为默认Source var typeShortName = cat.Split('.').Last(); var perTypeSettings = new EventLogSettings { LogName = _globalSettings.LogName, MachineName = _globalSettings.MachineName, Filter = _globalSettings.Filter, SourceName = typeShortName }; return new EventLogLogger(typeShortName, Options.Create(perTypeSettings)); }); } public void Dispose() => _loggerCache.Clear(); }
注册时替换默认的EventLog提供程序即可:
// Program.cs 或 Startup.cs 的ConfigureLogging方法中 logging.ClearProviders(); // 可选,清理不需要的默认提供程序 logging.AddProvider(new PerTypeEventLogLoggerProvider( Options.Create(new EventLogSettings { LogName = "Application" })));
- 注意:自定义Source首次创建需要管理员权限运行一次应用,Windows会自动完成Source注册,后续普通权限运行即可正常写入。
问题2:实现不同类型Logger写入完全独立的Windows EventLog
只需要修改上面自定义提供程序中perTypeSettings的LogName属性,将其和SourceName保持一致(或按需求自定义)即可:
var perTypeSettings = new EventLogSettings { LogName = typeShortName, // 用类型名作为独立日志名 SourceName = typeShortName, MachineName = _globalSettings.MachineName, Filter = _globalSettings.Filter };
配置完成后,ILogger<X>写入的日志会全部存到Windows事件查看器中名为"X"的独立日志节点下,ILogger<Y>写入"Y"节点,完全不需要靠Source字段做区分。自定义日志首次创建同样需要管理员权限执行一次。
问题3:基于appsettings.json的可配置化实现
完全可以把所有映射规则、日志级别等配置放到appsettings.json中,仅保留上述轻量提供程序作为通用适配层,后续新增、修改规则不需要重新编译代码。
首先在appsettings.json的Logging节点添加配置:
{ "Logging": { "PerTypeEventLog": { "DefaultLogName": "Application", "DefaultSource": "MyAppDefault", "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" }, "TypeMappings": { "X": { "SourceName": "X_BizService", "LogName": "X_ServiceLog" }, "Y": { "SourceName": "Y_HomeController", "LogName": "Y_ControllerLog" } } } } }
然后创建对应的强类型配置类,修改自定义提供程序的逻辑:优先从TypeMappings节点读取对应类型的配置,没有匹配配置的类型就使用默认值即可。
如果不想编写自定义提供程序代码,也可以直接使用Serilog等第三方日志框架,其EventLog扩展原生支持按日志上下文(SourceContext属性,即ILogger的泛型类型)动态配置输出目标和字段,配置灵活度更高,但上述原生实现已经足够满足需求,不需要额外引入第三方依赖。
内容的提问来源于stack exchange,提问作者Mulciber Coder

