Hm, I am not sure if there are any good resources regarding logging standards and practices, but my experience has lead me to believe:
Logging is not a business function or feature, it is a diagnostic tool. Therefore, we should not expose or define logging features as part of our business contracts (ie interfaces).
From our framework's perspective, we cannot enforce logging in the components we run, and from our component's perspective, we cannot enforce reporting to be enabled, so defining it as part of our interface is somewhat moot.
Again, logging is not something we can enforce, but if we wish to encourage logging, our framework should expose a logging api.
Consider authoring our own ILog interface, such as
// 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);
}
This thin abstraction affords fine control of implementation while minimizing impact to existing consumers. This is advantageous in that the consumer (our plug-in) does not care how logging is implemented, only that there is something to log to.
- Again, logging is not something we can enforce, but if a plug-in wishes to consume a logging service, it should be able to obtain it from a readily-available source.
One solution may be to expose a public static singleton, such as
// 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;
}
}
A consumer would use it as such
public class AwesomeLoggingPlugin : IWatcherPluginBase
{
private static readonly ILog _log =
LogProvider.GetLogger<AwesomeLoggingPlugin> ();
public AwesomeLoggingPlugin ()
{
_log.Debug ("Instantiated.");
}
}
The primary advantage to this approach is that it is easy to use and readily accessbile. A disadvantage is that our plug-in is now tightly-coupled and dependent on this static class, and for purists this may be an issue. Given logging is a passive activity this may not matter.
To sate the purists however, we could also inject an instance of ILog to our plugin. Such as,
public class AnotherAwesomeLoggingPlugin : IWatcherPluginBase
{
private readonly ILog _log = null;
public AnotherAwesomeLoggingPlugin (ILog log)
{
_log = log;
_log.Debug ("Instantiated.");
}
}