You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

.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的正确用法是先传异常
    }
}

注意事项

  • 日志级别过滤:在ChatNotificationLoggerIsEnabled方法里,你可以根据需要设置只处理特定级别的日志(比如只处理Error)。
  • 异步处理:如果发送聊天消息是耗时操作,一定要用异步调用,避免阻塞日志流程(上面的示例用了async void,因为事件处理程序允许这种写法,但如果是其他场景尽量用async Task)。
  • DI原则:不要在日志逻辑里直接new ChatHandler(),通过DI注入可以让代码更灵活、易于测试。

内容的提问来源于stack exchange,提问作者Losbaltica

火山引擎 最新活动