Dependency injection 将NLog与MEF结合使用的最佳方式是什么?
我想知道将NLog与托管可扩展性框架(MEF)结合使用的最佳方式是什么 我有一个应用程序支持使用MEF架构的插件(导入和导出等) 我想将日志功能添加到我的应用程序中。 作为日志组件,我想使用NLog 你推荐什么? 1.为NLog创建一个包装器,即配置NLog并导出其他插件导入的void Log(字符串级别,字符串消息)等函数的附加插件Dependency injection 将NLog与MEF结合使用的最佳方式是什么?,dependency-injection,inversion-of-control,mef,nlog,Dependency Injection,Inversion Of Control,Mef,Nlog,我想知道将NLog与托管可扩展性框架(MEF)结合使用的最佳方式是什么 我有一个应用程序支持使用MEF架构的插件(导入和导出等) 我想将日志功能添加到我的应用程序中。 作为日志组件,我想使用NLog 你推荐什么? 1.为NLog创建一个包装器,即配置NLog并导出其他插件导入的void Log(字符串级别,字符串消息)等函数的附加插件 2.每个插件都应该配置和使用自己的NLog实例。(实际上它们都会写入同一个文件)。我认为选项1更好 您可以看看开源框架如何使用MEF导入对ILogingServi
2.每个插件都应该配置和使用自己的NLog实例。(实际上它们都会写入同一个文件)。我认为选项1更好 您可以看看开源框架如何使用MEF导入对ILogingService的引用。它还提供了基于NLog的日志服务的默认实现,但您可以轻松地将其替换为log4Net 供参考:
- 接口
- 封装NLog并使用MEF导出自身的
SoapBox核心是LGPL的,因此您可以在应用程序中使用(这部分)。这是一种有趣的方法,但是,它似乎有一个缺点,即所有注入的记录器(或注入的一个单例)都是同一个实例(或将具有相同的名称,名称为NLogLoggingService类的名称。这意味着您无法很容易地控制日志记录的粒度(即,在一个类中将日志记录转换为“Info”级别,在另一个类中将日志记录转换为“Warn”)。此外,如果您选择使用呼叫站点格式化令牌,您将始终从NLog记录器中获取呼叫的呼叫站点,而不是应用程序代码中的呼叫站点 以下是链接的记录器的缩写版本:
[Export(Services.Logging.LoggingService, typeof(ILoggingService))]
class NLogLoggingService : ILoggingService
{
Logger log; public NLogLoggingService()
{
log = LogManager.GetCurrentClassLogger();
}
public void Debug(object message)
{
log.Debug(message);
}
public void DebugWithFormat(string format, params object[] args)
{
if (args.Length == 0)
{
log.Debug(format);
}
else
{
Debug(string.Format(format, args));
}
}
public bool IsDebugEnabled
{
get
{
return log.IsDebugEnabled;
}
}
}
在构造函数LogManager中。GetCurrentClassLogger()
用于获取NLog记录器。GetCurrentClassLogger将返回一个基于“当前”类型“命名”的NLog记录器,在本例中,该类型为NLOGlogginService。因此,要在app.config文件中配置NLog,您将根据记录器命名的类型进行配置“SoapBox.Core.NLogLoggingService”。通常,在直接使用NLog(或log4net)的代码中,每个类都有自己唯一命名的记录器,如下所示:
namespace MyNamespace
{
public class MyClass1
{
private static readonly Logger logger LogManager.GetCurrentClassLogger();
public void DoSomeWork()
{
logger.Info("Logging from inside MyClass1.DoSomeWork");
}
}
public class MyClass2
{
private static readonly Logger logger LogManager.GetCurrentClassLogger();
public void DoSomeWork()
{
logger.Info("Logging from inside MyClass2.DoSomeWork");
}
}
}
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);
}
}
现在,MyClass1和MyClass2的日志记录是可以单独控制的。您可以为每个类配置不同的级别,将它们发送到不同的目标,或者同时关闭其中一个或两个。或者,由于log4net和NLog中的日志记录层次结构的概念,您可以通过configuri同时控制这两个类中的日志记录为名称空间(在本例中为MyNamespace)或任何“祖先”定义一个“记录器”"命名空间。如果没有为完全限定的typename配置记录器,那么日志框架实际上会将名称视为点分隔字符串,并删除最后一个区块,然后检查记录器是否已配置。因此,在本例中,我们请求MyNamespace.MyClass1和MyNam的记录器我可以将app.config文件配置为使MyNamespace日志位于“info”并写入文件目标(log4net中的appender speak)。如果我这样做,那么我通过其完全限定名请求的两个记录器都将继承MyNamespace配置
使用建议的通过MEF注入NLog的方法,您将只有一个logger实例,因此您无法将每个类配置为不同的日志记录方式。此外,正如我前面提到的,如果您选择记录调用站点信息,您将始终获得该类的“SoapBox.Core.NLogLoggingService”和“Debug”(或DebugWithFormat、或Info、或InfoWithFormat等)用于该方法
这似乎是成功从log4net和NLog注入记录器的一个问题。您可以看到几个月前我就这个问题提出的问题
最终,我能够理解一些依赖注入框架如何成功注入特定于所创建类的log4net和NLog记录器(即,如果DI框架正在实例化MyClass,而MyClass又依赖于ILogger接口,那么MyClass将获得一个记录器,该记录器与MyClass通过LogManager.GetCurrentClassLogger api请求记录器本身时所发生的情况基本相同)。通常,DI/IoC框架中的“解析器”都提供了当前上下文(除其他信息外,还包含当前正在创建的对象的类型)。有了该类型,让特定于日志框架的解析器接收该类型并将其传递给日志框架以创建适合该类型的记录器就变得简单了
为了充分利用NLog(和log4net)的功能,您确实希望能够告诉MEF您的类依赖于“ILogger”,但注入到类中的“ILogger”实例应该取决于您的类的类型
我不知道用MEF实现这一点有多容易。或者,您可以将NLog的静态日志管理器包装在ILogManager中并注入它。这将偏离正常的“注入ILogger”范式
总而言之:如果您通过MEF以这种方式注入NLog,您确实能够使用NLog进行日志记录,但您将只有一个命名的记录器(SoapBox.Core.nloglogginService)。这意味着您将无法控制任何粒度-无论是级别/开/关还是输出(NLog目标/log4net Appender)
至于如何通过MEF注入NLog并保持“原始”NLog提供的粒度/灵活性,我没有一个好的答案
我可以说,我们已经决定使用抽象日志框架,但我们决定不注入日志。相反,我们将只使用静态日志管理器(由Common.logging提供)分发日志记录器。我一直在解决这个问题
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
}
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;
}
[ImportingConstructor]
public MyConstructor(
ILogFactory logFactory)
{
_logger = logFactory.Create(GetType());
}
public class LoggerExportProvider : ExportProvider
{
private readonly ExportDefinition _loggerExportDefinition;
private readonly Func<string, ILogger> _loggerFactory;
/// <summary>
/// Initializes a new instance of the <see cref="LoggerExportProvider"/> class.
/// </summary>
/// <param name="loggerFactory">The logger factory function.</param>
public LoggerExportProvider(Func<string, ILogger> loggerFactory)
{
_loggerFactory = loggerFactory;
_loggerExportDefinition = new ExportDefinition(typeof (ILogger).FullName, new Dictionary<string, object> {{"ExportTypeIdentity", typeof (ILogger).FullName}});
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
{
IList<Export> exports = new List<Export>();
var compositionElement = definition as ICompositionElement;
if (compositionElement == null || compositionElement.Origin == null)
return exports;
var constraint = definition.Constraint.Compile();
if (constraint(_loggerExportDefinition))
exports.Add(new Export(_loggerExportDefinition, () => _loggerFactory(compositionElement.Origin.DisplayName)));
return exports;
}
}
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);
}
}