Logger.getLogger(MyClass.class)是初始化log4j记录器的最佳方法吗?


13

此Mkyong教程建议以这种方式初始化记录器:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

现在,假设您使用的所有其他类都具有记录器,它们将以相同的方式初始化其记录器。

我的问题是-这是最好的方法吗?似乎...重复。


2
您对此有何详细了解(抛开Java的语法)?您必须创建一个变量来保存记录器,并且必须告诉getLogger()获取记录器的名称。
kdgregory

2
@kdgregory我在每个班级都在做同样的事情。
dwjohnston 2015年


3
太旧了,无法迁移,但是此问题不在此处,因此更适合StackOverflow。
安德烈斯·F

1
@AndresF。我认为我把它放在这里是因为它更多是关于代码样式/设计模式的问题,而不是技术问题。
dwjohnston '16

Answers:


16

您的评论说“冗长”是指需要在每个类中重复此行代码。我的第一反应是,从总体上看,向每个类添加两行代码(变量定义加import语句)并不是什么大问题。特别是因为您只需要将它们添加到具有行为的类中,因此需要进行日志记录。也就是说,您使用的特定代码行容易出现复制粘贴错误(稍后会详细介绍)。

但是,由于您需要替代品,因此这里列出了一些替代品,原因是您可能会或可能不想使用它们。

对整个应用程序使用单个记录器

如果您不关心报告什么类,或不愿意将所有必要的上下文放入消息中,那么简单的单例记录器就可以完成此工作:

LoggerSingleton.getInstance().debug("MyController is running")

我认为,日志记录框架的一大好处是拥有由单独的日志记录器实例提供的上下文-如果仅是将日志消息定向到不同的目的地。我不会为了保存一行代码而放弃(您仍然需要导入)。

另外,这会增加使用时的冗长性,最终将导致更多的击键。

在使用时创建记录器

我之所以扔掉它只是因为它消除了变量。我认为不需要对此发表评论。尽管确实显示了我获取记录器实例的首选技术。

Logger.getLogger(getClass()).debug("blah blah blah");

使用Bean后处理器注入记录器

您的示例使用Spring,而Spring使您可以插入Bean初始化代码。您可以创建一个后处理器,该后处理器检查bean的logger成员变量,并Logger在找到一个实例时创建一个实例。

尽管这样的后处理器只有几十行代码,但这是应用程序中另一个动人的部分,因此是潜在的错误来源。我宁愿尽可能少的那些。

使用混入

Scala和Groovy提供了traits,使您可以封装行为。典型的Scala模式是创建一个Logging特征,然后将其添加到需要记录的类中:

class MyController with Logging

不幸的是,这意味着您必须切换语言。除非您使用Java 8,否则可以使用Logging“默认方法” 创建接口:

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

现在,在您的类代码中,您可以简单地使用

getLogger().debug("blah blah blah");

虽然很容易,但是有两个缺点。一方面,它会污染使用它的每个类的接口,因为所有接口方法都是公共的。如果仅将其用于由Spring实例化和注入的类,则可能不是那么糟糕,特别是如果遵循接口/实现分离的话。

更大的问题是它必须在每次调用时查找实际的记录器实例。速度很快,但不必要。

而且您仍然需要导入声明。

将记录器移至超类

我会重复:我发现重复的记录器定义并不冗长,但是如果您这样做,我认为这是消除它们的最佳方法。

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

现在,您的控制器类继承自AbstractController,并且可以访问该logger变量。请记住,您必须将@Controller注释放在具体的类上。

有些人会发现这是继承的错误。我试图通过命名班级AbstractController而不是班级来减轻他们的负担AbstractProjectClass。您可以自己决定是否存在is-a关系。

其他人会反对使用实例变量而不是静态变量。IMO静态记录器容易出现复制粘贴错误,因为您必须显式引用类名。getClass()确保您的记录器始终正确。


我认为您应该小心继承类中的getClass(),在jdk 7
yuxh '19

@luxh-您对此有解释吗?
kdgregory


@yuxh-该问题中唯一提及MethodHandles的问题(您未链接的答案中没有提及),是类似的不受支持的断言,并带有注释要求澄清。您是否有比这更好的主张的权威支持?MethodHandles.lookup().lookupClass()Object.getClass()
kdgregory

MethodHandles.lookup().lookupClass()可以用于静态变量,不容易出现复制粘贴错误并且速度很快:stackoverflow.com/a/47112323/898747这确实意味着额外的进口,但是我非常喜欢我的记录仪处于静态状态,因此至少值得一提及:)
FableBlaze

0

为了扩展@kdgregory提供的答案,Groovy提供了@Slf4jgroovy.util.logging.Slf4j)开箱即用的注释,该注释对类执行AST转换,以使用记录器记录该记录,该记录器默认为变量名称log(如果未指定)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.