使我的记录器对我的Java应用程序非常有效

时间:2018-05-24 16:50:10

标签: java logging macros effective-java

我正在努力解决以下问题并寻求帮助。

我的应用程序有一个记录器模块。这将获取跟踪级别和消息(作为字符串)。

通常应该是从不同来源和/或不同方式构造的消息(例如,在记录之前使用String.format,有时使用不同对象的.toString方法等)。因此:错误消息的构造方法不能一概而论。

我想要的是让我的记录器模块有效。这意味着:只有在实际跟踪级别获取消息时才会构建跟踪消息。这可以通过阻止我的应用程序中的复制粘贴代码来实现。

使用C / C ++,通过使用宏很容易实现:

#define LOG_IT(level, message) if(level>=App.actLevel_) LOG_MSG(message);

仅当跟踪级别启用该消息时,才会执行LOG_MSG和字符串构造。

使用Java,我没有发现任何类似的可能性。这是为了防止:日志记录将是一行(没有if-else复制粘贴到处),并且只在必要时才进行字符串构造(昂贵的操作)。

我所知道的唯一解决方案是使用IF语句来记录每个记录器调用。但这正是我之前在C ++应用程序中避免使用的内容,也是我在实际Java实现中要避免的内容。

我的问题是,在目标系统上只有Java 1.6可用。因此,供应商不是一个选择。

我可以用Java做什么?如何轻松完成这个C / C ++方法?

3 个答案:

答案 0 :(得分:2)

首先,如果您正在考虑实施自己的记录器,我建议您阅读this

然后,我建议您查看一个完善的日志API,如SLF4j。虽然可以创建自己的API,但使用预先存在的API将节省您的时间和精力,而且首先为您提供更多功能和开箱即用的灵活性(即基于文件的配置,可定制性(查看映射的诊断上下文) )。

对于您的具体问题,没有一种简单的方法可以做您想要做的事情。 C / C ++与java的根本不同之处在于预处理器允许使用上面创建的宏。 Java实际上没有一个易于使用的等价物,尽管有一些项目的例子确实使用了编译时代码生成,这可能是最接近的等价物(即Project Lombok,Mapstruct)。

我知道的最简单的方法是避免昂贵的字符串构建操作,而日志记录是用简单的条件包围字符串的构建:

if ( logger.isTraceEnabled() )
{
    // Really expensive operation here
}

或者,如果您使用的是Java 8,标准日志记录库将采用java.util.function.Supplier<T>参数,该参数仅在当前日志级别与正在调用的日志记录方法的日志级别匹配时执行:

 log.fine(()-> "Value is: " + getValue());

目前还有一张为SLF4j开放的票证可以实现此功能here

如果您真的真的开始实施自己的记录器,那么上述两个功能很容易实现,但我再次鼓励您不要这样做。

答案 1 :(得分:1)

最新的日志记录库(包括java.util.logging)采用第二种方法形式,采用Supplier<String>

e.g。 log.info( ()->"Hello");代替log.info("Hello");

只有在有效记录消息时才调用供应商的get()方法,因此只在这种情况下构建字符串。

答案 2 :(得分:1)

我认为在这里最重要的事情是, C / C ++宏解决方案不会通过不构造记录的消息来节省计算,以防万一日志级别为消息将不会被记录。 为什么会这样呢?仅仅因为宏方法会使预处理器替代宏的所有用法:

LOG_IT(level, message)

代码:

if(level>=App.actLevel_) LOG_MSG(message);

将您通过级别传递的任何内容和您通过消息传递的所有内容以及宏本身替换为。 与要在程序中各处复制并粘贴代码的结果完全相同。宏唯一可以帮助您的是避免实际的复制和粘贴,并使代码更具可读性和可维护性。
有时他们设法做到这一点,而其他时候他们使代码变得更加晦涩难懂,因此难以维护。在任何情况下,宏都不提供延迟执行,以免您实际构造字符串,就像 Java8 Logger类通过使用lambda表达式所做的那样。 Java将lambda主体的执行推迟到最后一次可能的时间。换句话说,lambda的主体在if语句之后执行。
回到C \ C ++中的示例,作为开发人员,您可能希望代码工作不受日志级别的影响,因此您将被迫构造有效的字符串消息并将其传递给宏。否则,在某些日志级别,程序将崩溃!因此,由于消息字符串构造代码必须在调用宏之前,因此无论日志级别如何,您都将每次执行它。

因此,在Java 6中,使代码等效于您非常简单!您只需使用内置类: Logger 。此类提供对自动记录levels的支持,因此您无需为其创建自定义实现。
但是,如果您要问的是如何在没有lambda的情况下实现延迟执行,那么我认为这是不可能的。

如果要在C \ C ++中进行真正的延迟执行,则必须编写日志记录代码,例如将函数指针指向返回消息字符串的函数,您将使代码执行传递给您的函数通过if语句中的函数指针,然后您将调用传递的宏而不是字符串,而是传递创建并返回字符串的函数!我相信执行此操作的实际C \ C ++代码超出了此问题的范围...此处的关键概念是 C \ C ++为您提供了进行延迟执行的工具,仅仅是因为它们支持函数指针。在Java8之前,Java不支持函数指针。