繁体   English   中英

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

[英]How do you configure and enable log4net for a stand-alone class library assembly?

背景

我正在用 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 配置,它都会记录日志?

您可能可以围绕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() 问题。

解决方案1

第一组约束的解决方案基本上是将log4net.LogManager包装到您自己的自定义 LogManager 类中,如JacobJeroenMcWafflestix所建议的(参见下面的代码)。

不幸的是, 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 configuration file location [assembly: log4net.Config.Repository("CompanyName.IntegrationLibName")] [assembly: log4net.Config.XmlConfigurator(ConfigFile = "CompanyName.IntegrationLibName.config", Watch = true)]

参考

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

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

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

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

你可以在这里找到一个很好的描述: 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("Error occurred while running myMethod", ex)记录错误以及异常详细信息。

我发现的是以下内容:

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

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

我希望这有帮助。

这适用于我的共享库

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM