2010-08-09 55 views
4

我工作的程序,它是能夠加載各種(自制)插件。正在使用事件記錄一個好主意?

這些插件需要有一種方法將通知發送到宿主程序。

目前,我有一個接口作爲插件的基礎。 的接口定義,每當我需要登錄的東西

接口被觸發的事件是這樣的:

public delegate void LogEventHandler(object sender, LogEventArgs e); 
public interface IWatcherPluginBase 
{ 
    event LogEventHandler WriteLog; 

    string Name { get; } 
    string Description { get; } 
    string Contact { get; } 
    Version Version { get; } 
    PluginState Status { get; } 
    string StatusDescription { get; } 
    void Start(); 
    void Stop(); 
} 

在我使用下面的代碼觸發事件

if (WriteLog != null) 
    WriteLog(this, new LogEventArgs("Started", MessageLevel.Info)); 
插件

我的主程序添加了事件處理程序

plugin.WriteLog += new LogEventHandler(plugin_WriteLog); 

你知道其他(可能更好)的方法來實現日誌記錄嗎?

回答

1

嗯,我不知道是否有任何好的資源關於測井標準和做法,但我的經驗讓我相信:

  • 記錄不是業務功能或功能,它是一種診斷工具。因此,我們不應該公開或定義日誌功能作爲我們業務合同(即接口)的一部分。

  • 從我們的框架的角度來看,我們無法在我們運行的組件中實施日誌記錄,並且從組件的角度來看,我們無法強制報告被啓用,因此將其定義爲接口的一部分是有點沒有實際意義的。

  • 再說一次,日誌並不是我們可以強制執行的,但如果我們希望鼓勵日誌記錄,我們的框架應該公開一個日誌API。

考慮編寫我們自己的ILog接口,如

// most of us will recognize this as a thinly veiled 
// log4net subset, extend or reduce to address our 
// framework's requirements 
public interface ILog 
{ 
    bool IsDebugEnabled { get; } 
    void Debug(object message); 
    void Debug(object message, Exception exception); 
    void DebugFormat(string format, params object[] args); 
} 

這本薄薄的抽象得到執行的精細控制,同時最大限度降低對現有的消費者。這是有利的,因爲消費者(我們的插件)不關心如何實現日誌記錄,只有有些東西需要登錄。

  • 此外,日誌並不是我們可以強制執行的事情,但是如果插件希望使用日誌服務,它應該能夠從一個可用的源獲取它。

一種解決方案可以是公開公共靜態單,如

// again, thinly veiled wrapper to log4net. so long as we are able to 
// implement these methods, however, we do not care who the actual 
// provider is 
public static class LogProvider 
{ 
    public static ILog GetLogger<T>() 
    { 
     return GetLoggerByType(typeof(T)); 
    } 
    public static ILog GetLoggerByName(string name) 
    { 
     global::log4net.ILog log4netLogger = 
      global::log4net.LogManager.GetLogger(name); 

     // Log4NetLog is an implementation of our ILog 
     // that accepts a lognet:ILog and delegates to it 
     ILog logger = new Log4NetLog(log4netLogger); 
     return logger; 
    } 
    public static ILog GetLoggerByType(Type type) 
    { 
     global::log4net.ILog log4netLogger = 
      global::log4net.LogManager.GetLogger(type); 
     ILog logger = new Log4NetLog(log4netLogger); 
     return logger; 
    } 
} 

消費者將使用它作爲這樣

public class AwesomeLoggingPlugin : IWatcherPluginBase 
{ 
    private static readonly ILog _log = 
     LogProvider.GetLogger<AwesomeLoggingPlugin>(); 

    public AwesomeLoggingPlugin() 
    { 
     _log.Debug ("Instantiated."); 
    } 
} 

這種方法的主要優點是,它是易於使用和隨時可以訪問。缺點是我們的插件現在緊密耦合並且依賴於這個靜態類,對於純粹主義者來說這可能是一個問題。鑑於日誌記錄是一種被動行爲,這可能無關緊要。然後,我們也可以將注入到我們的插件的實例ILog。如,

public class AnotherAwesomeLoggingPlugin : IWatcherPluginBase 
{ 
    private readonly ILog _log = null; 
    public AnotherAwesomeLoggingPlugin (ILog log) 
    { 
     _log = log; 
     _log.Debug ("Instantiated."); 
    } 
} 
5

你可能會考慮usign命名空間中的System.DiagnosticsDebugDebuggerTraceEventLog)內置類。

通過這種方式,您可以將標準調試和跟蹤輸出重定向到VS控制檯(調試時)或文件(運行版本構建時)。

+0

得看看 – peter 2010-08-09 14:48:10

+0

+1:TraceSource值得一提。 – 2010-08-09 17:36:04

+0

TraceSource看起來很有意思:) – peter 2010-08-09 19:49:28

1

是 - 我會看到這樣一個好主意,利用事件 - 替代(用你喜歡的日誌框架)將配合某人變成日誌框架時,他們可能實際上已經用自己的。

使用內置類在System.Diagnostics名字空間(DebugTrace等)將是一個更輕的替代方案,但這些日誌目標比其他功能更全面的日誌框架彈性較差(如log4net的) ,並且還要求在編譯時定義一個標誌(分別是跟蹤或調試) - 這些方法是否合適取決於您預期如何使用這些消息。

唯一的細化,我建議將您的活動分成不同類型的事件/消息由插件被提出,而不是僅僅有一個一般事件:

public interface IWatcherPluginBase 
{ 
    event EventHandler<GenericMessageEventArgs> GenericMessage; 
    event EventHandler<PluginStartingEventArgs> PluginStarting; 
    event EventHandler<PluginStoppingEventArgs> PluginStopping; 
    // etc... 

(另外,你可能會發現它有幫助你的插件基定義爲抽象基類,而非接口 - 這樣你可以提供的基本實現,但是這是一個不同的討論)

+0

精煉與多個事件聽起來不錯 此刻我不認爲我需要一個摘要類,但感謝您的建議無論如何:) – peter 2010-08-09 14:50:45

1

看不出有問題,使用事件這一點。另一個解決辦法是可能實現的,其保持從該插件與它相互作用的方法Pluginhost的接口,例如:

public class MyApplication: IPluginHost 
{ 

} 

其中IPluginHost具有方法

void writeLog(....); 

然後在Plugin-接口添加

IPluginHost HostApplication { get; } 

並在加載插件時分配主機。然後

插件將能夠做到

HostApplication.WriteLog(....) 

這樣你就可以創建一些種類的API,允許插件在主機上執行各種操作。

(把這個從我的頭,因爲我是寫的,所以代碼可能無法爲是工作,但我希望你明白我的意思:))

0

如果您正在開發插件系統,您可能有興趣使用MEF。使用MEF或IoC框架,您可以定義一個日誌服務接口,並在它們實例化時將日誌服務對象注入到您的插件類中。使用MEF標籤,它看起來是這樣的:

//This would be defined in a Framework assembly that Plugins use 
public interface ILoggingService 
{ 
    //Various logging methods go here, including any overloads, like Debug, Trace, etc. 
} 

//This would be defined in one of your App assemblies that Plugins don't directly reference 
[Export(typeof(ILoggingService))] 
internal class AppLoggingService : ILoggingService 
{ 
    //Implementation of logging for your app 
} 

public class MyPlugin : IWatcherPluginBase 
{ 
    [Import] private ILoggingService _loggingService; 
} 

現在,當你實例化插件,您使用MEF來解決其進口和字段將被自動設置爲您所提供的AppLoggingService的共享實例。它有點像單身,只有沒有靜態依賴。因爲框架正在注入依賴關係,所以這爲您的單元測試開放了注入測試依賴關係的可能性,例如,您可能想要禁用爲測試而記錄到磁盤。

相關問題