【问题标题】:Java Logging With Abstract Classes带有抽象类的 Java 日志记录
【发布时间】:2012-08-23 00:52:42
【问题描述】:

我正在做一个项目,目前正在使用 log4j 实现一些日志记录,我很好奇应该如何实现日志。我正在讨论的两个实现如下:

第一个选项

对该类和所有子类使用超类中的单个日志:

public abstract class AbstractFoo {
    protected static Log LOG = LogFactory.getLog(AbstractFoo.class);

    ...
}

public class Foo extends AbstractFoo {
    public void someMethod() {
        LOG.info("Using abstract log");
    }
}

第二个选项

为每个类、超级类和子类使用单独的日志:

public abstract class AbstractFoo {
    private static Log LOG = LogFactory.getLog(AbstractFoo.class);

    ...
}

public class Foo extends AbstractFoo {
    private static Log LOG = LogFactory.getLog(Foo.class);        

    public void someMethod() {
        LOG.info("Using own log");
    }
}

什么更有意义,为什么?

【问题讨论】:

    标签: java inheritance logging abstract-class


    【解决方案1】:

    在抽象类中有一个 Logger 有两个原因(我能想到):

    1. 在抽象类的方法中使用记录器,同时仍将具体类作为调用者注销。
    2. 所有具体类之间的统一日志记录或通用日志记录。

    如果您更喜欢静态记录器(这也是我的首选),那么n1cr4m 的答案很好地解决了#1 和#2。

    但是,如果您对#2 更感兴趣并且不喜欢每个具体类都需要实现getLogger() 的事实,那么您可以执行以下操作。作为示例,我将使用转换器:

    public abstract class AbstractConverter{
    
       protected void logError(Logger logger, String someId, String msg){
           logger.error("Error during conversion of unit \""+ someId + "\": " + msg); 
       }
    
    }
    

    记录器可以是来自具体类的静态记录器。现在,无论何时登录,都会统一打印前缀,这也强制识别它转换的对象。

    这个解决方案的缺点是,如果AbstractConverter 需要在其方法之一中自己使用记录器,它将无法使用具体类中的记录器,除非您也将其设为参数,我强烈劝阻。如果您需要这种功能,请使用n1cr4m 的解决方案。

    【讨论】:

      【解决方案2】:

      这是我的解决方案(最终的静态记录器):

      public abstract class AbstractFoo {
           protected abstract Log getLogger();
           public doSomething() {
                getLogger().info("log something");
           }
      }
      
      public class Foo extends AbstractFoo {
          private static final Log log = Log.getLogger(Foo.class);
      
          protected Log getLogger() {
               return log;
          }
          public doSomethingElse() {
                log.info("log somethingElse");
          }
      }
      

      【讨论】:

        【解决方案3】:

        同样可以通过使用构造函数来实现。在 Base 类级别添加记录器,并使用 super() 从每个 Derived 类设置它。有代码:

        public abstract class AbstractFoo {
        
            protected Log log;  // base abstract class has a Log object.
        
            public AbstractFoo(Log logger) {   // parameterized constructor for logger, to be used by the derived class.
                this.log = logger;
            }
        
            public doSomething() {        // common method for all the derived classes.
              log.info("log something");
            }
            // rest of business logic.
        }
        
        public class Foo extends AbstractFoo {
        
            public Foo(){
                super(LogFactory.getLog(AbstractFoo.class));
            }
        
            public void someMethod() {
                log.info("Using own log");     // this uses its own logger.
            }
        }
        

        【讨论】:

          【解决方案4】:

          我也不会。相反,我会让它在这两种情况下都使用正确的类。

          public abstract class AbstractFoo {
              protected final Log log = LogFactory.getLog(getClass());
          
              ...
          }
          
          public class Foo extends AbstractFoo {
              public void someMethod() {
                  log.info("Using abstract log");
              }
          }
          

          如果您没有进行大量日志记录(无论如何这是个好主意),您可以使用一种方法。

          public abstract class AbstractFoo {
              protected Log log() { return LogFactory.getLog(getClass()); }
          
              ...
          }
          

          如果有一个类经常调用它,你可以重写它来给你一个缓存的实例。

          【讨论】:

          • 到目前为止,我已经看到了两种方法:静态记录器(如问题)和非静态记录器(如您的示例)。静态记录器不是更好的解决方案(每个实例一个记录器实例)吗?
          • 静态记录器更好,如果它们对所有实例都相同。在抽象类的情况下,实例的类并不完全相同。
          • 我喜欢这个,这似乎是结合两个选项的好方法。您最终得到一个日志,但它绑定到正确的类。 +1
          • @Emilio 我会称log() 为getter,因为它所做的只是获取一个值。你会用什么来代替protected
          • @Emilio 使用log 作为字段的问题是您必须向每个子类添加代码才能使用适当的记录器。虽然,如果您实际上没有记录日志,即日志记录已关闭,则每次调用 esp 使用字段的效率要高得多。
          【解决方案5】:

          如果您在抽象类中创建记录器,则所有日志都会被标记为源自 AbstractFoo。如果您希望/需要查看带有发生日志的子类标记的日志,请为子类创建记录器。

          【讨论】:

          • “如果您在抽象类中创建记录器,则所有日志都将标记为源自 AbstractFoo”-> 不,如果您使用 @Peter_Lawrey 接受的答案,则不正确。然后,您总是会得到带有类标记的日志。
          • Lawrey 回答的“问题”是它们现在是基于实例的记录器,这不是主意。
          • @MeBigFatGuy 拥有基于实例的记录器很糟糕吗?
          • 嗯,你是在浪费内存和时间来分配这些东西,并且无缘无故地制造更多的垃圾。除此之外,生活中肯定还有更多令人发指的问题,但这有点愚蠢。
          【解决方案6】:

          两者都有道理。这取决于您的应用程序。

          我认为更常用的做法是为每个班级设置私人记录器。这允许您为每个类和每个包配置日志记录。请记住,AbstractFooFoo 可能属于不同的包,您可能只想查看来自 Foo 的日志。

          此外,如果您想写protected 字段,请务必三思而后行。这不是完全禁止的,而是众所周知的不良做法。它使您的代码可读性降低并且难以维护。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-10-11
            • 1970-01-01
            • 2011-06-17
            • 2013-10-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多