在解决方案中跨多个程序集使用相同的Log4net记录器的最佳模式是什么?

时间:2009-11-11 04:37:33

标签: log4net projects-and-solutions

我有一个由主winforms应用程序组成的解决方案,以及我希望记录的相关内部编写的类库dll。此记录应由同一记录器执行,无论主UI客户端或关联的dll是否调用此记录。 dll当然可以被其他在其他解决方案中使用不同记录器的应用程序使用,但在这些情况下,它们将具有不同的log4net配置,并且可能完全不同的一组appender。

一种方法是在主应用程序中创建一个单例并从中进行记录,但是由于log4net是它自己的单例,只要我们将相同的字符串(或类型)传递给{{1},就可以引用它。我们将登录到同一目的地(在我的情况下,我希望使用RollingFileAppender)。

这很有效。但是,鉴于DLL将具有许多类,这意味着我们希望记录的每个类实例化或静态类将需要i)定义记录器名称的参数(以便记录到同一目的地)和ii)在每个入口点都需要调用log4net.LogManager.GetLogger

这里使用的最佳模式是什么?正确的方法是在每个程序集中创建单例实例吗?我担心的是,我们仍然需要将记录器名称传递给dll的每个入口点,这看起来有点过分。为了避免传入记录器名称,我可能会认为它总是等于log4net.LogManager.GetLogger(loggerName)

如果这对于log4net来说太难了,还有其他更简单的解决方案,例如企业日志记录块吗? 或者是面向方面编程(AOP)方法的最佳解决方案?

Reference for anti-pattern approach for singleton here

6 个答案:

答案 0 :(得分:19)

差不多一年后,但我还以为我会做出贡献!

根据您对Carl的帖子的评论,我想您可能误解了log4net的工作原理。是的,loggername确实标识了记录器。是的,记录器可以有很多appender。但是,许多记录器也可以写入SAME appender。因此,您可以将记录器“A”,“B”和“C”配置为所有记录到文件“X”。你可以得到这样的记录器:

ILog logger_a = LogManager.GetLogger("A");
ILog logger_b = LogManager.GetLogger("B");
ILog logger_c = LogManager.GetLogger("C");

现在,如果您使用这些记录器中的任何一个进行记录,如果您按照这种方式进行配置,它们都可以转到相同的位置(文件“X”)。

在整个应用程序中不使用相同记录器名称的优点是,您可以通过配置文件控制应用程序中不同位置的日志记录级别。因此,如果使用记录器“A”和“B”的代码工作正常,但使用记录器“C”的代码出现问题,则可以关闭“A”和“B”,然后转“ C“一路向上。这样你就可以获得更少的信息来解决问题。

大多数人(或至少大多数log4net示例)实际上为每个CLASS创建一个静态记录器实例,以该类命名(我不记得确切的语法,但很容易找到示例)。这为您提供了非常高的粒度级别,可用于控制日志记录。

在app.config文件中,您可以通过配置名为“*”的单个记录器来控制同一级别的所有记录器,或者您可以配置特定记录器(使用完全限定的类型名称),或者甚至可以通过部分配置来配置完全限定的类型名称。例如,您可以非常轻松地将名称空间ABC日志中的所有类设置为“info”级别,名称空间DEF中的所有类都记录在“错误”级别,并且名称空间GHI中的所有类都不会记录。并且所有这些记录器都可以登录到同一目的地(例如文件X)。

可能来不及帮助,但也许不是......

答案 1 :(得分:3)

首先,您应该创建一个包装类(将您的应用与日志记录提供程序分离)。

此包装类可以使用记录器标识符。如果您正在使用IoC容器,则只需注入记录器的名称或现有的预配置实例。

如果您使用Unity(其他容器类似),您可以执行类似

的操作
// During application initialization
IUnityContainer myContainer = new UnityContainer();
LoggingService concreteLoggingService = new LoggingService( "logID" );
myContainer.RegisterInstance<ILoggingService>( concreteLoggingService );

// This would be injected, so you wouldn't see this, but it's here for consistency
ILoggingService loggingService = myContainer.Resolve<ILoggingService>();
loggingService.LogMessage( "message" );

现在,假设您有一个IoC容器。您也可以创建服务定位器:

// During application initialization
ServiceLocator.Register<ILoggingService>( new LoggingService( "logID" ) );

// Retrieved as needed
ILoggingService loggingServce = LoggingServiceLocator.Locate<ILoggingService>();
loggingService.LogMessage( "message" );

在第二种情况下,您需要编写所有管道代码。使用IoC容器,您可以获得开箱即用的功能。

答案 2 :(得分:1)

我猜您可以在app.config中使用loggerName。

请问为什么loggerName必须与整个应用程序相同?

答案 3 :(得分:1)

我从未见过另一种有意义的使用模式,而不是log4net:

在需要记录的任何类之上,执行:

private static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

在主机层AssemblyInfo.cs中,执行:

[assembly: log4net.Config.XmlConfigurator]

在主机启动时,要么对appenders等进行编程设置,要么使用普通的app.config配置。

故事结束。没有包装。不能从上面推导出来。

我遇到过的每一种其他使用模式都是为了避免错误地理解log4net appender,包括这里的部分/全部答案。

答案 4 :(得分:0)

我们采用了类似于此的log4net方法。

我们在主应用程序和类库程序集中都使用log4net。我们围绕它编写了一个包装类,以便所有日志记录在我们调用的所有位置都显示相同的配置,并确保它是单例。这个包装类是从我们的应用程序中的任何地方调用的。

因此,类库和主应用程序都使用相同的配置注销到同一个日志文件,这正是我们想要的。这是你想要实现的目标吗?

答案 5 :(得分:0)

如何实现自己的TraceListener,以便在整个应用程序中使用Trace.Write?