2014-09-27 1 views
2

나는 이것을 위해 여러 가지 제안을 보았지만 아무것도 도움이되지 않는다.SLF4J MDC 메모리 누수

MDC를 사용하는 JAX-RS 애플리케이션이 있는데, 엔드 포인트에 도달하면 디버깅을 쉽게하기 위해 transactionId가 설정됩니다. 그러나 Tomcat을 중지하거나 다시 시작할 때 로그는 다음과 같은 항목으로 채워집니다.

27-9 월 -20 09 : 42 : 14.858 SEVERE [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoader.checkThreadLocalMapForLeaks 웹 애플리케이션 [/core-1.0.0-RC2]은 type [org.apache.log4j.helpers.ThreadLocalMap] (value [[email protected]])의 키를 가진 ThreadLocal과 값 형식의 [java.util.Hashtable] (value [{siteCode = 000tst, transactionId = dc8f3a1b-1d7a-4f91-abf6-58d015632d03}])하지만 웹 응용 프로그램이 중지되었을 때이를 제거하지 못했습니다. 쓰레드는 시간이 지남에 따라 갱신되어 가능한 메모리 누수를 피하려고 시도 할 것입니다.

<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>slf4j-api</artifactId> 
    <version>1.7.7</version> 
</dependency> 
<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>slf4j-log4j12</artifactId> 
    <version>1.7.7</version> 
</dependency> 

내가 MDC.clear (와 ResponseFilter이있는 경우) 그것은의 값을 제거 :이 내 sl4fj으로 종속되어

import org.slf4j.MDC; 

import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.container.ContainerRequestFilter; 
import java.io.IOException; 
import java.util.UUID; 

public void filter(ContainerRequestContext containerRequestContext) throws IOException { 

    String siteCodeHeader = containerRequestContext.getHeaderString("Site-Code"); 

    if (siteCodeHeader != null) { 
     MDC.put("siteCode", siteCodeHeader); 
    } else { 
     MDC.put("siteCode", "NULL"); 
    } 
    MDC.put("transactionId", UUID.randomUUID().toString()); 


} 

:

나는 MDC가 호출되는 RequestFilter이 MDC를 사용하지만 스레드를 지우지 않는 것 같습니다 :

09-09 월 12 : 09 : 12 : 58.216 SEVERE [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoader.checkThreadLocalMapForLeaks 우리는 b application [/core-1.0.0-RC2]은 type [org.apache.log4j.helpers.ThreadLocalMap] (value [[email protected]])의 키를 가진 ThreadLocal을 만들고 값은 [java.util.Hashtable] (값 [{}])을 입력했지만 웹 응용 프로그램이 중지되었을 때 제거하지 못했습니다. 쓰레드는 시간이 지남에 따라 갱신되어 가능한 메모리 누수를 피하려고 시도 할 것입니다.

분명히 log4j 1.2.17에서 수정되었지만 변경 사항이 slf4j로 필터링되지 않은 것처럼 보입니다.

답변

1

이전 두 답변의 조합을 사용하여 문제를 해결할 수있었습니다.

SLF4J가 아닌 MDC의 log4j 구현을 사용하고 ResponseFilter를 추가하여 제거했습니다. 그것은 영향을 미치거나 영향을받지 않았을 지 모르지만 web.xml에서 클래스를 규정하기보다는 제공자 주석을 사용했습니다.

RequestFilter (거의 같은) :

package com.example.jaxrs; 

import org.apache.log4j.MDC; 

import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.container.ContainerRequestFilter; 
import javax.ws.rs.ext.Provider; 
import java.io.IOException; 
import java.util.UUID; 

@Provider 
public class TransactionIdentifierRequestFilter implements ContainerRequestFilter { 

    @Override 
    public void filter(ContainerRequestContext containerRequestContext) throws IOException { 

     String siteCodeHeader = containerRequestContext.getHeaderString("Site-Code"); 

     if (siteCodeHeader != null) { 
      MDC.put("siteCode", siteCodeHeader); 
     } else { 
      MDC.put("siteCode", "NULL"); 
     } 
     MDC.put("transactionId", UUID.randomUUID().toString()); 

    } 
} 

ResponseFilter :

package com.example.jaxrs; 

import org.apache.log4j.MDC; 

import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.container.ContainerResponseContext; 
import javax.ws.rs.container.ContainerResponseFilter; 
import javax.ws.rs.ext.Provider; 
import java.io.IOException; 

@Provider 
public class TransactionIdentifierResponseFilter implements ContainerResponseFilter { 

    @Override 
    public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException { 
     MDC.clear(); 
    } 
} 

web.xml을

<init-param> 
    <param-name>jersey.config.server.provider.packages</param-name> 
    <param-value>com.example.jaxrs</param-value> 
</init-param> 
+0

이 방법은 잘못되었습니다. 이제 Log4J에 코드를 묶었습니다. 처음에는 [SLF4J means] (http://www.slf4j.org/api/org/slf4j/MDC.html#clear%28%29)로 MDC를 정리해야합니다. –

+0

작동하지 않았습니다. SLF4J MDC는 1.2.17 수정 사항이 적용되기 전에 이전 버전의 log4j에서 빌드됩니다. 실제 소스를보고 그 사실을 발견했습니다. slf4j에 사용했던 기본 로깅 프레임 워크는 log4j 였으므로 새로운 종속성 등을 요구하거나 로깅 정밀 검사를 완료 할 필요가 없었습니다. –

+0

그래서 log4j를 명시 적으로 업데이트하는 것이 좋습니다. –

0

이것은 분명히 SLF4J 문제는 아니지만 Log4J 문제 일뿐입니다.범위의 POM에 직접 종속성으로 고정 된 Log4J 버전을 추가하면 문제가 없어집니다.

+0

작동하지 않았다. Log4J가 아닌 MDC의 SLF4j 버전을 사용하고 있으며 최신 버전의 log4j는 이미 다른 종속성에 대한 종속성이 있습니다. –

+0

그런 다음 요청이 처리 된 후 MDC를 정리하지 못했습니다. –

+0

MDC를 지우고 그 값이 지워졌지만 threadlocal이 여전히 남아 있지만 반환합니다. –

1

MDC는 ThreadLocal을 사용하여 현재 스레드에 보관됩니다. 필터에 값을 추가하면 서비스 호출 후에 값을 제거해야합니다.

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
    try 
    { 

    //add your mdcs 

    // proceed along the chain 
    chain.doFilter(request, response); 

    } 
    finally 
    { 

    //remove your mdcs 

    } 
} 
+0

ServletContainer를 확장하고 doFilter() 메서드를 덮어 쓰고 서블릿 클래스를 사용자 정의 클래스로 설정하는 클래스를 만들었습니다 그러나 doFilter는 적중되지 않습니다. doFilter를 명시 적으로 호출해야합니까? –

+0

서블릿 3.0 스펙 이전에는 web.xml 파일에 필터 구성을 추가했습니다. http://stackoverflow.com/questions/11092421/spring-dispatcherservlet-code-to-execute-before-it – Hannes