public interface ILogFactory
{
#region Public Methods and Operators
/// <summary>
/// Creates a logger with the Callsite of the given Type
/// </summary>
/// <example>
/// factory.Create(GetType());
/// </example>
/// <param name="type">The type.</param>
/// <returns></returns>
ILogger Create(Type type);
#endregion
}
并实施了它:
using System;
using System.ComponentModel.Composition;
[Export(typeof(ILogFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class LogFactory : ILogFactory
{
#region Public Methods and Operators
public ILogger Create(Type type)
{
var logger = new Logger().CreateLogger(type);
return logger;
}
#endregion
}
使用ILogger:
public interface ILogger
{
#region Public Properties
bool IsDebugEnabled { get; }
bool IsErrorEnabled { get; }
bool IsFatalEnabled { get; }
bool IsInfoEnabled { get; }
bool IsTraceEnabled { get; }
bool IsWarnEnabled { get; }
#endregion
#region Public Methods and Operators
void Debug(Exception exception);
void Debug(string format, params object[] args);
void Debug(Exception exception, string format, params object[] args);
void Error(Exception exception);
void Error(string format, params object[] args);
void Error(Exception exception, string format, params object[] args);
void Fatal(Exception exception);
void Fatal(string format, params object[] args);
void Fatal(Exception exception, string format, params object[] args);
void Info(Exception exception);
void Info(string format, params object[] args);
void Info(Exception exception, string format, params object[] args);
void Trace(Exception exception);
void Trace(string format, params object[] args);
void Trace(Exception exception, string format, params object[] args);
void Warn(Exception exception);
void Warn(string format, params object[] args);
void Warn(Exception exception, string format, params object[] args);
#endregion
}
和实施:
using System;
using NLog;
using NLog.Config;
/// <summary>
/// The logging service.
/// </summary>
public class Logger : NLog.Logger, ILogger
{
#region Fields
private string _loggerName;
#endregion
#region Public Methods and Operators
/// <summary>
/// The get logging service.
/// </summary>
/// <returns>
/// The <see cref="ILogger" />.
/// </returns>
public ILogger CreateLogger(Type type)
{
if (type == null) throw new ArgumentNullException("type");
_loggerName = type.FullName;
var logger = (ILogger)LogManager.GetLogger(_loggerName, typeof(Logger));
return logger;
}
要使用它...只需注入ILogFactory并在Mefed导入构造函数中调用Create方法:
[ImportingConstructor]
public MyConstructor(
ILogFactory logFactory)
{
_logger = logFactory.Create(GetType());
}
public class Example
{
[Import]
public ILogger Logger { get; set;}
public Example()
{
var aggregatecatalogue = new AggregateCatalog();
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(typeof (ILogger).Assembly));
aggregatecatalogue.Catalogs.Add(new AssemblyCatalog(GetType().Assembly));
var container = new CompositionContainer(aggregatecatalogue, new LoggerExportProvider(s => new MockLogger(s)));
container.ComposeParts(this);
}
}
4 回答
这是一个有趣的方法,然而,它似乎有一个缺点,即注入的所有 Logger (或注入的一个单独的)将是相同的实例(或将具有相同的名称,名称是NLogLoggingService类 . 这意味着你不能很容易地控制日志记录的粒度(即在一个类中将日志记录转换为“Info”级别,在另一个类中转换为“Warn”) . 另外,如果您选择使用调用站点格式化令牌,那么将始终将呼叫的呼叫站点作为NLog Logger 而不是应用程序代码中的呼叫站点 .
以下是链接的 Logger 的缩写版本:
在构造函数中
LogManager.GetCurrentClassLogger()
用于获取NLog Logger . GetCurrentClassLogger将返回一个基于"current"类型的"named"的NLog Logger ,在这种情况下,它是NLogLoggingService . 因此,要在app.config文件中配置NLog,您将根据 Logger 的名称"SoapBox.Core.NLogLoggingService"进行配置 . 通常,在直接使用NLog(或log4net)的代码中,每个类都有自己的唯一命名 Logger ,如下所示:现在,MyClass1和MyClass2的日志记录是可单独控制的 . 您可以为每个类配置不同的级别,将它们发送到不同的目标,或者完全关闭一个或两个级别 . 或者,由于log4net和NLog中 Logger 层次结构的概念,您可以通过为命名空间(本例中为MyNamespace)或任何“祖先”命名空间配置“ Logger ”来同时控制两个类中的记录 . 如果没有为完全限定的类型名配置 Logger ,则日志记录框架实质上是通过将名称设置为点分隔字符串并删除最后一个块并检查是否已配置该 Logger 来向上移动层次结构 . 因此,在这种情况下,我们要求MyNamespace.MyClass1和MyNamespace.MyClass2的 Logger . 我可以配置app.config文件让MyNamespace登录“info”并写入文件目标(log4net-speak中的appender) . 如果我这样做,那么我通过其完全限定名称请求的两个 Logger 都将继承MyNamespace配置 .
使用建议的通过MEF注入NLog的方法,您将只有一个 Logger 实例,因此您不能将每个类配置为以不同方式记录 . 此外,正如我之前提到的,如果您选择记录调用站点信息,您将始终获得该类的“SoapBox.Core.NLogLoggingService”和该方法的“Debug”(或DebugWithFormat,或Info或InfoWithFormat等) .
这似乎是从log4net和NLog成功注入 Logger 的问题 . 你可以看到几个月前我问过这个问题的question .
最后,我能够弄清楚一些依赖注入框架如何成功注入特定于正在创建的类的log4net和NLog Logger (即,如果DI框架实例化MyClass,而MyClass又依赖于ILogger接口,那么MyClass将获得一个 Logger ,它基本上等同于MyClass通过LogManager.GetCurrentClassLogger api请求 Logger 本身时发生的 Logger . 通常,DI / IoC框架中的“解析器”被赋予当前上下文(包含当前正在创建的对象的类型等信息) . 有了这种类型,一个简单的问题就是让特定于日志框架的解析器接收该类型并将其传递给日志框架以创建适合该类型的 Logger .
为了充分利用NLog(和log4net)的功能,你真的希望能够告诉MEF你的类依赖于“ILogger”,而且注入你的类的“ILogger”实例也应该取决于你 class 的类型 .
我不知道它会有多容易用MEF实现这一目标 . 或者,您可以将NLog的静态LogManager包装在ILogManager中并注入它 . 这将偏离正常的“注入ILogger”范式 .
总结一下:如果以这种方式通过MEF注入NLog,您确实可以使用NLog进行日志记录,但是您只能拥有一个命名的 Logger (SoapBox.Core.NLogLoggingService) . 这意味着您将无法以任何粒度控制 - 无论是级别/开/关还是输出(NLog Target / log4net Appender)
就通过MEF注入NLog并保持“原始”NLog为您提供的粒度/灵活性而言,我没有一个好的答案 .
我可以说我们决定使用Common.Logging for .NET来抽象日志框架,但我们决定不注入日志记录 . 相反,我们将使用静态LogManager(由Common.Logging提供)来分发 Logger .
我认为选项1更好 .
您可以看一下开源框架SoapBox Core如何使用MEF导入对ILoggingService的引用 . 它还提供了基于NLog的日志服务的默认实现,但您可以轻松地将其替换为log4Net .
以供参考:
ILoggingService界面
Logging Service包装NLog并使用MEF导出自己
SoapBox Core是LGPL,因此您可以在应用程序中使用(此部分) .
我一直在与这个问题作斗争 .
真正重要的是日志文件中的Callsite(FullyQualified Namespace) .
首先,我尝试从Stacktrace中获取正确的 Logger :
但遗憾的是,带有MEF的Stacktrace非常长,我无法清楚地识别出ILogger请求者的正确调用者 .
因此,我没有通过构造函数注入注入ILogger接口,而是创建了一个ILogFactory接口,可以通过构造函数注入注入,然后调用工厂上的Create方法
并实施了它:
使用ILogger:
和实施:
要使用它...只需注入ILogFactory并在Mefed导入构造函数中调用Create方法:
希望这可以帮助
如果您创建一个新的ExportProvider并将ImportDefinition转换为ICompositionElement . 您可以获取 Logger 注入的类型 .
这是ExportProvider
这样设置就可以使用任何日志记录框架,因为你需要传递一个返回ILogger的函数(Ilogger是我们自己的,你必须创建自己的接口或者只是让它特定于NLOG) . 传递给函数的字符串是正在注入类型的完整类名 . (
compositionElement.Origin.DisplayName
)使用此引导MEF的示例如下所示:
上面的代码是从单元测试中复制的,所以我只是添加特定的程序集而不是解析目录 . MockLogger是ILogger接口的一个实现,它将日志记录类名称(或注入类型)作为参数提供给它的构造函数 .
这不需要解析任何堆栈跟踪并将信息直接从MEF中拉出来 .