自定义Serilog水槽与注入?

Custom Serilog sink with injection?(自定义Serilog水槽与注入?)

本文介绍了自定义Serilog水槽与注入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个简单的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)});
        }
    }
}

消费微服务如下启动:

        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();

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": {}
        }
    ]
  }

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

问题是在执行_logger.LogInformation(...)在微服务中,发送永远不会被触发,但如果我添加一个控制台接收器,它会输出数据吗?我还怀疑注入的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));
        }
    }

这使得可以按如下方式添加自定义接收器:

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();

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

推荐答案

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

依赖项注入意味着类的构造函数将接受必要的对象作为参数。虽然无法使用构造函数参数加载自定义接收器,但必须通过该服务来完成。

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

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

但是,我无法删除MS ILogger要求,并且我仍然需要能够在自定义接收器中进行日志记录。因此,首先我创建了一个如下所示的Serilog:

private static readonly ILogger _logger = Log.ForContext<MessageQueueSink>() as ILogger;

然后可以将此记录器包装在MS Ilogger类中,并将其发送到静态生产者方法:

public class CustomSerilogger : Microsoft.Extensions.Logging.ILogger
{
    private readonly ILogger _logger;
    public CustomSerilogger(ILogger logger)
    { _logger = logger; }

    public IDisposable BeginScope<TState>(TState state) => default!;

    public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) { return _logger.IsEnabled(LogLevelToLogEventLevel(logLevel)); }

    public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if (!IsEnabled(logLevel))
            return;

        _logger.Write(LogLevelToLogEventLevel(logLevel), exception, state.ToString());
    }

    private LogEventLevel LogLevelToLogEventLevel(Microsoft.Extensions.Logging.LogLevel loglevel)
    {
        switch(loglevel)
        {
            case Microsoft.Extensions.Logging.LogLevel.Debug:
                return LogEventLevel.Debug;
            case Microsoft.Extensions.Logging.LogLevel.Information:
                return LogEventLevel.Information;
            case Microsoft.Extensions.Logging.LogLevel.Warning:
                return LogEventLevel.Warning;
            case Microsoft.Extensions.Logging.LogLevel.Error:
                return LogEventLevel.Error;
            case Microsoft.Extensions.Logging.LogLevel.Critical:
                return LogEventLevel.Fatal;
            case Microsoft.Extensions.Logging.LogLevel.None:
                return LogEventLevel.Verbose;
            case Microsoft.Extensions.Logging.LogLevel.Trace:
                return LogEventLevel.Verbose;
        }
        return LogEventLevel.Verbose;
    }
}

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

var mqSinkSettings = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build()
                                                    .GetSection("MessageQueueSinkSettings").Get<MessageQueueSinkSettings>();

终于成功了!

这篇关于自定义Serilog水槽与注入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:自定义Serilog水槽与注入?