带注入的定制 Serilog 水槽?

Ban*_*hee 8 c# code-injection serilog ilogger

我创建了一个简单的 Serilog 接收器项目,如下所示:

namespace MyApp.Cloud.Serilog.MQSink
{
    public class MessageQueueSink: ILogEventSink
    {
        private readonly IMQProducer _MQProducerService;
        public MessageQueueSink(IMQProducer mQProducerService)
        {
            _MQProducerService = mQProducerService;
        }
        public void Emit(LogEvent logEvent)
        {
            _MQProducerService.Produce<SendLog>(new SendLog() { LogEventJson = JsonConvert.SerializeObject(logEvent)});
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

消费微服务的启动方式如下:

        var configurationBuilder = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
        var appSettings = configurationBuilder.Get<AppSettings>();

        configurationBuilder = new ConfigurationBuilder().AddJsonFile("ExtendedSettings.json").Build();

            Host.CreateDefaultBuilder(args)
                .UseMyAppCloudMQ(context => context.UseSettings(appSettings.MQSettings))
                .UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
                .ConfigureServices((hostContext, services) =>
                {
                    services
                        .AddHostedService<ExtendedProgService>()
                        .Configure<MQSettings>(configurationBuilder.GetSection("MQSettings"))
                })
                .Build().Run();
Run Code Online (Sandbox Code Playgroud)

appsettings.json 的 serilog 部分如下所示:

  "serilog": {
    "Using": [ "Serilog.Sinks.File", "Serilog.Sinks.Console", "MyApp.Cloud.Serilog.MQSink" ],
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId" ],
    "WriteTo": [
      {
        "Name": "MessageQueueSink",
        "Args": {}
        }
    ]
  }
Run Code Online (Sandbox Code Playgroud)

MQSink 项目被添加为微服务项目的引用,我可以看到 MQSink dll 最终位于 bin 文件夹中。

问题是,当在微服务中执行 _logger.LogInformation(...) 时,永远不会触发 Emit,但是如果我添加控制台接收器,它会输出数据吗?我也怀疑注入的MQ不能正常工作?

怎么解决这个问题呢?

编辑 :

打开Serilog内部日志可以看到找不到MessageQueueSink这个方法。我没有找到任何方法让它与 appsetings.json 一起工作,所以我开始研究如何在代码中绑定它。

为了让它工作,必须创建一个扩展:

public static class MySinkExtensions
    {
        public static LoggerConfiguration MessageQueueSink(
                  this Serilog.Configuration.LoggerSinkConfiguration loggerConfiguration,
                  MyApp.Cloud.MQ.Interface.IMQProducer mQProducer = null)
        {
            return loggerConfiguration.Sink(new MyApp.Cloud.Serilog.MQSink.MessageQueueSink(mQProducer));
        }
    }
Run Code Online (Sandbox Code Playgroud)

这使得添加自定义接收器成为可能,如下所示:

Host.CreateDefaultBuilder(args)
                    .UseMyAppCloudMQ(context => context.UseSettings(appSettings.MQSettings))
                     .ConfigureServices((hostContext, services) =>
                    {
                        services
                            .Configure<MQSettings>(configurationBuilder.GetSection("MQSettings"))
                    })
                    .UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration).WriteTo.MessageQueueSink())
                    .Build().Run();
Run Code Online (Sandbox Code Playgroud)

自定义接收器已加载并触发 Emit,但我仍然不知道如何将 MQ 注入到接收器中?如果我可以在 appsettings.json 文件中完成 Serilog 和接收器的所有配置,那就更好了。

Ban*_*hee 0

@pfx 帮助如何加载 Serilog 自定义接收器,为此我给了他赏金,谢谢!然而,这不是我的情况的最终解决方案。

\n

依赖注入意味着类的构造函数将采用必要的对象作为参数。虽然\xe2\x80\x99s 不可能使用构造函数参数加载自定义接收器,但必须通过服务来完成。

\n
public static IHost CreateHostBuilder(string[] args)\n           => Host.CreateDefaultBuilder(args)\n                    .ConfigureServices((hostContext, services) =>\n                    {\n                        services\n                            .AddTransient<IMyService, MyService>()\n                            .AddTransient<ICommunicator, Communicator>()\n                            .AddTransient<ILogEventSink, CustomSerilogSink>();\n                    })\n                   .UseSerilog((context, services, configuration) => configuration\n                                                                       .ReadFrom.Configuration(context.Configuration)\n                                                                       .ReadFrom.Services(services)\n                                                                       .WriteTo.Console(LogEventLevel.Information)\n                                                                       .Enrich.FromLogContext())\n           .Build();\n        }\n
Run Code Online (Sandbox Code Playgroud)\n

问题是 MQ 控制器类注入了一个 ILogger,这创建了一个循环引用,导致释放。MQ 控制器是共享的,因此我无法对其进行太多更改,但我可以将所需的方法设为静态。它们没有使用本地 MQ 对象,而是被发送到该方法中。

\n

然而,我无法删除 MS ILogger 要求,并且我仍然需要能够在自定义接收器中登录。所以首先我创建了一个像这样的 Serilog:

\n
private static readonly ILogger _logger = Log.ForContext<MessageQueueSink>() as ILogger;\n
Run Code Online (Sandbox Code Playgroud)\n

然后可以将该记录器包装在 MS Ilogger 类中并发送到静态生成器方法:

\n
public class CustomSerilogger : Microsoft.Extensions.Logging.ILogger\n{\n    private readonly ILogger _logger;\n    public CustomSerilogger(ILogger logger)\n    { _logger = logger; }\n\n    public IDisposable BeginScope<TState>(TState state) => default!;\n\n    public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) { return _logger.IsEnabled(LogLevelToLogEventLevel(logLevel)); }\n\n    public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)\n    {\n        if (!IsEnabled(logLevel))\n            return;\n\n        _logger.Write(LogLevelToLogEventLevel(logLevel), exception, state.ToString());\n    }\n\n    private LogEventLevel LogLevelToLogEventLevel(Microsoft.Extensions.Logging.LogLevel loglevel)\n    {\n        switch(loglevel)\n        {\n            case Microsoft.Extensions.Logging.LogLevel.Debug:\n                return LogEventLevel.Debug;\n            case Microsoft.Extensions.Logging.LogLevel.Information:\n                return LogEventLevel.Information;\n            case Microsoft.Extensions.Logging.LogLevel.Warning:\n                return LogEventLevel.Warning;\n            case Microsoft.Extensions.Logging.LogLevel.Error:\n                return LogEventLevel.Error;\n            case Microsoft.Extensions.Logging.LogLevel.Critical:\n                return LogEventLevel.Fatal;\n            case Microsoft.Extensions.Logging.LogLevel.None:\n                return LogEventLevel.Verbose;\n            case Microsoft.Extensions.Logging.LogLevel.Trace:\n                return LogEventLevel.Verbose;\n        }\n        return LogEventLevel.Verbose;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

要配置接收器,我必须在 appsettings.json 文件中创建自定义接收器设置部分,然后将其读入设置对象。

\n
var mqSinkSettings = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build()\n                                                    .GetSection("MessageQueueSinkSettings").Get<MessageQueueSinkSettings>();\n
Run Code Online (Sandbox Code Playgroud)\n

终于成功了!

\n