背景:
最近做服务化拆分过程中,因为要把公共的拦截器如登录相关的封装到API包中,很多拦截器中都有日志的打印。按理说api包中不应该有日志的打印,但是如果去掉重要的日志,那么会很难定位线上的问题。
解决方案一:
原先我们账户提供的API包需要打印一些本地存根的信息,如初始化缓存,本地存根是否取到对象,是否走了dubbo接口。于是参照了下现有的框架的解决方案,写了下面这一段逻辑。让依赖方启动的时候初始化这个bean,然后就可以用这个LogUtil对象来打印日志了。
@Component public class LogUtil implements InitializingBean { public static final Logger accountOpt = LoggerFactory.getLogger("accountOpt"); @Override public void afterPropertiesSet() throws Exception { Properties p = new Properties(); p.setProperty("log4j.category.accountOpt", "INFO" + ",accountOpt"); p.setProperty("log4j.appender.accountOpt", "org.apache.log4j.DailyRollingFileAppender"); p.setProperty("log4j.appender.accountOpt.File", "logs/log/accountOpt.log" ); p.setProperty("log4j.appender.accountOpt.layout", "org.apache.log4j.PatternLayout"); p.setProperty("log4j.appender.accountOpt.maxFileSize ", "100MB" ); p.setProperty("log4j.appender.accountOpt.maxBackupIndex ", " 30"); p.setProperty("log4j.appender.accountOpt.DatePattern ", " '.'yyyy-MM-dd"); p.setProperty("log4j.appender.accountOpt.layout.ConversionPattern", "%d - accountOpt - %c - %p [%t] %x - %m%n"); PropertyConfigurator.configure(p); } } // 使用 LogUtil.accountOpt.info("Account userName:" + userName + " is not in cache");
缺点:
1)依赖API的服务需要依赖log4j才能打印出日志。
2)配置写死在代码里了,比较丑。
3)依赖方需要手动初始化这个bean。
解决方案二:
slf4j(Simple Logging Facade for Java ), API依赖slf4j, 依赖方只要引入相应的slf4j-adapter和相应的底层实现即可。slf4j 使用了Facade模式,为啥为叫这个模式? 看看Facade模式定义吧,为复杂的子系统定义一套简单的接口,子系统就是下面的各种log实现啊,接口就是slf4j啊。通过slf4j也顺带学习了下Facade模式了。
架构如下:
代码实现:
// 依赖方只要配置accountOpt 即可 Logger logger = LoggerFactory.getLogger("accountOpt");
优势:
1)api不关心具体的日志实现,留给依赖方实现,依赖方只要引入自己的适配器jar和底层的实现log即可。
2) 即使api里面引入了相应的log实现,依赖方依然可以exclude相应的实现,做不同的log实现,而且可以打印出API里面的日志。
3)如果api已经依赖了具体的实现,如account-api依赖了log4j。那么应用应用依赖account-api的时候直接exclude掉log4j的东西,直接引入slf4j-log4j-adapter,这个包里面定义了与log4j相同的包相同的接口。所以启动不会报错,而且也不用依赖account-api里面log4j的实现。这一点是周会分享的时候波爷补充的,所以经常发现分享的时候别人往往能够看到你的不足,经常分享吧。
最终选择:
我们的最终选择是API里面使用slf4j,实现里面使用logback,因为据说logback比log4j性能高很多。