使用共享程序集时如何配置 log4net?
我有多个依赖于公共组件的组件。每个组件都存在于自己的组件中。所有组件都使用 log4net 进行日志记录。所有组件都加载到单个进程空间中,但组件的使用顺序会有所不同。所有外部组件在第一次使用时都会加载它们各自的 log4net 配置,以尝试将日志数据发送到它们。公共组件不加载任何配置。此外,还有一个遗留组件不使用通用组件。它还会在首次使用时对外显示并加载其配置。我不能直接接触这个遗留组件的代码或配置。
我面临的问题是,由于组件在首次使用时加载其配置,最后加载配置的人获胜。这会导致日志记录发生,但所有日志输出最终都会进入最后加载的配置。显然,查看您的日志文件并将一些其他组件的日志条目写入其中只是很难看。
我通过使用 RepositoryAttribute 和 AliasRepositoryAttribute 找到了部分解决方案。这让外部组件将其配置加载到它们的“记录器存储库”中,并有效地将它们彼此隔离。遗留组件现在很高兴地写入自己的日志,而我的组件没有噪音,我的组件很高兴地写入自己的日志,而不会在其他日志中产生噪音。
我说的是部分解决方案,因为仍然存在记录公共组件的情况。使用 AliasRepositoryAttribute 时,加载的第一个组件并为公共组件设置别名会获取所有日志输出,即使它是调用公共组件的另一个组件。这很糟糕,因为我会在后面的组件中丢失重要的日志记录信息,并且我将在第一个日志中包含不相关的日志记录信息。
以下代码演示了该问题:
通用:(代替通用组件)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using log4net.Config;
[assembly: Repository("CommonLib")]
namespace CommonLib
{
public class CommonClass
{
static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public void DoCommon(string from)
{
Log.Debug("DoCommon:" + from);
}
}
}
库 A:(代表其中一个外接组件)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using CommonLib;
using log4net;
using log4net.Config;
[assembly: Repository("ALib")]
[assembly: AliasRepository("CommonLib")]
namespace ALib
{
public static class LogPrep
{
static bool _loaded = false;
public static void Ensure()
{
if (_loaded)
return;
var doc = new XmlDocument();
doc.LoadXml(
@"<log4net xsi:noNamespaceSchemaLocation='http://csharptest.net/downloads/schema/log4net.xsd'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
debug='true'>
<appender name='ConsoleAppender' type='log4net.Appender.ColoredConsoleAppender'>
<mapping>
<level value='DEBUG' />
<foreColor value='White' />
<backColor value='Green' />
</mapping>
<layout type='log4net.Layout.PatternLayout'>
<conversionPattern value='ALib: %d{yyyy MMM dd HH:mm:ss} [%p] %c{1} %mdc - %m%n' />
</layout>
</appender>
<root>
<level value='DEBUG' />
<appender-ref ref='ConsoleAppender' />
</root>
</log4net>");
XmlConfigurator.Configure(doc.DocumentElement);
_loaded = true;
}
}
public class AClass
{
static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public void DoA()
{
LogPrep.Ensure();
Log.Debug("DoA");
var common = new CommonClass();
common.DoCommon("A");
}
}
}
库 B:(代替另一个外接组件)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using CommonLib;
using log4net;
using log4net.Config;
[assembly: Repository("BLib")]
[assembly: AliasRepository("CommonLib")]
namespace BLib
{
public class BClass
{
static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static class LogPrep
{
static bool _loaded = false;
public static void Ensure()
{
if (_loaded)
return;
var doc = new XmlDocument();
doc.LoadXml(
@"<log4net xsi:noNamespaceSchemaLocation='http://csharptest.net/downloads/schema/log4net.xsd'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
debug='true'>
<appender name='ConsoleAppender' type='log4net.Appender.ColoredConsoleAppender'>
<mapping>
<level value='DEBUG' />
<foreColor value='White' />
<backColor value='Red' />
</mapping>
<layout type='log4net.Layout.PatternLayout'>
<conversionPattern value='BLib: %d{yyyy MMM dd HH:mm:ss} [%p] %c{1} %mdc - %m%n' />
</layout>
</appender>
<root>
<level value='DEBUG' />
<appender-ref ref='ConsoleAppender' />
</root>
</log4net>");
XmlConfigurator.Configure(doc.DocumentElement);
_loaded = true;
}
}
public void DoB()
{
LogPrep.Ensure();
Log.Debug("DoB");
var common = new CommonClass();
common.DoCommon("B");
}
}
}
可执行容器:(代替遗留组件)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using ALib;
using BLib;
using log4net;
using log4net.Config;
namespace TestLog4NetRepositories
{
class Program
{
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
// Set up a simple configuration that logs on the console.
var doc = new XmlDocument();
doc.LoadXml(
@"<log4net xsi:noNamespaceSchemaLocation='http://csharptest.net/downloads/schema/log4net.xsd'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
debug='true'>
<appender name='ConsoleAppender' type='log4net.Appender.ColoredConsoleAppender'>
<mapping>
<level value='DEBUG' />
<foreColor value='White' />
<backColor value='Blue' />
</mapping>
<layout type='log4net.Layout.PatternLayout'>
<conversionPattern value='Main: %d{yyyy MMM dd HH:mm:ss} [%p] %c{1} %mdc - %m%n' />
</layout>
</appender>
<root>
<level value='DEBUG' />
<appender-ref ref='ConsoleAppender' />
</root>
</log4net>");
XmlConfigurator.Configure(doc.DocumentElement);
Log.Info("Entering application.");
var a = new AClass();
a.DoA();
var b = new BClass();
b.DoB();
Log.Info("Exiting application.");
}
}
}
如果您运行此代码,请注意 Common 的输出以绿色打印出来,而从未以红色打印出来。
log4net: Creating repository [BLib] using type [log4net.Repository.Hierarchy.Hierarchy]
log4net:ERROR Failed to alias repository [CommonLib] System.InvalidOperationException: Repository [CommonLib] is already aliased to repository [ALib]. Aliases cannot be redefined.
at log4net.Core.DefaultRepositorySelector.AliasRepository(String repositoryAlias, ILoggerRepository repositoryTarget)
at log4net.Core.DefaultRepositorySelector.LoadAliases(Assembly assembly, ILoggerRepository repository)
虽然上面的可执行容器显示 A 在 B 之前,但在实际情况下,B 可以在 A 之前。
正如我之前提到的,我有更复杂的层次结构,但上面展示了我面临的最小问题。