[英]Is using an event for logging a good idea?
我正在研究能够加载各种(自制)插件的程序。
这些插件需要有一种方法来向主机程序发送通知。
目前我有一个接口作为插件的基础。 接口定义了每当我需要记录某些内容时触发的事件
界面如下所示:
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);
您知道其他(可能更好)的方法来实现日志记录吗?
您可以考虑在命名空间System.Diagnostics
( Debug
, Debugger
, Trace
, EventLog
)中使用内置类。
这样,您可以将标准调试和跟踪输出重定向到VS控制台(调试时)或文件(运行发布版本时)。
是的 - 我会看到以这种方式使用事件作为一个好主意 - 替代方案(使用您最喜欢的日志框架)会将某人绑定到日志框架中,而实际上他们可能已经使用了自己的日志框架。
在System.Diagnostics
名称空间( Debug
, Trace
等...)中使用内置类将是一个更轻量级的替代方案,但是这些日志目标不如其他功能更全面的日志框架(如log4net)灵活性更低,并且还要求在编译时定义一个标志(分别为Trace或Debug) - 这些方法是否合适取决于您预期这些消息的使用方式。
我建议的唯一改进是将事件分成插件引发的不同类型的事件/消息,而不是只有一个通用事件:
public interface IWatcherPluginBase
{
event EventHandler<GenericMessageEventArgs> GenericMessage;
event EventHandler<PluginStartingEventArgs> PluginStarting;
event EventHandler<PluginStoppingEventArgs> PluginStopping;
// etc...
(另外,您可能会发现将插件库定义为抽象基类而不是接口是有帮助的 - 这样您就可以提供基本实现,但这是一个不同的讨论)
没有看到使用事件的问题。 另一个解决方案是在Pluginhost上实现一个接口,该接口包含从插件中与它交互的方法,例如:
public class MyApplication: IPluginHost
{
}
其中IPluginHost有方法
void writeLog( .... );
然后在Plugin-interface中添加
IPluginHost HostApplication { get; }
并在加载插件时分配主机。
然后插件就可以了
HostApplication.WriteLog( ....)
这样,您就可以创建某种类型的API,以允许插件在主机上执行各种操作。
(在我写作的时候把这个从头脑中拿出来,所以代码可能无法正常工作,但希望你能得到我的观点:))
嗯,我不确定在采伐标准和做法方面是否有任何好的资源,但我的经验让我相信:
记录不是业务功能或功能,它是一种诊断工具。 因此,我们不应将日志记录功能公开或定义为业务合同(即接口)的一部分。
从我们的框架的角度来看,我们无法强制执行我们运行的组件的日志记录,并且从我们的组件的角度来看,我们无法强制执行报告,因此将其定义为我们的界面的一部分有点没有实际意义。
同样,日志记录不是我们可以强制执行的,但如果我们希望鼓励日志记录,我们的框架应该公开日志记录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.");
}
}
如果您正在开发插件系统,您可能会对使用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的共享实例。 它有点像单身人士,只有没有静态依赖。 由于依赖项是由框架注入的,因此可以为您的单元测试注入测试依赖项,因为您可能希望禁用日志记录到磁盘以进行测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.