【问题标题】:How do you output the context class using log4net as a service?如何使用 log4net 作为服务输出上下文类?
【发布时间】:2011-05-14 21:30:35
【问题描述】:

我使用 Log4Net 作为服务,它使用 StructureMap 注入到其他服务中。

如何确保日志文件包含进行 log4net 调用的调用服务类上下文(类名和/或线程)?

当然,调用类或线程将始终是日志服务,这无法帮助我了解日志调用的真正来源。

编辑:

注册码:

  ObjectFactory.Initialize(x =>
    {           
        x.For<ILog>().AlwaysUnique().Use(s => s.ParentType == null ? 
            LogManager.GetLogger(s.BuildStack.Current.ConcreteType) : 
            LogManager.GetLogger(s.ParentType));

    });

服务层:

public class LoggerService : ILoggerService
    {
        private readonly ILog log;

        public LoggerService(ILog logger)
        {            
            log = logger;
            log.Info("Logger started {0}".With(logger.Logger.Name));
        }       

        public void Info(string message)
        {
            log.Info(message);
        }
}

在日志记录中,我仍然总是将 LoggerService 作为上下文,因此我永远不会看到实际调用的记录器。它似乎无法正常工作。我觉得我在这里遗漏了一些东西......

编辑 2: 我在这里为控制台应用添加了粘贴链接:

http://pastie.org/1897389

我希望父类被记录,但它不能在最简单的级别上工作。

【问题讨论】:

  • 看来您已经将记录器抽象到了我通常会做的水平之上,但请尝试使用 ObjectFactory.Initiatlize 来代替:用 [LogManager.GetLogger(s.ParentType)] 替换 [LogManager.GetLogger(s.ParentType)]。 GetLogger(s.ParentType.UnderlyingSystemType.Name))]
  • @Bryan - 我尝试改用 s.root.ConcreteType.Name 并将 app.config 设置为使用 %logger 输出记录器的名称。虽然它没有给我 SubActionService 记录器名称(请参阅下面的我的粘贴链接)。 UnderlyingSystemType 总是 LoggerService 这没有帮助。
  • 尝试在 [x.For().AlwaysUnique().Use] lambda 表达式中使用断点,在监视窗口内检查 [s.ParentType] 的值并钻取降低到您正在寻找的价值。这就是我在使用您的源代码查看注入类的父类型时使用的。

标签: dependency-injection log4net structuremap


【解决方案1】:

您可能想查看Castle Dynamic proxy 以便使用 AOP 解决它。有 an example 在 Structure Map Google Group 上将其与 Structure Map 一起使用。

Ayende 拥有an example 使用 Log4Net 和 Windsor 的基于 AOP 的日志记录。

【讨论】:

  • 感谢您的链接。有什么方法可以使用 StructureMap,因为这是我的应用程序目前用于 DI 的方法?
  • 是的,看看我使用 StructureMap 链接到的示例:bit.ly/mmAsN5(它使用 SM 的旧语法,但原理相同)。基本上,您使用 EnrichWith 并使用 DynamicProxy 包装要记录的实例并实现 Logging 拦截器。示例中的拦截器记录到控制台,但您可以将其切换到 Log4Net。
【解决方案2】:

我在生成的很多代码中都使用了 StructureMap,并且我有一个 StructureMap 注册表,用于将记录器挂接到它所注入的类的上下文中。

作为参考,我使用的是 StructureMap 的 2.6.2 版本,但在 2.5+ 中使用新的 .For().Use() 格式应该没问题。

public class CommonsRegistry : Registry
{
  public CommonsRegistry()
  {
    For<ILogger>().AlwaysUnique().Use(s => s.ParentType == null ? new Log4NetLogger(s.BuildStack.Current.ConcreteType) : new Log4NetLogger(s.ParentType.UnderlyingSystemType.Name));
    XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location), "Log.config")));
  }
}

此注册表的作用是针对注入 ILogger 的任何位置,使用它注入的类是记录消息记录到/上下文的位置。

*另外,在第二行 (XmlConfigurator.ConfigureAndWatch) 中,我告诉 Log4Net 从文件“Log.config”而不是应用程序配置文件中获取日志信息,您可能喜欢也可能不喜欢,并且可以省略。

我使用的代码是一个常见的 IOC.Startup 例程,如果我想使用默认寄存器,我会通过它。

ObjectFactory.Initialize(x =>
{
  x.AddRegistry<CommonsRegistry>();
  ...
}

这为我提供了日志实例中的调用类名称,消息会自动记录到该实例中,所需要做的就是将记录器注入到类中。

class foo
{
  private readonly ILogger _log;

  public foo(ILogger log)
  {
    _log = log;
  }
}

现在消息被记录为上下文/类“foo”。

【讨论】:

  • 嗨,这与第一个建议的丰富功能相比如何?我正在尝试权衡这两种选择的利弊。不过看起来不错!
  • 丰富文档说“EnrichWith() -- 注册一个在创建后针对新对象运行的 Func,并为您提供返回与原始对象不同的对象的选项”我的注册表函数在哪里正在创建记录器,设置正确的上下文(在我的示例中为父类 foo)。
  • @jaffa - 正如@Bryan 所说,EnrichWith 将对象替换为新对象。在使用 DynamicProxy 进行日志记录的情况下,您将围绕目标创建一个包装类,用于执行日志记录。使用 AOP 的优点是您不必将日志记录代码添加到您要记录的每个位置,它会在运行时被拦截并自动记录。有关更多解释,请参阅 Ayendes 帖子。
  • 更新了 CommonsRegistry 例程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-28
  • 1970-01-01
  • 2013-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多