.NET 5中如何在ILogger记录日志后触发自定义方法?
捕获ILogger日志并触发聊天通知的解决方案
我来帮你解决这个问题——在.NET Worker程序里要捕获所有ILogger输出的日志(包括你自己代码和NuGet包的),并触发聊天通知,直接订阅ILogger本身是行不通的,因为ILogger接口并没有内置事件机制。不过我们可以利用.NET日志系统的提供者模式来实现这个需求,下面是具体的步骤和代码示例:
核心思路
.NET的日志系统基于「提供者-记录器」模式:每一个ILogger实例都是由ILoggerProvider创建的。我们可以自定义一个日志提供者和记录器,拦截所有日志输出,然后触发你的CallMeWhenLog逻辑(或者直接调用聊天工具发送消息)。
步骤1:创建自定义日志记录器
这个记录器会拦截日志,并通过事件暴露日志内容:
public class ChatNotificationLogger : ILogger { private readonly string _categoryName; // 定义事件,当有新日志时触发 public event Action<string>? LogReceived; public ChatNotificationLogger(string categoryName) { _categoryName = categoryName; } // 实现ILogger的接口方法,这些方法是日志系统自动调用的 public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default; // 可以在这里过滤日志级别,比如只处理Error和Information public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Information; public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { // 生成最终的日志字符串(包括异常信息) var loggedMessage = formatter(state, exception); // 触发事件,传递日志内容 LogReceived?.Invoke(loggedMessage); } }
步骤2:创建自定义日志提供者
提供者负责创建上面的日志记录器,并订阅它的事件来处理聊天通知:
public class ChatNotificationLoggerProvider : ILoggerProvider { private readonly ChatHandler _chatHandler; // 通过依赖注入获取ChatHandler,避免每次创建新实例 public ChatNotificationLoggerProvider(ChatHandler chatHandler) { _chatHandler = chatHandler; } public ILogger CreateLogger(string categoryName) { var logger = new ChatNotificationLogger(categoryName); // 订阅日志事件,触发聊天通知 logger.LogReceived += SendChatNotification; return logger; } // 这里就是你的CallMeWhenLog逻辑的实现 private async void SendChatNotification(string loggedMessage) { // 异步发送消息,避免阻塞日志流程(如果你的SendMessage是同步的,直接调用即可) await _chatHandler.SendMessageAsync(loggedMessage); } public void Dispose() { } }
步骤3:注册到依赖注入容器
在你的Worker程序的Program.cs里,把自定义提供者和ChatHandler注册到DI:
var host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<Worker>(); // 你的Worker服务 // 注册ChatHandler为单例,复用实例 services.AddSingleton<ChatHandler>(); // 注册自定义日志提供者,这样所有ILogger都会使用我们的记录器 services.AddSingleton<ILoggerProvider, ChatNotificationLoggerProvider>(); }) .Build(); await host.RunAsync();
步骤4:原代码无需修改
你的TestClass里的日志调用完全不需要改动,不管是_logger.LogInformation还是_logger.LogError,所有日志都会被自定义记录器拦截,并自动触发聊天通知:
public class TestClass { private readonly ILogger<TestClass> _logger; public TestClass(ILogger<TestClass> logger) { _logger = logger; } public async Task Process(Message message, CancellationToken cancellationToken = default) { _logger.LogInformation("New message!"); _logger.LogError(ex, "New message error!"); // 注意LogError的正确用法是先传异常 } }
注意事项
- 日志级别过滤:在
ChatNotificationLogger的IsEnabled方法里,你可以根据需要设置只处理特定级别的日志(比如只处理Error)。 - 异步处理:如果发送聊天消息是耗时操作,一定要用异步调用,避免阻塞日志流程(上面的示例用了
async void,因为事件处理程序允许这种写法,但如果是其他场景尽量用async Task)。 - DI原则:不要在日志逻辑里直接
new ChatHandler(),通过DI注入可以让代码更灵活、易于测试。
内容的提问来源于stack exchange,提问作者Losbaltica




