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

发布于 2024-08-10 13:48:18 字数 893 浏览 11 评论 0原文

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

一种方法是在主应用程序中创建一个单例并从中记录日志,但是由于 log4net 是它自己的单例,因此只要我们将相同的字符串(或类型)传递给 log4net.LogManager 就可以引用它。 GetLogger 我们将记录到相同的目的地(在我的例子中,我希望使用 RollingFileAppender)。

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

这里使用的最佳模式是什么?正确的方法是在每个程序集中创建一个单例实例吗?我担心的是,我们仍然需要将记录器名称传递给 dll 的每个入口点,这似乎有点过分了。为了避免传入记录器名称,我可能假设它始终等于 System.Reflection.Assembly.GetCallingAssembly().GetName().Name。

如果这对于 log4net 来说太困难了,是否还有其他更简单的解决方案,例如 Enterprise Logging Block? 或者最好的解决方案是面向方面编程(AOP)方法?

此处提供单例反模式方法的参考

I have a solution consisting of a main winforms app, with associated internally written class library dll’s from which I wish to log. This logging should performed by the same logger, regardless of whether the main UI client or the associated dll’s call this. The dll’s may of course be used by other apps that have different loggers in other solutions, but in these circumstances will have a different log4net config and maybe a different suite of appenders altogether.

One approach would be to create a singleton within the main app and log from that, however since log4net is its own singleton, that can be referenced so as long as we pass the same string (or type) to log4net.LogManager.GetLogger we will be logging to the same destination (in my case I wish to use a RollingFileAppender).

This works. However, given that the DLL will have a number of classes it would mean each class instantiation or static class from which we wish to log would require i) an argument defining the logger name (in order to log to the same destination) and ii) at each entry point would need to call log4net.LogManager.GetLogger(loggerName).

What is the best pattern to use here? Would the correct approach be to create a singleton instance in each assembly? My concern here is that we will still need to pass in the logger name to each entry point for the dll, which seems like overkill. In order to avoid passing in the logger name, I might assume that it is always equal to System.Reflection.Assembly.GetCallingAssembly().GetName().Name.

If this is all too difficult for log4net, are there other easier solutions such as the Enterprise Logging Block?
Or is the best solution an aspect oriented programming (AOP) approach?

Reference for anti-pattern approach for singleton here .

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

嘿嘿嘿 2024-08-17 13:48:18

差不多一年过去了,但我想我还是会做出贡献!

根据您对 Carl 帖子的评论,我认为您可能误解了 log4net 的工作原理。是的,记录器名称确实标识了记录器。是的,一个记录器可以有许多附加程序。但是,许多记录器也可以写入相同的附加程序。因此,您可以将记录器“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 示例)实际上为每个类创建一个静态记录器实例,以该类命名(我不记得确切的语法,但很容易找到示例)。这为您提供了非常高的粒度来控制日志记录。

在 app.config 文件中,您可以通过配置名为“*”的单个记录器来控制同一级别的所有记录器,也可以配置特定的记录器(通过使用完全限定的类型名),甚至可以通过完全限定的类型名的一部分进行配置类型名称。例如,您可以很容易地使命名空间 ABC 中的所有类在“信息”级别记录,命名空间 DEF 中的所有类在“错误”级别记录,而命名空间 GHI 中的所有类根本不记录。并且所有这些记录器都可以记录到相同的目的地(例如文件X)。

可能来得太晚了,但也许不是……

Almost one year later, but I thought I would contribute anyway!

Based on your comment to Carl's post, I think that maybe you misunderstand how log4net works. Yes, the loggername does identify the logger. Yes, a logger can have many appenders. BUT, many loggers can also write to the SAME appender. So, you could configure loggers "A", "B", and "C" to all log to file "X". You can get the loggers like this:

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

Now, if you log with any of these loggers, they CAN all go to the same place (file "X") if you configured them that way.

The advantage to NOT using the same logger name throughout your application is that you can control the level of logging in different places in your application via the config file. So, if the code that is using loggers "A" and "B" is working fine, but the code that is using logger "C" is having a problem, you could turn "A" and "B" off, and turn "C" all the way up. That way you have less information to have to dig through to find your problem.

Most people (or at least most log4net examples) actually create a static logger instance per CLASS, named for that class (I don't remember the exact syntax but it is easy to find examples). This gives you a very high level of granularity for controlling your logging.

In your app.config file, you can control all loggers at the same level by configuring a single logger called "*" or you can configure specific loggers (by using the fully qualified typename) or you can even configure by part of the fully qualified typename. For example, you could very easily make all classes in namespace ABC log at "info" level, all classes in namespace DEF log at "error" level, and all classes in namespace GHI not log at all. AND all of these loggers can log to the same destination (e.g file X).

Might have been too late to help, but maybe not...

绝影如岚 2024-08-17 13:48:18

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

该包装类可以采用记录器标识符。如果您使用的是 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 容器,您可以开箱即用。

First, you should create a wrapper class (to decouple your app from the logging provider).

This wrapper class could take the logger identifier. If you're using an IoC container, you could just inject the name of the logger, or an existing, pre-configured, instance.

If you're using Unity (other containers are similar), you could do something like

// 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" );

Now, this assumes you have an IoC container. You can alternatively create a service locator:

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

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

In the second case you need to write all the plumbing code. With an IoC container, you get that out-of-the-box.

你曾走过我的故事 2024-08-17 13:48:18

我猜你的 app.config 中可能有 loggerName。

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

I guess you could have the loggerName in your app.config.

May I ask why the loggerName has to be the same for the whole application?

橪书 2024-08-17 13:48:18

我从未见过比 log4net 更有意义的使用模式:

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

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

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

[assembly: log4net.Config.XmlConfigurator]

在主机启动中执行附加程序等的编程设置,或者使用普通的 app.config 配置。

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

我遇到的每一个其他使用模式都是为了避免不真正理解 log4net 附加程序,包括这里的一些/所有答案。

I have never seen another, meaningful, usage pattern than this for log4net:

On top of any class that needs logging, do:

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

In the host layers AssemblyInfo.cs, do:

[assembly: log4net.Config.XmlConfigurator]

In the host startup either do programmatic setup of the appenders etc, or use plain app.config configuration.

End of story. No wrappers. No derivation from the above.

Each and every other usage pattern I have come across, is made to circumvent not really understanding log4net appenders, including some/all of the answers here.

双手揣兜 2024-08-17 13:48:18

我们遵循与此类似的使用 log4net 的方法。

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

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

We followed an approach for using log4net similar to this.

We use log4net in both the main app and class library assemblies. We wrote a wrapper class around it, so that all the logging appears with the same sort of configuration from everywhere we call it and also to make sure its a singleton. This wrapper class is called from everywhere within our app.

As a result, the class libraries and the main app all log out to the same log file with the same configuration, which is what we wanted. Is this what you were trying to achieve?

十六岁半 2024-08-17 13:48:18

如何实现您自己的 TraceListener 以便您可以只是在整个应用程序中使用 Trace.Write 吗?

How about implementing your own TraceListener so that you can just use Trace.Write throughout your application?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文