在现实的场景中经常有这样的需求,在纷繁的日志文件中,通过搜索某一个关键字(如订单号),能方便清晰的列出某一业务(如支付)的完整的处理流程。
一个笨办法是在每个日志中加上该关键字,如下所示:
logger.info("[{}] entering pay",orderId); logger.info("[{}] check whether order is repeated ",orderId); logger.info("[{}] save order to db",orderId); logger.error("[{}]", orderId, e); ......
有没有更方便的方法满足这一业务要求呢? 而不用这么麻烦要在每个日志中都要显式加上tag,所幸log4j自身就提供了这么一个方便易用的工具类--NDC。
只要在入口方法中设置tag,离开方法前remove即可。见如下示例代码:
protected static final Logger logger = LoggerFactory .getLogger(NDCDemo.class); void test1(long orderId){ //入口方法 NDC.push("["+orderId+"]"); //进入方法设置tag logger.info("entering test1"); //正常记录日志 无需显式添加tag test2(); test3(); test6(); NDC.remove(); //离开方法删除tag } private void test6() { logger.info("entering test6"); } private void test3() { logger.info("entering test3"); test5(); } private void test5() { logger.info("entering test5"); } private void test2() { logger.info("entering test2"); test4(); } private void test4() { logger.info("entering test4"); } public static void main(String[] args) { NDCDemo app = new NDCDemo(); app.test1(System.currentTimeMillis()); }
以及日志输出,注意每个日志信息前都有了上述代码所设置的tag。
[04 21:10:02,032 INFO ] [main] com.tcl.gateway.log4j.NDCDemo - [1428153002029]-entering test1 [04 21:10:02,035 INFO ] [main] com.tcl.gateway.log4j.NDCDemo - [1428153002029]-entering test2 [04 21:10:02,035 INFO ] [main] com.tcl.gateway.log4j.NDCDemo - [1428153002029]-entering test4 [04 21:10:02,035 INFO ] [main] com.tcl.gateway.log4j.NDCDemo - [1428153002029]-entering test3 [04 21:10:02,035 INFO ] [main] com.tcl.gateway.log4j.NDCDemo - [1428153002029]-entering test5 [04 21:10:02,035 INFO ] [main] com.tcl.gateway.log4j.NDCDemo - [1428153002029]-entering test6
同时配置文件中输出模板需要添加一个字符x(表示从NDC取消息)。如下所示:
<layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c - %x-%m%n" /> </layout>
这样的话即使在多用户并发的情况下(如servlet和dubbo service)也容易根据某一关键字(如订单号、ip)来定位出完整的业务链。
另外除了NDC外还有一个工具类--MDC–也适用于这一场景,使用方式和NDC差不多,仍沿用上述示例,仅需部分改动:
void test1(int orderId){ MDC.put("orderId", "["+orderId+"]"); // NDC.push --> MDC.put(key , value) logger.info("entering test1"); test2(); test3(); test6(); MDC.remove("orderId"); //NDC.remove --> MDC.remove(key) }
配置文件的输出模板中加一个X,如下所示:
<layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c -%X{orderId}-%m%n" /> </layout>
另外他们两者的区别及更多用法见各自的javadoc。