I had a habit to pass logger to constructor, like:
public class OrderService : IOrderService {
public OrderService(ILogger logger) {
}
}
A plain singleton is not a good idea. It makes it hard to replace the logger. I tend to use filters for my loggers (some "noisy" classes may only log warnings/errors).
I use singleton pattern combined with the proxy pattern for the logger factory:
public class LogFactory
{
private static LogFactory _instance;
public static void Assign(LogFactory instance)
{
_instance = instance;
}
public static LogFactory Instance
{
get { _instance ?? (_instance = new LogFactory()); }
}
public virtual ILogger GetLogger()
{
return new SystemDebugLogger();
}
}
This allows me to create a FilteringLogFactory or just a SimpleFileLogFactory without changing any code (and therefore complying to Open/Closed principle).
Sample extension
public class FilteredLogFactory : LogFactory
{
public override ILogger GetLogger()
{
if (typeof(ITextParser).IsAssignableFrom(typeof(T)))
return new FilteredLogger(typeof(T));
return new FileLogger(@"C:\Logs\MyApp.log");
}
}
And to use the new factory
// and to use the new log factory (somewhere early in the application):
LogFactory.Assign(new FilteredLogFactory());
In your class that should log:
public class MyUserService : IUserService
{
ILogger _logger = LogFactory.Instance.GetLogger();
public void SomeMethod()
{
_logger.Debug("Welcome world!");
}
}