【问题标题】:Proper Logging in OOP context在 OOP 上下文中正确记录
【发布时间】:2008-09-17 19:23:07
【问题描述】:

自从我第一次开始学习面向对象编程以来,我一直在努力解决一个问题:应该如何在“正确的”OOP 代码中实现记录器?

我的意思是一个对象,它具有我们希望代码中的每个其他对象都能够访问的方法;此方法将输出到控制台/文件/任何内容,我们将使用它进行日志记录——因此,此对象将是记录器对象。

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

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

【问题讨论】:

    标签: language-agnostic oop logging


    【解决方案1】:

    确实想将记录器设置为全局变量,因为全局变量并不不好。至少,它们本质上并不坏。记录器是正确使用全局可访问对象的一个​​很好的例子。如果您想了解更多信息,请阅读单例设计模式。

    【讨论】:

    • 我自己说得再好不过了。
    【解决方案2】:

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

    日志记录并不能很好地适应 OO(这没关系,但并非所有事情都如此)。如果你必须自己实现它,我建议只在每个类的顶部实例化“Log”:

    private final log=new Log(this);

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

    这使得它比单例更容易使用。

    让您的记录器找出您传入的类并使用它来注释日志。既然你有了一个日志实例,你就可以做如下事情:

    log.addTag("账单");

    并且log可以为每个条目添加标签bill,这样你就可以为你的显示实现更好的过滤。

    log4j 和电锯是一个完美的开箱即用解决方案 - 如果您不只是学术,请使用它们。

    【讨论】:

    • 通过这样做,您将无法在测试相关对象时模拟记录器。这意味着,例如,如果记录器正在记录到第三方服务,则每次测试该对象时,您都会在某处获得此日志行。我认为在这种情况下,将记录器存储在容器中会是一个更好的主意,因为您始终可以将此对象的容器(单例)实例替换为虚拟记录器。
    • @Bruno 在 Log4J 中,我们为测试提供了不同的配置文件,例如在测试期间它可以转到给定日志的文本文件或控制台,但在生产中它可能转到数据库。我还将记录器传递给构造函数以使测试更容易。单例也可以工作,但如果它变大就很难模拟/替换测试(实际上,这只是复制了 log4j 提供的配置文件的工作)。最好的选择是 Spring,但根据您的情况,这可能是矫枉过正。
    【解决方案3】:

    全局可访问的记录器是测试的痛苦。如果您需要“集中式”日志记录工具,请在程序启动时创建它并将其注入需要日志记录的类/方法中。 你如何测试使用这样的方法:

    public class MyLogger 
    {
        public static void Log(String Message) {}
    }
    

    如何用 mock 替换它?

    更好:

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

    【讨论】:

      【解决方案4】:

      我一直使用单例模式来实现日志对象。

      【讨论】:

        【解决方案5】:

        你可以看看单例模式。

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              【解决方案8】:

              在我看来,在实践中,单例/全局方法可以正常工作。最好全局事物只是一个框架,您可以将不同的侦听器连接到该框架(观察者模式),例如1个控制台输出,1个数据库输出,1个Windows EventLog输出等

              不过要注意过度设计,我发现在实践中,只有全局方法的单个类可以很好地工作。

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

              【讨论】:

                【解决方案9】:

                来自微软模式和实践组的Enterprise Library Logging Application Block 是在 OOP 环境中实现日志框架的一个很好的例子。他们有一些很棒的文档来说明他们是如何实现日志应用程序块的,并且所有源代码都可供您自己查看或修改。

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

                他们实现企业库日志记录应用程序块的方式是拥有一个静态Logger 类,其中包含许多实际执行日志操作的不同方法。如果您正在查看模式,这可能是单例模式的更好用途之一。

                【讨论】:

                  【解决方案10】:

                  我完全支持 AOP 和 log4*。这真的帮助了我们。 例如,谷歌给了我this article。您可以尝试搜索更多关于该主题的内容。

                  【讨论】:

                    【解决方案11】:

                    (恕我直言)“记录”如何发生并不是您的解决方案设计的一部分,它更多地是您碰巧在其中运行的任何环境的一部分 - 例如 Java 中的系统和日历。

                    您的“好”解决方案是与任何特定日志记录实现尽可能松散耦合的解决方案,因此请考虑接口。我会查看here 的线索,了解 Sun 是如何解决这个问题的,因为他们可能想出了一个非常好的设计,并将其全部展示给你学习!

                    【讨论】:

                      【解决方案12】:

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

                      注意,Singleton 是等价的,但涉及不必要的分配

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

                      如果你有多个线程,你可能需要锁定日志功能以避免交错输出

                      恕我直言,单独记录是不够的,这就是我写CALM的原因

                      祝你好运!

                      【讨论】:

                        【解决方案13】:

                        也许以透明的方式插入 Logging 更适合 Aspect Oriented Programming 习惯用法。但我们在这里谈论的是面向对象设计......

                        在我看来,单例模式可能是最有用的:您可以通过 LoggingService 类的公共静态方法从任何上下文访问 Logging 服务。

                        虽然这看起来很像一个全局变量,但实际上并非如此:它被正确地封装在单例类中,并不是每个人都可以访问它。这使您即使在运行时也可以更改日志记录的处理方式,但可以保护日志记录的工作不受“vilain”代码的影响。

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

                        【讨论】:

                          【解决方案14】:

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

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

                          实际上,我大部分时间都使用公共日志记录(我在 java 中做了很多工作),但我发现上面描述的设计的某些方面是有益的。拥有一个其他人已经花费大量时间调试的强大日志系统的好处超过了对可以被认为是更清洁的设计的需求(这显然是主观的,特别是考虑到本文缺乏细节)。

                          我遇到了导致永久内存问题的静态记录器问题(至少,我认为这就是问题所在),所以我可能很快会重新访问记录器。

                          【讨论】:

                            【解决方案15】:

                            为了避免全局变量,我建议创建一个全局 REGISTRY 并在那里注册你的全局变量。

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

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

                            【讨论】:

                              【解决方案16】:

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

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

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 2015-01-18
                                • 1970-01-01
                                • 2017-07-06
                                • 2015-11-11
                                • 2011-02-22
                                • 2020-04-30
                                • 1970-01-01
                                • 1970-01-01
                                相关资源
                                最近更新 更多