Serilog Sink with MassTransit - IBus 服务解析导致无限循环

问题描述 投票:0回答:2

我正在尝试为 Serilog 设置一个接收器,通过 MassTransit 发布日志消息。 当我尝试在启动时创建接收器时遇到问题。创建接收器会导致无限循环。

我应该如何注册依赖注入来解决这个圈子?

我觉得是因为MassTransit.IBus使用了ILogger。因此,当创建记录器时,它会通过 IBus 请求我的接收器,IBus 请求记录器,后者请求 IBus,等等。

Program.cs中的启动代码

//Code that sets up my mass transit. IBus is registered as part of it.
builder.Host.SetupMassTransit(); 

builder.Host.UseSerilog((ctx, services, lc) =>
{
    lc.WriteTo.Sink(new MassTransitSink(services.GetService<IBus>())); //Set a breakpoint here
});

lc.WriteTo.Sink(...
上设置一个断点,你会看到它被重复调用。

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; } 
}
dependency-injection serilog masstransit
2个回答
2
投票

这是一个解决方法,不允许您使用 Microsoft 的 ILogger,接受的答案是允许您使用 Serilog.ILogger 和 Microsoft.Extension.Logging.ILogger

我能够通过手动将

Serilog.ILogger
的实例添加到依赖项容器来解决问题。

替换

UseSerilog
program.cs
部分:

builder.Host.UseSerilog((ctx, services, lc) =>
{
    lc.WriteTo.Sink(new MassTransitSink(services.GetService<IBus>()));
});

使用代码手动添加

ILogger
实例。

builder.Host.ConfigureServices(services =>
{
    services.AddSingleton<Serilog.ILogger>(sp =>
    {
        var bus = sp.GetRequiredService<IBus>();

        return new LoggerConfiguration()
            .MinimumLevel.Debug()
            .WriteTo.Sink(new MassTransitSink(bus))
            .CreateLogger();
    });

    services.AddLogging(configure => configure.ClearProviders().AddSerilog());
});

Serilog.ILogger
现在可以在调试器中使用。然而,
Microsoft.Extensions.Logging.ILogger
不起作用,即使它通常在设置使用
.UseSerilog()
时可用。

我想我会在这里发布这个以防其他人遇到同样的问题。我花了几个小时才弄清楚这个问题。

任何人都知道一个更具可读性和/或

Microsoft.Extensions.Logging.ILogger
Serilog.ILogger
可用的解决方案?


0
投票

问题是

ILogger
IBus
之间的循环引用。因为我们的
ILogger
是通过 serilog 解决的,带有一个汇到 MassTransit 的接收器,所以它需要一个
IBus
,这需要一个
ILogger
,重复。要修复它,请注册一个
ILoggerFactory
,它可以为 MassTransit 提供一个不依赖于
ILogger
IBus

//Under the hood, this registers an ILoggerFactory
builder.UseSerilog((context, services, lc) =>
{
    lc.WriteTo.Sink(new MassTransitSink(services.GetService<IBus>()));
});

builder.ConfigureServices((context, services) =>
{
    //Register an ILoggerFactory to replace the one serilog registers when calling builder.UseSerilog
    //This ILoggerFactory must provider MassTransit with an ILogger that is not dependent on an IBus.

    services.AddSingleton<ILoggerFactory, WorkAroundLoggerFactory>();
});

为什么有效:
在引擎盖下,serilog 注册了一个

SerilogLoggerFactory
,它实现了
ILoggerFactory
。在运行时,
ILoggerFactory
被解析并用于创建任何
ILogger
。因此,我们可以注册一个替换
ILoggerFactory
来查找为 MassTransit 请求的 ILoggers,并提供一个不依赖于
IBus
的 ILoggers,避免循环引用。

这里是我的替换ILoggerFactory的例子,WorkAroundLoggerFactory。 因为我仍然想要我的 ILoggers 来自 Serilog 的标准配置,所以

WorkAroundLoggerFactory
使用
SerilogLoggerFactory
。为了让 DI 容器找到
SerilogLoggerFactory
,我必须在调用
SerilogLoggerFactory
 之前注册类型 
builder.UseSerilog(...)

Program.cs中的启动代码

//Code that sets up my mass transit. IBus is registered as part of it.
builder.Host.SetupMassTransit(); 

builder.ConfigureServices((context, services) =>
{
    //If you want to use the SerilogLoggerFactory in your work around factory, you need to tell the DI container to watch for it, before it is registered as part of UseSerilog
    services.AddSingleton<SerilogLoggerFactory>();
});

builder.UseSerilog((context, services, lc) =>
{
    lc.WriteTo.Sink(new MassTransitSink(services.GetService<IBus>()));
});

builder.ConfigureServices((context, services) =>
{
    //Register the replacement ILoggerFactory
    services.AddSingleton<ILoggerFactory, WorkAroundLoggerFactory>();
});

WorkAroundLogger.cs

// A logger factory that uses the SerilogLoggerFactory for anything but MassTransit logs.
public class WorkAroundLoggerFactory : ILoggerFactory
{
    private readonly IServiceProvider serviceProvider;
    private SerilogLoggerFactory? serilogLoggerFactory;

    public WorkAroundLoggerFactory(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }
    
    void ILoggerFactory.AddProvider(ILoggerProvider provider)
    {
        serilogLoggerFactory!.AddProvider(provider);
    }

    ILogger ILoggerFactory.CreateLogger(string categoryName)
    {
        if (categoryName.StartsWith("MassTransit"))
        {
            //Return an ILogger that does not use MassTransit for logging any MassTransit logs.
            //If you wanted to log to windows event viewer, you could use EventLogLoggerProvider().CreateLogger(categoryName);
            return new MyLogger(); 
        }
        else
        {
            if (serilogLoggerFactory is null)
            {
                //We can't resolve the SerilogILoggerFactory at construction because it will cause a loop, because it will think it needs an IBus, which needs an ILogger, which needs an IBus, ... etc
                serilogLoggerFactory = serviceProvider.GetService(typeof(SerilogLoggerFactory)) as SerilogLoggerFactory;
                if (serilogLoggerFactory is null)
                {
                    throw new NullReferenceException(nameof(SerilogLoggerFactory));
                }
            }
            return serilogLoggerFactory.CreateLogger(categoryName);
        }
    }

    void IDisposable.Dispose()
    {
        if (serilogLoggerFactory != null)
        {
            serilogLoggerFactory.Dispose();
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.