Serilog结合MassTransit Sink:IBus服务解析引发无限循环求助
解决Serilog Sink与MassTransit IBus的循环依赖问题
问题背景
搭建通过MassTransit发布日志消息的Serilog Sink时,启动阶段出现无限循环。核心原因是循环依赖:创建Serilog日志记录器需要依赖IBus实例的Sink,而MassTransit的IBus初始化过程又需要ILogger,导致双方初始化时互相触发,陷入循环调用。
原启动代码(Program.cs):
// 配置MassTransit,IBus已注册到DI容器 builder.Host.SetupMassTransit(); builder.Host.UseSerilog((ctx, services, lc) => { lc.WriteTo.Sink(new MassTransitSink(services.GetService<IBus>())); // 断点显示此处被反复调用 });
原MassTransitSink代码:
public class MassTransitSink : ILogEventSink { private readonly IBus bus; public MassTransitSink(IBus bus) { this.bus = bus; } public async void Emit(LogEvent logEvent) { await bus.Publish<MyLog>(new MyLog("Hello World")); } } public class MyLog { public MyLog(string message) { Message = message; } public string Message { get; set; } }
解决方案
通过延迟获取IBus实例打破循环依赖,避免在Serilog初始化阶段直接解析IBus,改为在首次处理日志事件时再初始化IBus。同时修复async void的潜在问题(未捕获异常会导致程序崩溃)。
1. 修改MassTransitSink实现
注入IServiceProvider并使用Lazy<IBus>延迟加载IBus实例,同时将Emit方法改为async Task:
public class MassTransitSink : ILogEventSink { private readonly Lazy<IBus> _lazyBus; // 注入DI容器的服务提供器,而非直接注入IBus public MassTransitSink(IServiceProvider serviceProvider) { _lazyBus = new Lazy<IBus>(() => serviceProvider.GetRequiredService<IBus>()); } // 替换async void为async Task,避免未捕获异常 public async Task Emit(LogEvent logEvent) { try { // 首次调用时才会初始化IBus,此时MassTransit已完成启动 await _lazyBus.Value.Publish<MyLog>(new MyLog(logEvent.RenderMessage())); } catch (Exception ex) { // 日志发布失败时降级处理,避免影响主程序 Console.WriteLine($"日志发布失败: {ex.Message}"); } } }
2. 调整Serilog配置
修改Serilog初始化代码,传入IServiceProvider而非直接获取IBus:
builder.Host.SetupMassTransit(); builder.Host.UseSerilog((ctx, services, lc) => { lc.WriteTo.Sink(new MassTransitSink(services)); });
原理说明
Lazy<IBus>确保IBus实例仅在首次处理日志事件时才从DI容器中解析,此时MassTransit已完成初始化,ILogger也已就绪,彻底打破循环依赖链。- 替换
async void为async Task,让Serilog能正确处理异步操作中的异常,避免未捕获异常导致程序崩溃。
内容的提问来源于stack exchange,提问作者Moniker
相关产品推荐
相关产品推荐

