kotlin 和java
伐木不是一个性感的话题,但仍然很重要。 在Java世界中,日志记录框架通过Commons Logging和JDK日志记录(从Log4J到SLF4J)不等(暂时不包括Log4J 2)。 尽管其架构和功能有所不同,但它们的所有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<T>
接口,该接口返回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上提供了二进制工件,因此您只需要以下依赖项即可使用它:
<dependency>
<groupId> ch.frankel.log4k </groupId>
<artifactId> slf4k-api </artifactId>
<version> 1.0.0 </version>
</dependency>
kotlin 和java