何时从MDC中删除

Old*_*eon 5 java tomcat servlets log4j mdc

我最近发现了在记录时使用MDC的魔力.它完美地运作.

我有以下方法:

public static final String MDC_CLIENT="client";
public static final String MDC_SESSION="session";
public static final String MDC_DEVICE="device";

// Called for every request.
public static void request(final HttpServletRequest request) {
  // The MDC is a thread-local storage accessable from the log formatter.
  MDC.put(MDC_CLIENT, String.format("%s:%s", request.getRemoteHost(), request.getRemotePort()));
  HttpSession session = request.getSession();
  MDC.put(MDC_SESSION, session.getId());
  MDC.put(MDC_DEVICE, (String)session.getAttribute("device"));
  // Also record the context.
  setContext(session.getServletContext());
}
Run Code Online (Sandbox Code Playgroud)

这被称为每个jsp中的第一个动作.这使我能够很好地跟踪日志中会话的详细信息.

但是 - 我怎么知道remove这些地图条目的时间?我应该注意哪些事件才能让我整理地图?

我在Tomcat下托管.如果它重新使用线程,那么我不会泄漏内存,因为它们本质上是线程本地的,所以每次put都会覆盖put上次使用线程时的旧内存.如果它没有 - 或者我托管在其他东西之下 - 我本质上可能永远地增长地图 - 或者至少在主机重新启动之前.

我认为我的问题的本质是 - 是否有一个我可以检测到的事件,表明特定会话或线程已完成并即将发布.

Ian*_*rts 5

我不会将调用放在每个JSP中,而是注册一个ServletRequestListener,它会在请求开始时和完成时得到通知:

public class MDCListener implements ServletRequestListener {
  public void requestInitialized(ServletRequestEvent e) {
    YourUtilityClass.request((HttpServletRequest)e.getServletRequest());
  }

  public void requestDestroyed(ServletRequestEvent e) {
    YourUtilityClass.tearDown((HttpServletRequest)e.getServletRequest());
  }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用过滤器,它可以包含通常的请求处理流程:

void doFilter(ServletRequest request, ServletResponse response,
              FilterChain chain) throws IOException, ServletException {
  setupMDC();
  chain.doFilter(request, response);
  tearDownMDC();
}
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,您只需注册相关的类,web.xml容器应该处理其余的.

  • 每个@Stefan:回答@ prime的问题:如果线程被另一个请求重用,并且您没有清除请求之间的MDC,那么您将访问旧请求的值。 (2认同)