关于不可知的语言:在OOP上下文中正确登录

关于不可知的语言:在OOP上下文中正确登录

Proper Logging in OOP context

自从我开始学习面向对象的编程以来,这就是我一直在苦苦挣扎的问题:一个人应该如何用"适当的" OOP代码实现记录器?

我的意思是,一个对象具有一种方法,我们希望代码中的所有其他对象都可以访问; 此方法将输出到console / file / every,我们将使用它进行日志记录-因此,此对象将是logger对象。

我们不想将记录器对象建立为全局变量,因为全局变量不好,对吧? 但是我们也不想让在每个对象中调用的每个方法的参数中都通过logger对象。

在大学里,当我提起这件事给教授时,他实际上无法给我答案。 我意识到实际上有一些软件包(例如Java)可以实现此功能。 不过,我最终要寻找的是如何正确地并以OOP方式自己实现这一点的知识。


您确实希望将记录器建立为全局变量,因为全局变量还不错。至少,它们并不是天生的坏。记录器是正确使用全局可访问对象的一个??很好的例子。如果需要更多信息,请阅读有关Singleton设计模式的信息。


有一些经过深思熟虑的解决方案。其中一些涉及绕过OO,并使用另一种机制(AOP)。

日志记录并不能很好地适合OO(没关系,并非所有事情都可以)。如果您必须自己实现它,建议您在每个类的顶部实例化" Log":

private final log = new Log(this);

然后您所有的日志记录调用都是微不足道的:log.print(" Hey");

这比单例更易于使用。

让记录器确定您要传入的类,然后使用该类来注释日志。由于您有了日志实例,因此可以执行以下操作:

log.addTag(" Bill");

日志可以将标签帐单添加到每个条目,以便您可以对显示进行更好的过滤。

log4j和电锯是一种完美的即用型解决方案-如果您不只是在学习,请使用它们。


全球可访问的记录器是测试的难题。如果需要"集中式"日志记录工具,请在程序启动时将其创建,然后将其注入需要日志记录的类/方法中。
您如何测试使用类似以下内容的方法:

1
2
3
4
public class MyLogger
{
    public static void Log(String Message) {}
}

如何用模拟代替它?

更好:

1
2
3
4
5
6
7
8
9
public interface ILog
{
    void Log(String message);
}

public class MyLog : ILog
{
    public void Log(String message) {}
}

我一直使用Singleton模式来实现日志记录对象。


将记录器创建为单例类,然后使用静态方法访问它。


我认为您应该为此使用AOP(面向方面??的编程),而不是OOP。


我认为,在实践中,单例/全局方法很好用。最好全局对象只是一个框架,您可以将不同的侦听器(观察者模式)连接到该框架,例如一种用于控制台输出,一种用于数据库输出,一种用于Windows EventLog输出,等等。

不过要提防过度设计,我发现实际上只有一个带有全局方法的单个类可以很好地工作。

或者,您可以使用您提供的特定框架的基础结构。


您可以看一下Singleton模式。


我全都支持AOP和log4 *。这确实帮助了我们。
例如,谷歌给我这篇文章。您可以尝试在该主题上进行更多搜索。


为避免全局变量,我建议创建一个全局注册表,并在此注册您的全局变量。

对于日志记录,我更喜欢提供单例类或提供一些静态日志记录方法的类。

实际上,我将使用现有的日志记录框架之一。


(IMHO)"记录"的发生方式不是解决方案设计的一部分,它更是运行于任何环境(例如Java中的System和Calendar)的一部分。

您的"好"解决方案是尽可能松散地耦合到任何特定日志记录实现的解决方案,因此请考虑接口。我将在此处查看有关Sun如何解决该问题的示例,因为他们可能提出了一个非常好的设计,并为您提供了学习的全部机会!


使用静态类,它具有最少的开销,并且可以在简单的程序集引用中从所有项目类型访问

请注意,单例是等效的,但涉及不必要的分配

如果您使用多个应用程序域,请注意,可能需要一个代理对象才能从主域以外的域访问静态类。

另外,如果您有多个线程,则可能需要锁定日志记录功能以避免交错输出

仅IMHO日志记录是不够的,这就是为什么我写CALM的原因

祝好运!


也许以透明的方式插入Logging宁愿属于"面向方面的编程"习惯用法。但是我们在这里谈论OO设计...

我认为Singleton模式可能是最有用的:您可以通过LoggingService类的公共静态方法从任何上下文访问Logging服务。

尽管这看起来很像一个全局变量,但事实并非如此:它被正确地封装在singleton类中,并且不是每个人都可以访问它。这使您即使在运行时也可以更改日志记录的处理方式,但可以保护日志记录免受"恶意"代码的影响。

在我工作的系统中,我们创建了多个Logging'singletons',以便能够区分来自不同子系统的消息。这些可以在运行时打开/关闭,可以定义过滤器,可以写入文件...命名。


过去,我通过将日志记录类的实例添加到需要访问日志记录的类的基类(或接口(如果语言支持)的话)来解决此问题。当您记录某些内容时,记录器会查看当前的调用堆栈,并从中确定调用代码,设置有关记录语句的适当元数据(源方法,代码行(如果可用),记录的类等)。多个类具有记录器,并且不需要使用可以自动确定的元数据来专门配置记录器。

这确实增加了相当大的开销,因此对于生产日志记录而言,它不一定是明智的选择,但是如果您以这种方式进行设计,则可以有条件地禁用记录器的各个方面。

实际上,我大部分时间都使用commons-logging(我在Java中做了很多工作),但是我在上面描述的设计中有一些方面是有益的。使用健壮的日志系统(其他人已经花费大量时间进行调试)的好处超过了对可以被视为更简洁设计的需求(显然这是主观的,尤其是鉴于本文中没有详细说明)。

我遇到了静态记录器的问题,导致了permgen内存问题(至少,我认为这就是问题所在),所以我可能很快会再讨论记录器。


另一种可能的解决方案是拥有一个Log类,该类封装了日志记录/存储过程。这样,您可以在需要时仅实例化new Log();,而不必使用单例。

这是我的首选解决方案,因为如果要通过数据库进行日志记录,则只需注入数据库。如果您正在使用文件,则无需注入任何依赖项。您也可以完全避免使用全局或静态日志记录类/函数。


来自Microsoft的Pattern&Practices组的企业库日志记录应用程序块是在OOP环境中实现日志记录框架的一个很好的例子。他们拥有一些有关如何实现其日志记录应用程序块的出色文档,所有源代码均可供您自己查看或修改。

还有其他类似的实现:log4net,log4j,log4cxx

他们实现企业库日志记录应用程序块的方式是拥有一个静态Logger类,该类具有许多实际执行日志操作的方法。如果您正在研究模式,那么这可能是Singleton模式的更好用途之一。


推荐阅读