IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    使用MDC在log4j里记录用户信息

    axiu发表于 2016-03-25 01:12:08
    love 0

    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()是用来获取当前用户的。此处可以改成其他内容。

    public class UserToMdcFilter implements Filter{
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            MDC.put("user", (SecurityUtils.getSubject().getPrincipal() != null) ? SecurityUtils.getSubject().getPrincipal() : "");
            try {
                chain.doFilter(request, response);
            } finally {
                MDC.remove("user");
            }
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        @Override
        public void destroy() {
        }
    }

    在spring的xml文件里声明一个bean,指向上面创建的类,方便注入。

    <bean id="log4jMDCUserFilter" class="com.test.common.UserToMdcFilter" />

    在web.xml里声明一个filter,用它过滤所有request。

    <filter>
            <filter-name>log4jMDCUserFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        </filter>
        
        <filter-mapping>
            <filter-name>log4jMDCUserFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>

    最后记得在logj.properties里加入user,格式为%X{user}(user为上面放到MDC里的key)

    例如:

    log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH\:mm\:ss} [%c{1}:%L]-[%p] %X{user} - %m%n

    输出效果:

    2016-03-24 16:44:49 [DispatcherServlet:997]-[DEBUG] axiu - Successfully completed request

    转载请注明来源:使用MDC在log4j里记录用户信息
    本文链接地址:http://axiu.me/coding/record-userinfo-by-using-mdc-in-log4j/


沪ICP备19023445号-2号
友情链接