如何在使用共享程序集时配置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)
虽然上面的可执行容器在B之前显示A,但在实际情况中,B可以在A之前。
正如我之前提到的,我有更复杂的层次结构,但上面展示了我面临的最小问题。