結合 AOP 輕鬆處理事件發布處理日誌_包裝設計

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。

結合 AOP 輕鬆處理事件發布處理日誌

Intro

前段時間,實現了 EventBus 以及 EventQueue 基於 Event 的事件處理,但是沒有做日誌(EventLog)相關的部分,原本想增加兩個接口, 處理事件發布日誌和事件處理日誌,最近用了 AOP 的思想處理了 EntityFramework 的數據變更自動審計,於是想着事件日誌也用 AOP 的思想來實現,而且可能用 AOP 來處理可能會更好一些,最近自己造了一個 AOP 的輪子 —— FluentAspects,下面的示例就以它來演示了,你也可以換成自己喜歡的 AOP 組件,思想是類似的

事件日誌示例

事件發布日誌

事件發布日誌只需要攔截事件發布的方法調用即可,在發布事件時進行攔截,在攔截器中根據需要進行日誌記錄即可

事件發布者接口定義:

public interface IEventPublisher
{
    /// <summary>
    /// publish an event
    /// </summary>
    /// <typeparam name="TEvent">event type</typeparam>
    /// <param name="event">event data</param>
    /// <returns>whether the operation succeed</returns>
    bool Publish<TEvent>(TEvent @event) where TEvent : class, IEventBase;

    /// <summary>
    /// publish an event async
    /// </summary>
    /// <typeparam name="TEvent">event type</typeparam>
    /// <param name="event">event data</param>
    /// <returns>whether the operation succeed</returns>
    Task<bool> PublishAsync<TEvent>(TEvent @event) where TEvent : class, IEventBase;
}

事件發布日誌攔截器:

public class EventPublishLogInterceptor : AbstractInterceptor
{
    public override async Task Invoke(IInvocation invocation, Func<Task> next)
    {
        Console.WriteLine("-------------------------------");
        Console.WriteLine($"Event publish begin, eventData:{invocation.Arguments.ToJson()}");
        var watch = Stopwatch.StartNew();
        try
        {
            await next();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Event publish exception({ex})");
        }
        finally
        {
            watch.Stop();
            Console.WriteLine($"Event publish complete, elasped:{watch.ElapsedMilliseconds} ms");
        }
        Console.WriteLine("-------------------------------");
    }
}

事件處理日誌

事件處理器接口定義:

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

public interface IEventHandler
{
    Task Handle(object eventData);
}

事件處理日誌攔截器定義:

public class EventHandleLogInterceptor : IInterceptor
{
    public async Task Invoke(IInvocation invocation, Func<Task> next)
    {
        Console.WriteLine("-------------------------------");
        Console.WriteLine($"Event handle begin, eventData:{invocation.Arguments.ToJson()}");
        var watch = Stopwatch.StartNew();
        try
        {
            await next();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Event handle exception({ex})");
        }
        finally
        {
            watch.Stop();
            Console.WriteLine($"Event handle complete, elasped:{watch.ElapsedMilliseconds} ms");
        }
        Console.WriteLine("-------------------------------");
    }
}

AOP 配置

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(builder =>
    {
        builder.UseStartup<Startup>();
    })
    .UseFluentAspectsServiceProviderFactory(options =>
    {
        // 攔截器配置
        
        // 攔截 `IEventPublisher` 日誌,註冊事件發布日誌攔截器
        options
            .InterceptType<IEventPublisher>()
            .With<EventPublishLogInterceptor>();

        // 攔截 `IEventHandler`,註冊事件處理日誌攔截器
        options.InterceptType<IEventHandler>()
            .With<EventHandleLogInterceptor>();
    }, builder =>
    {
        // 默認使用默認實現來生成代理,現在提供了 Castle 和 AspectCore 的擴展,也可以自己擴展實現自定義代理生成方式
        // 取消註釋使用 Castle 來生成代理
        //builder.UseCastleProxy();
    }, t => t.Namespace?.StartsWith("WeihanLi") == false // 要忽略的類型斷言
    )
    .Build()
    .Run();

More

事件發布示例,定義了一個發布事件的中間件:

// pageView middleware
app.Use((context, next) =>
{
    var eventPublisher = context.RequestServices
        .GetRequiredService<IEventPublisher>();
    eventPublisher.Publish(new PageViewEvent()
    {
        Path = context.Request.Path.Value,
    });

    return next();
});

事件處理示例是用一個消息隊列的模式來處理的,示例和前面的事件的文章類似,EventConsumer 是一個後台任務,完整代碼示例如下:

public class EventConsumer : BackgroundService
{
    private readonly IEventQueue _eventQueue;
    private readonly IEventHandlerFactory _eventHandlerFactory;

    public EventConsumer(IEventQueue eventQueue, IEventHandlerFactory eventHandlerFactory)
    {
        _eventQueue = eventQueue;
        _eventHandlerFactory = eventHandlerFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var queues = await _eventQueue.GetQueuesAsync();
            if (queues.Count > 0)
            {
                await queues.Select(async q =>
                        {
                            var @event = await _eventQueue.DequeueAsync(q);
                            if (null != @event)
                            {
                                var handlers = _eventHandlerFactory.GetHandlers(@event.GetType());
                                if (handlers.Count > 0)
                                {
                                    await handlers
                                            .Select(h => h.Handle(@event))
                                            .WhenAll()
                                        ;
                                }
                            }
                        })
                        .WhenAll()
                    ;
            }

            await Task.Delay(1000, stoppingToken);
        }
    }
}

完整的示例代碼可以從https://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/AspNetCoreSample 獲取

OverMore

之前在微軟的 EShopOnContainers 項目里又看到類似下面這樣的代碼,在發布事件的時候包裝一層 try … catch 來記錄事件發布日誌,相比之下,本文示例中的這種方式更為簡潔,代碼更清爽

Reference

  • https://www.cnblogs.com/weihanli/p/12941919.html
  • https://www.cnblogs.com/weihanli/p/implement-event-queue.html
  • https://github.com/WeihanLi/WeihanLi.Common
  • https://github.com/WeihanLi/WeihanLi.Common/blob/dev/samples/AspNetCoreSample/Startup.cs

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務