如何为独立的类库程序集配置和启用 log4net?

发布于 2024-07-25 08:34:44 字数 692 浏览 4 评论 0 原文

背景

我正在 C# .NET 3.5 中编写一个类库程序集,用于与其他应用程序集成,包括第三方商业现成 (COTS) 工具。 因此,有时这个类库会被我控制的应用程序(EXE)调用,而有时它会被我控制的其他DLL或应用程序调用。

假设

  • 我使用的是 C# 3.0、.NET 3.5 SP1 和 Visual Studio 2008 SP1
  • 我使用的是 log4net 1.2.10.0 或更高

版本 限制

任何解决方案都必须:

  • 允许类库通过其自己的配置文件启用和配置日志记录(如果调用)应用程序未配置 log4net。
  • 允许类库通过调用应用程序配置启用和配置日志记录,

如果指定了 log4net 信息,则

  • 或者允许类库始终使用其自己的配置文件启用和配置日志记录。

问题

当我的独立类库由我无法控制的 DLL 或应用程序(例如第三方 COTS 工具)调用并且未指定 log4net 配置信息时,我的类库无法执行任何操作记录。


问题

如何为独立类库程序集配置和启用 log4net,以便无论调用应用程序是否提供 log4net 配置它都会记录日志?

Background

I am writing a class library assembly in C# .NET 3.5 which is used for integration with other applications including third-party Commercial-Off-The-Shelf (COTS) tools. Therefore, sometimes this class library will be called by applications (EXEs) that I control while other times it will be called by other DLLs or applications that I do not control.

Assumptions

  • I am using C# 3.0, .NET 3.5 SP1, and Visual Studio 2008 SP1
  • I am using log4net 1.2.10.0 or greater

Constraints

Any solution must:

  • Allow for the class library to enable and configure logging via it's own configuration file, if the calling application does not configure log4net.
  • Allow for the class library to enable and configuring logging via the calling applications configuration, if it specifies log4net information

OR

  • Allow for the class library to enable and configuring logging using it's own configuration file at all times.

Problem

When my stand-alone class library is called by a DLL or application that I do not control (such as a third-party COTS tool) and which doesn't specify log4net configuration information, my class library is unable to do any of it's logging.


Question

How do you configure and enable log4net for a stand-alone class library assembly so that it will log regardless if the calling application provides log4net configuration?

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

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

发布评论

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

评论(6

风向决定发型 2024-08-01 08:34:45

您可以在这里找到一个很好的描述:
log4net:快速入门指南

该文章描述了,要分别在每个程序集之前配置它,请为名为 AssemblyName.dll.log4net 的程序集创建一个 XML 文件,并将以下 XML 代码放入其中:

<?xml version="1.0" encoding="utf-8"?>
<log4net debug="false">
   <appender name="XmlSchemaFileAppender" type="log4net.Appender.FileAppender">
      <file value="AppLog.xml" />
      <appendToFile value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.XmlLayout" />
   </appender>

   <root>
      <level value="WARN" />
      <appender-ref ref="XmlSchemaFileAppender" />
   </root>
</log4net>

它进一步描述了实例化新记录器,只需将其声明为整个类的变量,如下所示:

public class LogSample
{
    private static readonly log4net.ILog Log 
            = log4net.LogManager.GetLogger(typeof(LogSample));
    // Class Methods Go Here...
}

然后您可以在类中使用私有变量Log,例如:

Log.Info("Sample message");

同样,您可以使用Log.Error(“运行时发生错误” myMethod", ex) 来记录错误以及异常详细信息。

我发现的是以下内容:

  • 不要忘记调用 log4net.Config.XmlConfigurator.Configure(); 来激活您的配置

  • 如果您需要知道写入文件的路径,这里一些代码如何从L​​og4Net获取它

我希望这有帮助。

You can find a good description here:
log4net: A quick start guide

As the article describes, to configure it fore each assembly separately, create an XML file for your assembly named AssemblyName.dll.log4net and place the following XML code into it:

<?xml version="1.0" encoding="utf-8"?>
<log4net debug="false">
   <appender name="XmlSchemaFileAppender" type="log4net.Appender.FileAppender">
      <file value="AppLog.xml" />
      <appendToFile value="true" />
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
      <layout type="log4net.Layout.XmlLayout" />
   </appender>

   <root>
      <level value="WARN" />
      <appender-ref ref="XmlSchemaFileAppender" />
   </root>
</log4net>

It further describes, to instantiate a new logger, simply declare it as a variable for the entire class as follows:

public class LogSample
{
    private static readonly log4net.ILog Log 
            = log4net.LogManager.GetLogger(typeof(LogSample));
    // Class Methods Go Here...
}

You can then use the private variable Log inside your class like:

Log.Info("Sample message");

Likewise you can use Log.Error("Error occurred while running myMethod", ex) to log errors along with the exception details.

What I found is the following:

  • Don't forget to call log4net.Config.XmlConfigurator.Configure(); to activate your configuration

  • If you need to know the path of the file(s) written, here some code how to obtain it from Log4Net

I hope this helps.

笑梦风尘 2024-08-01 08:34:45

这对我来说适用于共享库

protected static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.Assembly.GetExecutingAssembly().GetType());

This works for me for a shared library

protected static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.Assembly.GetExecutingAssembly().GetType());
故人的歌 2024-08-01 08:34:44

解决方案 1

第一组约束的解决方案基本上是包装 log4net。 LogManager 到您自己的自定义 LogManager 类中,例如 雅各布Jeroen,和 McWafflestix 已建议(请参阅下面的代码)。

不幸的是,log4net.LogManager 类是静态的,C# 不支持静态继承,因此您不能简单地继承它并重写 GetLogger 方法。
log4net.LogManager 类,所以这当然是可能的。

此解决方案的另一个缺点是,如果您有现有的代码库(在我的例子中就是这样做的),您将必须用包装类替换对 log4net.LogManager 的所有现有调用。 然而对于今天的重构工具来说这没什么大不了的。

对于我的项目,这些缺点超过了使用调用应用程序提供的日志配置的好处,因此,我选择了解决方案 2。

代码

首先,您需要一个 LogManager 包装类:

using System;
using System.IO;
using log4net;
using log4net.Config;

namespace MyApplication.Logging
{
    //// TODO: Implement the additional GetLogger method signatures and log4net.LogManager methods that are not seen below.
    public static class LogManagerWrapper
    {
        private static readonly string LOG_CONFIG_FILE= @"path\to\log4net.config";

        public static ILog GetLogger(Type type)
        {
            // If no loggers have been created, load our own.
            if(LogManager.GetCurrentLoggers().Length == 0)
            {
                LoadConfig();
            }
            return LogManager.GetLogger(type);
        }

        private void LoadConfig()
        {
           //// TODO: Do exception handling for File access issues and supply sane defaults if it's unavailable.   
           XmlConfigurator.ConfigureAndWatch(new FileInfo(LOG_CONFIG_FILE));
        }              
}

然后在您的类中,而不是:

private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

使用:

private static readonly ILog log = LogManagerWrapper.GetLogger(typeof(MyApp));

方案 2

解决 根据我的目的,我决定采用满足第二组约束的解决方案。 请参阅下面的代码了解我的解决方案。

来自 Apache log4net 文档

“程序集可以选择使用命名的日志存储库而不是默认存储库,这将程序集的日志记录与应用程序的其余部分完全分开,这对于希望为其组件使用 log4net 但又不想要求所有这些的组件开发人员非常有用。使用其组件的应用程序知道 log4net。这也意味着它们的调试配置与应用程序配置分开。程序集应指定 RepositoryAttribute 来设置其日志记录存储库。”

代码

我将以下几行放在了我的类库的 AssemblyInfo.cs 文件:

//Log4Net配置文件位置 
  [程序集:log4net.Config.Repository(“CompanyName.IntegrationLibName”)] 
  [程序集:log4net.Config.XmlConfigurator(ConfigFile =“CompanyName.IntegrationLibName.config”,Watch = true)] 
  

     

参考文献

Solution 1

A solution for the first set of constraints is to basically wrap the log4net.LogManager into your own custom LogManager class like Jacob, Jeroen, and McWafflestix have suggested (see code below).

Unfortunately, the log4net.LogManager class is static and C# doesn't support static inheritance, so you couldn't simply inherit from it and override the GetLogger method.
There aren't too many methods in the log4net.LogManager class however, so this is certainly a possibility.

The other drawback to this solution is that if you have an existing codebase (which I do in my case) you would have to replace all existing calls to log4net.LogManager with your wrapper class. Not a big deal with today's refactoring tools however.

For my project, these drawbacks outweighed the benefits of using a logging configuration supplied by the calling application so, I went with Solution 2.

Code

First, you need a LogManager wrapper class:

using System;
using System.IO;
using log4net;
using log4net.Config;

namespace MyApplication.Logging
{
    //// TODO: Implement the additional GetLogger method signatures and log4net.LogManager methods that are not seen below.
    public static class LogManagerWrapper
    {
        private static readonly string LOG_CONFIG_FILE= @"path\to\log4net.config";

        public static ILog GetLogger(Type type)
        {
            // If no loggers have been created, load our own.
            if(LogManager.GetCurrentLoggers().Length == 0)
            {
                LoadConfig();
            }
            return LogManager.GetLogger(type);
        }

        private void LoadConfig()
        {
           //// TODO: Do exception handling for File access issues and supply sane defaults if it's unavailable.   
           XmlConfigurator.ConfigureAndWatch(new FileInfo(LOG_CONFIG_FILE));
        }              
}

Then in your classes, instead of:

private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

Use:

private static readonly ILog log = LogManagerWrapper.GetLogger(typeof(MyApp));

Solution 2

For my purposes, I have decided to settle on a solution that meets the second set of constraints. See the code below for my solution.

From the Apache log4net document:

"An assembly may choose to utilize a named logging repository rather than the default repository. This completely separates the logging for the assembly from the rest of the application. This can be very useful to component developers that wish to use log4net for their components but do not want to require that all the applications that use their component are aware of log4net. It also means that their debugging configuration is separated from the applications configuration. The assembly should specify the RepositoryAttribute to set its logging repository."

Code

I placed the following lines in the AssemblyInfo.cs file of my class library:

// Log4Net configuration file location
[assembly: log4net.Config.Repository("CompanyName.IntegrationLibName")]
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "CompanyName.IntegrationLibName.config", Watch = true)]

    

References

人间不值得 2024-08-01 08:34:44

您可以围绕 XmlConfigurator 类编写一些代码:

public static class MyLogManager
{
    // for illustration, you should configure this somewhere else...
    private static string configFile = @"path\to\log4net.config";

    public static ILog GetLogger(Type type)
    {
        if(log4net.LogManager.GetCurrentLoggers().Length == 0)
        {
            // load logger config with XmlConfigurator
            log4net.Config.XmlConfigurator.Configure(configFile);
        }
        return LogManager.GetLogger(type);
    }
}

然后在你的类中,而不是:

private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

使用:

private static readonly ILog log = MyLogManager.GetLogger(typeof(MyApp));

当然,最好让这个类成为一个服务并使用你选择的 IoC 容器动态配置它,但是你明白了吗?

编辑:修复了评论中指出的 Count() 问题。

You can probably code something around the XmlConfigurator class:

public static class MyLogManager
{
    // for illustration, you should configure this somewhere else...
    private static string configFile = @"path\to\log4net.config";

    public static ILog GetLogger(Type type)
    {
        if(log4net.LogManager.GetCurrentLoggers().Length == 0)
        {
            // load logger config with XmlConfigurator
            log4net.Config.XmlConfigurator.Configure(configFile);
        }
        return LogManager.GetLogger(type);
    }
}

Then in your classes, instead of:

private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

Use:

private static readonly ILog log = MyLogManager.GetLogger(typeof(MyApp));

Of course, it would be preferable to make this class a service and dynamically configure it with the IoC container of your choice, but you get the idea?

EDIT: Fixed Count() problem pointed out in comments.

情感失落者 2024-08-01 08:34:44

在代码中,您可以通过检查是否有任何记录器,

log4net.LogManager.GetCurrentLoggers().Count()

然后您可以使用 XmlConfigurator 从文件加载默认配置:

log4net.Config.XmlConfigurator.Configure(configFile)

您可以在静态或常规构造函数中进行初始化。

class Sample
{
    private static readonly log4net.ILog LOG;

    static Sample()
    {
        if (log4net.LogManager.GetCurrentLoggers().Count() == 0)
        {
            loadConfig();
        }
        LOG = log4net.LogManager.GetLogger(typeof(Sample));

    }

    private static void loadConfig()
    {
        /* Load your config file here */
    }

    public void YourMethod()
    {
       LOG.Info("Your messages");
    }
}

In your code you can check if there are any loggers via

log4net.LogManager.GetCurrentLoggers().Count()

You could then for example use an XmlConfigurator to load a default configuration from a file:

log4net.Config.XmlConfigurator.Configure(configFile)

You could do the initialization in a static or regular constructor.

class Sample
{
    private static readonly log4net.ILog LOG;

    static Sample()
    {
        if (log4net.LogManager.GetCurrentLoggers().Count() == 0)
        {
            loadConfig();
        }
        LOG = log4net.LogManager.GetLogger(typeof(Sample));

    }

    private static void loadConfig()
    {
        /* Load your config file here */
    }

    public void YourMethod()
    {
       LOG.Info("Your messages");
    }
}
静谧 2024-08-01 08:34:44

在独立类库中,有一个使用 log4net.Config.XmlConfigurator 加载 log4net 配置文件的单例。

具体来说,您可以定义所有代码以使用您自己的自定义日志记录类; 这个类可以只是 log4net 日志记录调用的简单包装,但有一个补充; 创建一个静态成员,其中包含您要记录的日志信息; 通过调用该类的静态构造函数中的 XmlConfigurator 来初始化它。 这就是你所要做的。

In your standalone class library, have a singleton which loads the log4net configuration file using the log4net.Config.XmlConfigurator.

Specifically, you can define all of your code to use your own custom logging class; this class can just be a simple wrapper of the log4net logging calls, with one addition; make a static member which contains the log information you want to log to; initialize that with a call to the XmlConfigurator in the static constructor for that class. That's all you have to do.

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