【问题标题】:How can I get NLog to inject dependencies into a target?如何让 NLog 将依赖项注入目标?
【发布时间】:2020-09-17 13:19:10
【问题描述】:

我有一个自定义的 NLog 日志目标类,如下所示:

public class MyTarget : AsyncTaskTarget
{
   public MyTarget() {}

   public MyTarget(INeedThisThingToFunction thing)
   {
      Thing = thing;
   }

   public INeedThisThingToFunction Thing { get; set; }

   public override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
   {
      Thing.Use();
      return null;
   }
}

我不知道如何确保调用第二个构造函数。我在 Program.cs 中完成了这个:

public static void Main(string[] args)
{
   var host = CreateWebHostBuilder(args).Build();
   ConfigureLogging(host.Services.GetAutofacRoot());
   LogManager.GetCurrentClassLogger().Info("Hi mom");
   host.Run();
}

private static void ConfigureLogging(IComponentContext container) {
   ConfigurationItemFactory.Default.CreateInstance = type =>
   {
      if (type != typeof(MyTarget) return Activator.CreateInstance(type);
      var thing = new ThingTheTargetNeedsToFunction();
      return new MyTarget(thing);
   }
   LogManager.Configuration.Reload();
}

我也尝试了很多其他的方法,但这是最接近做某事的方法。当LogManager.Configuration.Reload() 被调用时,CreateInstance 代码触发;但是当Info 方法触发时,目标上的Thing 属性为空。

有没有更好的方法来做到这一点?比如,一种有效的方式?

使用 .NET Core 3、NLog、Autofac。

【问题讨论】:

  • 这能回答你的问题吗? NLog: Dependency Injection for custom Targets
  • 我希望避免使用服务定位器模式,因为对我来说这似乎违背了 DI 的目的。如果我可以注册我的日志目标并让 Autofac 正确实例化它们,我会很高兴。或 NLog。
  • @DavidBraverman 现在 NLog 依赖注入支持相当有限。主要是因为日志记录通常是在依赖容器完全加载之前设置的。但是正在努力改善这种情况,因此自定义目标可以在初始化期间解决自定义依赖项(而不是构造函数参数)。并希望自动延迟初始化,直到自定义依赖项可用。并且希望还将 LogEvents 排队,以便在成功执行初始化后写入它们。另见github.com/NLog/NLog/issues/3675(欢迎反馈)

标签: c# .net-core dependency-injection autofac nlog


【解决方案1】:

如果Thing 仅在构建host 后可用,那么您可以这样做:

public static void Main(string[] args)
{   
   var host = CreateWebHostBuilder(args).UseNLog().Build();
   ConfigureLogging(host.Services.GetAutofacRoot());
   LogManager.GetCurrentClassLogger().Info("Hi mom");
   host.Run();
}

private static void ConfigureLogging(IComponentContext container)
{
   var defaultConstructor = ConfigurationItemFactory.Default.CreateInstance;
   ConfigurationItemFactory.Default.CreateInstance.CreateInstance = type =>
   {
      if (type == typeof(MyTarget))
      {
          var thing = new ThingTheTargetNeedsToFunction();
          return new MyTarget(thing);
      }

      return defaultConstructor(type);
   };

   // Reload config and assign the newly reloaded config
   LogManager.Configuration = LogManager.Configuration?.Reload();
}

然后确保您的自定义 MyTarget 可以处理它在“禁用模式”下运行,其中 Thing 未分配:

[Target("MyTarget")] 
public class MyTarget : AsyncTaskTarget
{
   public MyTarget() {}

   public MyTarget(INeedThisThingToFunction thing)
   {
      Thing = thing;
   }

   public INeedThisThingToFunction Thing { get; set; }

   public override await Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken cancellationToken)
   {
      if (Thing == null)
         return null; // Handle that `Thing` is unassigned by default-constructor

      await Thing.UseAsync().ConfigureAwait(false);
   }
}

【讨论】:

  • 不幸的是,这也不起作用。当 ConfigureLogging 方法运行时,Thing 在 MyTarget 类中得到正确设置。但是当 LogManager.GetCurrentClassLogger() 运行时,MyTarget.Thing 属性为空。 Autofac 是否在不同的上下文或线程中运行?如果不求助于服务定位器反模式,就不可能做我想做的事吗?
  • @DavidBraverman 记住它是LogManager.Configuration = LogManager.Configuration?.Reload(); 而不仅仅是LogManager.Configuration.Reload()
猜你喜欢
  • 2011-08-26
  • 1970-01-01
  • 2020-03-14
  • 2017-08-06
  • 1970-01-01
  • 2020-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多