将logging仪作为单件使用是一种好的做法吗?
我有一个习惯将logging器传递给构造函数,如:
public class OrderService : IOrderService { public OrderService(ILogger logger) { } }
但是这很烦人,所以我用了一段时间的这个属性:
private ILogger logger = NullLogger.Instance; public ILogger Logger { get { return logger; } set { logger = value; } }
这也越来越讨厌 – 这不是干燥的,我需要在每个class都重复这个。 我可以使用基类,但是再次 – 我使用Form类,所以需要FormBase,等等。所以我认为,与ILogger暴露单身人士会有什么缺点,所以很想知道在哪里得到logging器:
Infrastructure.Logger.Info("blabla");
更新:正如Merlyn正确注意到的,我应该提到,在第一个和第二个例子中,我使用DI。
这也越来越讨厌 – 这不是干的
确实如此。 但是,只有这样,你才能做到这一点。 你必须到处使用logging器,所以你必须拥有这些types的属性。
所以让我们看看我们可以做些什么。
独生子
单身人士是可怕的<flame-suit-on>
。
我build议坚持与财产注射,你已经做了你的第二个例子。 这是最好的因子,你可以做,而不诉诸于魔术。 有一个明确的依赖关系比通过单例隐藏更好。
但是,如果单身人士为你节省大量时间,包括所有你需要做的重构(水晶球时间!),我想你可能能够和他们一起生活。 如果有一个单身人士的用途,这可能是它。 请记住,如果你想改变主意,成本将会高到一定程度。
如果你这样做,请使用Registry
模式 (请参阅说明)查看其他人的答案,以及注册(可重置)单例工厂而不是单例logging器实例的答案。
还有其他的替代scheme可以起到很好的作用,所以你应该首先检查一下。
Visual Studio代码片段
您可以使用Visual Studio代码片段来加速重复代码的入口。 你将能够input类似logger
标签 ,代码将神奇地出现给你。
使用AOPclosures
您可以使用像PostSharp这样的面向方面编程(AOP)框架来自动生成一些属性注入代码。
完成后可能看起来像这样:
[InjectedLogger] public ILogger Logger { get; set; }
您也可以使用他们的方法跟踪示例代码来自动跟踪方法的入口和出口代码,这可能不需要将一些logging器属性一起添加。 您可以在类级别或命名空间范围内应用该属性:
[Trace] public class MyClass { // ... } // or #if DEBUG [assembly: Trace( AttributeTargetTypes = "MyNamespace.*", AttributeTargetTypeAttributes = MulticastAttributes.Public, AttributeTargetMemberAttributes = MulticastAttributes.Public )] #endif
我在我的dependency injection容器中放置了一个logging器实例,然后将这个logging器注入需要的类中。
好问题。 我相信在大多数项目logging是一个单身人士。
我脑海中浮现出一些想法:
- 使用ServiceLocator (或者其他的dependency injection容器,如果你已经使用了任何的),它允许你跨服务/类共享logging器,这样你可以实例化logging器甚至多个不同的logging器,并通过ServiceLocator共享,显然这将是一个单例,某种反转控制 。 这种方法为logging器实例化和初始化过程提供了很大的灵活性。
- 如果你几乎需要logging器 – 实现
Object
types的扩展方法,所以每个类都可以调用logging器的方法,如LogInfo()
,LogDebug()
,LogError()
单身是个好主意。 更好的办法是使用registry模式,这对实例化有了更多的控制。 在我看来,单例模式太接近全局variables。 使用registry处理对象创build或重用,将来可能会对实例化规则进行更改。
registry本身可以是一个静态类,以提供简单的语法来访问日志:
Registry.Logger.Info("blabla");
一个普通的单身人士不是一个好主意。 这使得很难更换logging器。 我倾向于为我的logging器使用filter(一些“嘈杂”类可能只logging警告/错误)。
我使用singleton模式结合logging器工厂的代理模式:
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<T>() { return new SystemDebugLogger(); } }
这允许我创build一个FilteringLogFactory
或者只是一个SimpleFileLogFactory
而不用改变任何代码(因此遵守Open / Closed原则)。
示例扩展
public class FilteredLogFactory : LogFactory { public override ILogger GetLogger<T>() { if (typeof(ITextParser).IsAssignableFrom(typeof(T))) return new FilteredLogger(typeof(T)); return new FileLogger(@"C:\Logs\MyApp.log"); } }
并使用新工厂
// and to use the new log factory (somewhere early in the application): LogFactory.Assign(new FilteredLogFactory());
在你的class级,应该logging:
public class MyUserService : IUserService { ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>(); public void SomeMethod() { _logger.Debug("Welcome world!"); } }
在.NET中有一本书dependency injection。 基于你需要你应该使用拦截。
在本书中,有一个图表帮助决定是否使用构造函数注入,属性注入,方法注入,环境上下文,拦截。
这就是使用这个图表的一个原因:
- 你有依赖或需要吗? – 需要它
- 这是交叉的关切吗? – 是的
- 你需要从中得到答案吗? – 没有
使用拦截
如果你想看看一个很好的解决scheme,我build议你看看谷歌应用程序引擎与python日志就像import logging
,然后你可以只是logging.debug("my message")
或logging.info("my message")
,它确实保持它应该如此简单。
Java没有一个很好的解决scheme,即log4j应该避免,因为它实际上迫使你使用单身,这里回答是“可怕的”,我有可怕的经验试图使日志logging输出相同的日志语句只有一次当我怀疑双重日志logging的原因是我在同一个虚拟机(!)中的两个类加载器中有一个日志logging对象的单身人士
我请求你原谅C#没有那么具体,但是从我看到的使用C#的解决scheme看起来类似于Java,我们有了log4j,我们也应该把它变成一个单例。
这就是为什么我非常喜欢GAE / python的解决scheme ,它非常简单,您不用担心类加载器,因此可以获得双重日志logging或任何devise模式。
我希望这些信息可以与你有关,我希望你想看看我的日志logging解决scheme,我build议,而不是我欺负多less问题单身人士怀疑由于不可能有一个真正的单身人士时它必须是在几个类加载器instanciating。
我个人觉得最简单的另一个解决scheme是使用静态Logger类。 你可以从任何类的方法调用它,而不必改变类,例如添加属性注入等。它非常简单和易于使用。
Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed
鉴于我现在使用通用logging器(typesT的ILogger) – 答案是否定的)