log是程序员的好朋友,合理的使用log系统,可以方便记录发生的bug、异常,尤其在debug无能的时候,log发挥至关重要的作用。Log4j是Apache的一个开源项目,是一个功能强大的日志组件。可以使用配置文件,即可在程序中使用它的日志输出机制。除此之外,作者(们)还写了SLF4j、logback等一系列的log组件,不过log4j应该是持续时间最久,用户基数也比较大的一支。
关于log4j的介绍,可以看 LOG4J官网。有使用方法等详细信息。但是有时候除了log,还希望输出一些附加信息,虽然可以在每个打印log的地方都插入这些信息,但显然是很不方便的,尤其对于一些固定来源的信息,不如给个上层调用来的方便。这就是此文要讨论的内容。
NDC vs MDC
NDC和MDC可以记录程序中上下文相关的信息,经常被用于输出log。NDC(Nested Diagnostic Context)即嵌套上下文诊断,MDC(Mapped Diagnostic Context)即映射的上下文诊断,二者的区别从表面上来看就是存储方式不一样。
更进一步的,NDC使用了栈的思想来存取信息,即把相关信息压入(push)/弹出(pop),这就是达到嵌套效果的原因:入A入B出B出A,或者,入A出A入B出B这样。在适当的时候push一堆进去,然后log4j在打印的时候就会从NDC里取值,这样,就很容易把当前NDC的内容给关联进去。通过这种机制,打log的时候就不用关注上下文或者NDC的信息,完全由系统去做。
NDC需要程序去控制合适push/pop适当的信息,并有可能操作不当(没有定期执行NDC.remove)造成内存泄漏。
MDC的机制和NDC类似,不过是采用了键值对应的思想来存取信息。
哪个更合适?
当需要打印的内容有嵌套关系的时候,用NDC;当键值就够用的时候,MDC。
将当前用户信息加入log
目前的Spring项目中某些操作希望简单输出具体用户信息到log里,所以就用了MDC处理这个。
目前使用了shiro来进行用户权限管理,SecurityUtils.getSubject().getPrincipal()是用来获取当前用户的。此处可以改成其他内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
publicclassUserToMdcFilterimplementsFilter{
@Override
publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{
MDC.put("user",(SecurityUtils.getSubject().getPrincipal()!=null)?SecurityUtils.getSubject().getPrincipal():"");
try{
chain.doFilter(request,response);
}finally{
MDC.remove("user");
}
}
@Override
publicvoidinit(FilterConfigfilterConfig)throwsServletException{
}
@Override
publicvoiddestroy(){
}
}
在spring的xml文件里声明一个bean,指向上面创建的类,方便注入。
1
在web.xml里声明一个filter,用它过滤所有request。
1
2
3
4
5
6
7
8
9
log4jMDCUserFilter
org.springframework.web.filter.DelegatingFilterProxy
log4jMDCUserFilter
/*
最后记得在logj.properties里加入user,格式为%X{user}(user为上面放到MDC里的key)
例如:
1
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-ddHH\:mm\:ss}[%c{1}:%L]-[%p]%X{user}-%m%n
输出效果:
1
2016-03-2416:44:49[DispatcherServlet:997]-[DEBUG]axiu-Successfullycompletedrequest