Java 8和Kotlin中的智能日志记录

伐木不是一个性感的话题,但仍然很重要。 在Java世界中,通过Commons Logging和JDK日志记录(从现在开始不包括Log4J 2),日志记录框架的范围从Log4J到SLF4J 。 尽管其架构和功能不同,但它们的所有API看起来都是相同的。 记录器针对每个日志级别都有一个方法, 例如

  • debug(String message)
  • info(String message)
  • error(String message)
  • 等等

级别组织为层次结构。 一旦将框架配置为特定级别,将仅写入以相同或更高优先级记录的消息。

到目前为止,一切都很好。 当消息包含变量以便必须将它们串联时,就会出现问题。

LOGGER.debug("Customer + "customer.getId()+" has just ordered "+item.getName()+" ("+item.getId()+")");

在Java中,字符串串联具有一定的性能成本, 无论配置的日志级别如何,都会发生。

因此,诸如SLF4J之类的现代日志记录框架提供了改进的签名,可以接受类型为String的消息格式和变量为Object varargs的消息格式。 在这种情况下,仅当记录器有效写入时才发生串联。

LOGGER.debug("Customer {} has just ordered {} ({})",customer.getId(),item.getName(),item.getId());

但是,有时变量不容易获得,而必须仅出于记录目的而明确地进行计算。

LOGGER.debug("Customer {} has just ordered {}",customer.getId(),order.expensiveComputation());

SLF4J对此无济于事,因为即使记录器决定后者不写,因为该框架配置了更高的优先级,该方法也会被评估。 因此,在这种情况下,建议将logger方法调用包装在相关的优先级检查中。

if(LOGGER.isDebug()){
        
    LOGGER.debug("Customer {} has just ordered {}",customer.getId(),order.expensiveComputation());
}

必须对每个昂贵的方法调用进行此操作,因此,它需要严格的编码规则并进行检查,以确保在相关时才进行换行,但仅在那时才进行换行。 此外,它降低了可读性,从而降低了可维护性。 为了在没有这些缺点的情况下自动实现相同的结果,可以使用AOP ,但要付出额外的复杂性。

带有Java 8和Supplier接口,该接口返回String ,因此可以这样创建方法:

publicvoiddebug(Supplier<String>s){
        
   if(LOGGER.isDebugEnabled()){
        
        LOGGER.debug(s.get());
   }
}

在这种情况下, 在包装条件的值为true 时才调用get()方法。

使用此方法非常简单:

debug(()->("Customer + "customer.getId()+" has just ordered "+order.expensiveComputation());

大! 但是,将改进的debug()方法放在哪里?

  • 在课程本身中:每个课程重复一次。 真?
  • 在实用程序类中:应该将LOGGER添加为第一个参数。 你说烦吗
  • 在记录器中:可以创建包装器并将标准记录器保留为委托,但是工厂是final (至少在SLF4J中如此),并且具有许多private方法。
  • 一方面:回到正方...

这是在Java领域开始变得不太好的步骤。

那Kotlin呢? 它带有扩展功能 (和属性)。 这可能是将来发布的主题,因为您仅应将此功能用于Kotlin。 足以说Kotlin可以使其看起来像可以为已经定义的类型添加状态和行为。

因此,可以在一个适当命名的文件中定义debug()

funLogger.debug(s:()->String){
        
    if(isDebugEnabled)debug(s.invoke())
}

调用它真的感觉就像调用标准记录器,只传递了一个lambda:

LOGGER.debug{
        "Customer + "customer.getId()+" has just ordered "+order.expensiveComputation()}

最后,让我们内联定义的函数。 这样,编译器将有效地用bytecode中的方法替换调用该方法的每个位置。 我们获得了可读语法的好处,并避免了在运行时解开lambda的开销:

inlinefunLogger.debug(s:()->String){
        
    if(isDebugEnabled)debug(s.invoke())
}

请注意,尽管语法不太好,但仍可以在Java中调用它:

Slf4KUtilsKt.debug(LOGGER,()->"Customer + "+customer.getId()+" has just ordered "+order.expensiveComputation());

还要注意的是Log4J的2已经实现了此功能外的开箱。

至此,在需要时将上面的代码片段复制编码就足够简单了。 但是开发人员本质上是懒惰的 ,所以我围绕SLF4J方法创建了一个完整的包装器。 Github上提供了源代码, Bintray上提供了二进制工件,因此您仅需要以下依赖关系即可使用它:


   ch.frankel.log4k 
   slf4k-api 
   1.0.0 

翻译自: https://blog.frankel.ch/smart-logging-in-java8-kotlin/

你可能感兴趣的:(java,python,spring,数据库,android)