将 NLog 与 MEF 结合使用的最佳方式是什么?

发布于 2024-09-19 06:51:13 字数 275 浏览 0 评论 0原文

我想知道将 NLog 与托管可扩展性框架 (MEF) 结合使用的最佳方法是什么?

我有一个支持使用 MEF 架构的插件的应用程序(导入和导出等) 我想向我的应用程序添加日志记录功能。 作为一个日志组件,我想使用 NLog。

你会推荐什么? 1. 为 NLog 创建一个包装器,即配置 NLog 并导出其他插件导入的 void Log(string level, string message) 等函数的附加插件 2. 每个插件都应该配置和使用它自己的 NLog 实例。 (实际上它们都会写入同一个文件)。

I am wondering what is the best way to use NLog with Managed Extensibility Framework (MEF)?

I have an application that support plugins using MEF architecture (Import and Exports etc)
I want to add logging capability to my application.
As a logging component I want to use NLog.

What would you recommend?
1. Create a wrapper for NLog, i.e. additional plugin that configures NLog and exports functions like void Log(string level, string message) that other plugins importing
2. Every plugin should have it is own instance of NLog configured and used. (They all would write to the same file actually).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

甜`诱少女 2024-09-26 06:51:14

这是一种有趣的方法,但是,它似乎存在这样的缺点:所有注入的记录器(或注入的一个单例)将是相同的实例(或将具有相同的名称,该名称是该记录器的名称)这意味着您无法非常轻松地控制日志记录的粒度(即,在一个类中将日志记录设置为“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 记录器,在本例中为 NLogLoggingService。因此,要在 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");
    }
  }
}

现在 MyClass1 和 MyClass2 的日志记录是单独可控的。您可以为每个类别配置不同的级别,将它们发送到不同的目标,或者完全关闭其中一个或两个。或者,由于 log4net 和 NLog 中记录器层次结构的概念,您可以通过为命名空间(本例中为 MyNamespace)或任何“祖先”命名空间配置“记录器”来同时控制两个类中的日志记录。如果没有为完全限定的类型名配置记录器,那么日志记录框架实际上会通过将名称视为点分隔字符串并删除最后一个块并检查是否配置了该记录器来向上移动层次结构。因此,在本例中,我们请求 MyNamespace.MyClass1 和 MyNamespace.MyClass2 的记录器。我可以将 app.config 文件配置为在“info”处记录 MyNamespace 并写入文件目标(log4net-speak 中的附加程序)。如果我这样做,那么我通过完全限定名称请求的两个记录器都将继承 MyNamespace 配置。

使用建议的通过 MEF 注入 NLog 的方法,您将只有一个记录器实例,因此您无法将每个类配置为以不同方式记录。另外,正如我之前提到的,如果您选择记录调用站点信息,您将始终获得该类的“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 的静态 LogManager 包装在 ILogManager 中并注入它。这将偏离正常的“注入 ILogger”范例。

总结一下:如果您以这种方式通过 MEF 注入 NLog,您确实可以使用 NLog 进行日志记录,但您将只有一个命名的记录器 (SoapBox.Core.NLogLoggingService)。这意味着您将无法以任何程度的粒度进行控制 - 无论是级别/开/关还是输出(NLog Target/log4net Appender),

对于如何通过注入 NLog 而言,我没有一个好的答案MEF 并保持“原始”NLog 为您提供的粒度/灵活性。

我可以说我们已经决定使用 Common.Logging for .NET 来抽象日志框架,但我们决定不要注入日志记录。相反,我们将仅使用静态 LogManager(由 Common.Logging 提供)来分发记录器。

This is an interesting approach, however, it seems to suffer from the drawback that all loggers that are injected (or the one singleton that is injected) will be the same instance (or will have the same name, the name being the name of the NLogLoggingService class. That means that you cannot very easily control the granularity of logging (i.e. turn logging to "Info" level in one class and "Warn" in another class). Also, if you opt to use the call site formatting tokens, you will always get the call site of the call the the NLog logger rather than the call site in your application code.

Here is an abbreviated version of the logger that was linked:

  [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; 
      } 
    } 
  }

In the constructor LogManager.GetCurrentClassLogger() is used to get the NLog logger. GetCurrentClassLogger will return a NLog logger that is "named" based on the "current" type, which, in this case, is NLogLoggingService. So, to configure NLog in the app.config file, you will configure based on the that the logger is named "SoapBox.Core.NLogLoggingService". Commonly, in code that uses NLog (or log4net) directly, each class gets its own uniquely named logger like this:

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

Now the logging for MyClass1 and MyClass2 is individually controllable. You can configure different levels for each class, send them to different targets, or turn one or both off altogether. Alternatively, due to the concept of logger hierarchies in both log4net and NLog, you could control the logging in both class simultaneously by configuring a "logger" for the namespace (MyNamespace in this case), or any "ancestor" namespace. If there is not a logger configured for the fully qualified typename, then the logging framework essentially moves up the hierarchy by considering the name a dot delimited string and removing the last chunk and checking to see if that logger is configured. So, in this case, we are requesting loggers for MyNamespace.MyClass1 and MyNamespace.MyClass2. I could configure the app.config file to have MyNamespace log at the "info" and write to a file target (appender in log4net-speak). If I did that, then both loggers that I requested via their fully qualified names would inherit the MyNamespace configuration.

With the suggested way of injecting NLog via MEF, you will only have one logger instance, so you cannot configure each class to log differently. Also, as I mentioned earlier, if you opt to log call site information, you will always get "SoapBox.Core.NLogLoggingService" for the class and "Debug" (or DebugWithFormat, or Info, or InfoWithFormat, etc) for the method.

This seems to be an issue with successfully injecting loggers from log4net and NLog. You can see the question that I asked about this very issue a couple of months ago.

Ultimately I was able to figure out how some dependency injection frameworks can successfully inject log4net and NLog loggers that are specific to the class being created (i.e. if the DI framework is instantiating MyClass, which in turn depends on an ILogger interface, then MyClass will get a logger that is essentially equivalent to what would have happened if MyClass requested the logger itself via the LogManager.GetCurrentClassLogger api). Generally "resolvers" in DI/IoC frameworks are given the current context (containing, among other information, the type of the object currently being created). With that type available, it becomes a simple matter of having a logging framework-specific resolver receive that type and pass it along to the logging framework to create a logger appropriate for that type.

In order to get the most out of NLog's (and log4net's) capabilities you would really like to be able to tell MEF that your class is dependendent on "ILogger", but also that the instance of "ILogger" that gets injected into your class should depend on the Type of your class.

I don't know how easy it will be to achieve that with MEF. Alternatively, you could wrap NLog's static LogManager in a ILogManager and inject that. That would deviate from the normal "inject ILogger" paradigm.

To summarize: If you inject NLog via MEF this way, you will indeed be able to log with NLog, but you will only ever have one named logger (SoapBox.Core.NLogLoggingService). This means that you will not be able control with any degree of granularity - either for levels/on/off or for output (NLog Target/log4net Appender)

I don't have a good answer for what to do as far as injecting NLog via MEF AND keeping the granularity/flexibility that "raw" NLog gives you.

I can say that we have decided to use Common.Logging for .NET to abstract the logging framework but we decided NOT to inject logging. Instead, we will just use a static LogManager (as provided by Common.Logging) to hand out loggers.

小梨窩很甜 2024-09-26 06:51:14

我认为选项 1 更好。

您可以查看开源框架 SoapBox Core 如何使用 MEF 导入对 ILoggingService 的引用。它还提供基于 NLog 的日志记录服务的默认实现,但您可以轻松地将其替换为 log4Net。

供参考:

是 LGPL,因此您可以在应用程序中使用(这部分)。

I think Option 1 is better.

You can take a look at how the open source framework SoapBox Core imports a reference to an ILoggingService using MEF. It also provides a default implementation of the logging service based on NLog, but you could easily swap it out for log4Net, for example.

For reference:

SoapBox Core is LGPL'd, so you might be able to use (this part) in your application.

坏尐絯℡ 2024-09-26 06:51:14

我已经和这个问题作斗争有一段时间了。

真正重要的是日志文件中的调用站点(FullyQualified Namespace)。

首先,我尝试从 Stacktrace 中获取正确的记录器:

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static NLog.Logger GetLogger()
    {
        var stackTrace = new StackTrace(false);
        StackFrame[] frames = stackTrace.GetFrames();
        if (null == frames) throw new ArgumentException("Stack frame array is null.");
        StackFrame stackFrame;
        switch (frames.Length)
        {
            case 0:
                throw new ArgumentException("Length of stack frames is 0.");
            case 1:
            case 2:
                stackFrame = frames[frames.Length - 1];
                break;
            default:
                stackFrame = stackTrace.GetFrame(2);
                break;
        }

        Type declaringType = stackFrame.GetMethod()
                                       .DeclaringType;

        return declaringType == null ? LogManager.GetCurrentClassLogger() :                 LogManager.GetLogger(declaringType.FullName);
    }

但遗憾的是,MEF 的 Stacktrace 非常长,我无法清楚地识别 ILogger 请求者的正确调用者。

因此,我没有通过构造函数注入注入 ILogger 接口,而是创建了一个 ILogFactory 接口,它可以通过构造函数注入注入,然后调用工厂上的 Create 方法

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

希望这有帮助

I have been fighting with this problem a while now.

Really improtant was the Callsite (FullyQualified Namespace) within the logfiles.

First, i tryed to get the right logger out of the Stacktrace:

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static NLog.Logger GetLogger()
    {
        var stackTrace = new StackTrace(false);
        StackFrame[] frames = stackTrace.GetFrames();
        if (null == frames) throw new ArgumentException("Stack frame array is null.");
        StackFrame stackFrame;
        switch (frames.Length)
        {
            case 0:
                throw new ArgumentException("Length of stack frames is 0.");
            case 1:
            case 2:
                stackFrame = frames[frames.Length - 1];
                break;
            default:
                stackFrame = stackTrace.GetFrame(2);
                break;
        }

        Type declaringType = stackFrame.GetMethod()
                                       .DeclaringType;

        return declaringType == null ? LogManager.GetCurrentClassLogger() :                 LogManager.GetLogger(declaringType.FullName);
    }

But sadly, the Stacktrace with MEF is very long and i cannot clearly identify the correct caller for the Requester of the ILogger.

So, instead of injecting the ILogger Interface via Constructor Injection, i have created a ILogFactory Interface, that can get injected via Constructor Injection and call then the Create Method on the Factory

    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
    }

And implemented it:

    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
    }

With the 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
    }

and Implementation of:

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

To use it... just inject the ILogFactory and calle the Create Method in a Mefed Importing Constructor:

      [ImportingConstructor]
      public MyConstructor(          
        ILogFactory logFactory)
       {
        _logger = logFactory.Create(GetType());
        }

hope this helps

最冷一天 2024-09-26 06:51:14

如果您创建一个新的 ExportProvider 并将传入的 ImportDefinition 转换为 ICompositionElement。您可以获得记录器被注入的类型。

这是 ExportProvider

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

它的设置方式使其可以与任何日志记录框架一起使用,因为您需要传入一个将返回 ILogger 的函数(Ilogger 是我们自己的,您必须创建自己的接口或只是使其特定于 Nlog)。传递给函数的字符串也是要注入的类型的完整类名。 (compositionElement.Origin.DisplayName)

使用此引导 MEF 的示例如下所示:

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

上面的代码是从单元测试复制的,因此我只是添加特定的程序集而不是解析目录。 MockLogger 是 ILogger 接口的实现,它将日志记录类名称(或注入类型)作为其构造函数的参数。

这不需要解析任何堆栈跟踪并直接从 MEF 中提取信息。

If you create a new ExportProvider and cast the ImportDefinition being passed in to a ICompositionElement. You can get the type that the logger is being injected into.

Here is the ExportProvider

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

This is setup in such a way that it will work with any logging framework as you need to pass in a function that will return an ILogger (the Ilogger is our own, you'll have to create your own interface or just make it specific to Nlog). The string being passed to the function is the full class name that the type is being injected too. (compositionElement.Origin.DisplayName)

An example of bootstrapping MEF with this would look like this:

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

The code above was copied from a unit test, so I'm just add specific assemblies instead of parsing a directory. The MockLogger is an implementation of the ILogger interface that takes the logging class name (or injecting type) as a parameter to it's constructor.

This doesn't require parsing any stack traces and pulls the information that is otherwise sitting there directly out of MEF.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文