在现实的场景中经常有这样的需求,在纷繁的日志文件中,通过搜索某一个关键字(如订单号),能方便清晰的列出某一业务(如支付)的完整的处理流程。
一个笨办法是在每个日志中加上该关键字,如下所示:
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。