继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

混入日志

www说
关注TA
已关注
手记 476
粉丝 83
获赞 493

本文通过应用中天天都见得到的日志打印谈起,聊聊封装隐藏,性能优化,惰性求值,消除重复的技术实践。

延迟评估

Eliminate Effects Between Unrelated Things.

远古时代

这是早期日志打印的方式。

if (logger.isLoggable(Level.INFO)) {
  logger.info("problem:" + getDiagnostic());
}

这个实现存在如下一些问题:

  • 重复的「样板代码」,并且散乱到程序的各个角落;

  • logger.debug之前,首先要logger.isLoggableLogger暴露了太多的状态逻辑,违反了LoD(Law of Demeter)

应用LoD

logger.info("unexpect problem: {}", getDiagnostic());

这样的设计虽然将状态的查询进行了封装,但依然存在一个严重的性能问题。即使日志开关关闭,getDiagnostic都将被调用;如果它是一个耗时、昂贵的操作,将严重地消耗系统性能。

使用Java8

灵活地应用Lambda惰性求值的特性,可以很漂亮地解决这个问题。

public void log(Level level, Supplier<String> supplier) {  if (isLoggable(level)) {
    log(supplier.get());
  }
}public void debug(Supplier<String> supplier) {
  log(Level.DEBUG, supplier);
}public void info(Supplier<String> supplier) {
  log(Level.INFO, supplier);
}

...

用户的代码也更加简洁,省略了那些重复的样板代码。

logger.info(() -> "problem:" + getDiagnostic());

使用Scala: call-by-name

使用Java8 Lambda时,() ->的语法显得有点怪异;如果使用Scala,可以使用by-name机制进一步提高表达力。

def log(level: Level, msg: => String) {  if (isLoggable(level)) {
    log(msg)
  }
}def debug(msg: => String) {
  log(DEBUG, msg)
}def info(msg: => String) {
  log(INFO, msg)
}
logger.info(s"problem: ${getDiagnostic()}");

s"problem: ${getDiagnostic()}"语句并非在logger.info展开计算,它被延迟直至被apply的时候才真正地被评估和计算。

复用代码

DRY: Don't Repeat Youself

用户空间

再将目光投放到Java应用,当每次要使用Logger时,都需要搬迁import语句,并定义logger的静态字段,这样的重复结构很容易通过Copy-Paste产生。

import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class Application {  private static Logger logger = LoggerFactory.getLogger(Application.class);  
  public void run() {
    logger.trace("start run");
  }
}

消除重复

使用Scala可以定义Logging的特质,以便消除重复。

import org.slf4j.{Logger, LoggerFactory}trait Logging {  val loggerName = this.getClass.getName  lazy val logger: Logger = LoggerFactory.getLogger(loggerName)  def trace(msg: => String) {    if (logger.isTraceEnabled())
      logger.trace(msg)
  }  def info(msg: => String) {    if (logger.isTraceEnabled())
      logger.trace(msg)
  }
  
  ...
}

混入特质

应用程序可以通过混入Logging,自动得到日志打印的各个接口。

class Main extends App with Logging {
  info("starting...")
}



作者:刘光聪
链接:https://www.jianshu.com/p/4a6853174e88


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP