logback4_MDC


MDC (Mapped Diagnostic Context)

一 What’s MDC

在分布式系统中,多线程处理多个客户端司空见惯.有个做法是每个客户端对应一个Logger,但这种做法会产生大量Logger而且管理起来也不便.另有一种做法是将日志的上下文信息放到MDC里.

MDC 是slf4j的定义,而不是 Logback的, 但Logback-classic依赖了 slf4j,所以Logback中也能使用slf4j.

但是 slf4j中的MDC 只是个 代理, Logback中真正的MDC实现是LogbackMDCAdapter.

这是MDC的核心方法, 其实也是些增删改查的方法.

public class MDC {
  //Put a context value as identified by key
  //into the current thread's context map.
  public static void put(String key, String val);

  //Get the context identified by the key parameter.
  public static String get(String key);

  //Remove the context identified by the key parameter.
  public static void remove(String key);

  //Clear all entries in the MDC.
  public static void clear();
}

1.1 本质

MDC本质上就是个 ThreadLocal.
LogbackMDCAdapter的代码中我们可以看出来 , 这个类的核心数据结构是 :

final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();

1.2 例子

下面三块反映了如何使用MDC.
1 java代码

public class SimpleMDC {
  static public void main(String[] args) throws Exception {
    // You can put values in the MDC at any time. Before anything else
    // we put the first name
    MDC.put("first", "Dorothy");
    Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
    // We now put the last name
    MDC.put("last", "Parker");
    // The most beautiful two words in the English language according
    // to Dorothy Parker:
    logger.info("Check enclosed.");
    logger.debug("The most beautiful two words in English.");
    MDC.put("first", "Richard");
    MDC.put("last", "Nixon");
    logger.info("I am not a crook.");
    logger.info("Attributed to the former US president. 17 Nov 1973.");
  }
}

2 配置

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> 
  <layout>
    <Pattern>%X{first} %X{last} - %m%n</Pattern>
  </layout> 
</appender>

3 结果

Dorothy Parker - Check enclosed.
Dorothy Parker - The most beautiful two words in English.
Richard Nixon - I am not a crook.

二 Automating access to the MDC

在处理多客户端的时候,MDC很有用. 通过MDC在线程中传递数据, 也通过MDC将数据透传给 appender . 虽然没有MDC 我们在logger.xxx()时 多打印出自己想要的信息也是可以,但是:其一没有封装, 其二通过方法人肉传参其实很繁琐.

2.1 案例一

有一个web应用, 我们希望在日志中记录客户端的ip host user等信息,一个简便的方法是在 servlet Filter 中实现,比如:

public class UserServletFilter implements Filter {
  private final String USER_KEY = "username";
  public void destroy() {
  }
  public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
    boolean successfulRegistration = false;
    HttpServletRequest req = (HttpServletRequest) request;    
    Principal principal = req.getUserPrincipal();
    // Please note that we could have also used a cookie to 
    // retrieve the user name
    if (principal != null) {
      String username = principal.getName();
      successfulRegistration = registerUsername(username);
    } 
    try {
      chain.doFilter(request, response);
    } finally {
      if (successfulRegistration) {
        MDC.remove(USER_KEY);
      }
    }
  }
  public void init(FilterConfig arg0) throws ServletException {
  }
  private boolean registerUsername(String username) {
    if (username != null && username.trim().length() > 0) {
      MDC.put(USER_KEY, username);
      return true;
    }
    return false;
  }
}

然后我们将这个Filter 注册到servlet 容器中(通过web.xml)

2.2 切线程

正由于MDC的本质是ThreadLocal,在多线程环境中使用时需要特别注意.
如果涉及到线程切换, 我们可以在 当前线程中 MDC.getCopyOfContextMap(), 然后在另一个线程中 第一句就执行 MDC.setCopyOfContextMap(). 本质上这把 ThreadLocal的内容跨线程传递了.

2.3 MDCInsertingServletFilter

MDCInsertingServletFilter是一个Logback内置的记录HTTP信息的Filter, 我们只要:
1 在web.xml中配置:

<filter>
  <filter-name>MDCInsertingServletFilter</filter-name>
  <filter-class>
    ch.qos.logback.classic.helpers.MDCInsertingServletFilter
  </filter-class>
</filter>
<filter-mapping>
  <filter-name>MDCInsertingServletFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

2 在 appender 配置
%X{req.remoteHost} %X{req.requestURI}%n%d - %m%n

这里的 req.remoteHost就是一个MDC key (内置)

1 ,2 两步完成即可使用该Filter .

三 参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值