2011-12-13 2 views
5

우리는 많은 다른 응용 프로그램에서 인스턴스화 된 일반 오래된 자바 라이브러리가 있습니다. 이 경우 각 응용 프로그램은 모두 동일한 tomcat 컨테이너에있는 웹 응용 프로그램입니다.라이브러리의 각 인스턴스에 대한 별도의 로거

각 응용 프로그램은 자체 로거를 사용하여 자체 로그 파일에 기록합니다. 특정 응용 프로그램과 관련된 라이브러리에 의해 생성 된 로그가 해당 응용 프로그램 별 로그 파일로 이동되기를 원합니다.

library = new library(Logger applicationsVeryOwnLogger); 

을 그리고 라이브러리에있는 모든 명령문을 기록, 그 로거를 사용이를 위해

는 한 가지 방법은 응용 프로그램이 라이브러리의 로거를 전달할 수 있도록하는 것입니다. 그러나 이는 이제 로거가 라이브러리의 클래스 변수가되고 라이브러리의 모든 클래스가 올바른 로거를 사용하기 위해 라이브러리에 대한 참조를 필요로 함을 의미합니다.

더 좋은 방법이 있습니까?

답변

2

이전 응용 프로그램 중 하나에서 비슷한 필요성이있었습니다. 우리가 생각해 낸 해결책은 ResourceLoader (컨텍스트)에 의해 리소스 (Logger, Config 파일 등)를 검색하는 ResourceManager였습니다.

은 일반적으로 EAR로 배포 모든 응용 프로그램이 자체 클래스 로더를 얻고 라이브러리가 그럼 그냥 ResourceManager.getLogger를 호출 할 수 있습니다() 현재 스레드/응용 프로그램과 관련된 로거를 얻을 수 있습니다. 그렇게하면 라이브러리의 모든 메서드 호출에 전달할 필요가 없습니다 (라이브러리를 변경할 수 있어야합니다).

Logger logger = Logger.getLogger([Application Logger Name]); 
ResourceManager.registerLogger(logger); 

라이브러리에서 로거를 검색 (유틸리티 메소드) :

private Logger getLogger() 
    { 
     return ResourceManager.getLogger();  
    } 
EJB/웹 애플리케이션의 초기화 단계에서

import java.util.*; 
import java.util.logging.*; 

public class ResourceManager 
{ 
    private static final Map<ClassLoader, Map<String, Object>> resources = 
     Collections.synchronizedMap(new WeakHashMap<ClassLoader, Map<String, Object>>()); 
    public static final String LOGGER = Logger.class.getName(); 

    static 
    { 
     // adjust for log4j or other frameworks 
     final Logger logger = Logger.getLogger("logging.default"); 
     logger.setLevel(Level.ALL); 
     logger.addHandler(new ConsoleHandler() 
     { 
      { 
       setOutputStream(System.out); 
       setLevel(Level.ALL); 
      } 
     }); 
     registerResource(null, LOGGER, logger); 
    } 

    private static ClassLoader getApplicationScope() 
    { 
     return Thread.currentThread().getContextClassLoader(); 
    } 

    public static void registerResource(final String name, final Object resource) 
    { 
     registerResource(getApplicationScope(), name, resource); 
    } 

    public static synchronized void registerResource(final ClassLoader scope, final String name, final Object resource) 
    { 
     Map<String, Object> hm = null; 
     hm = resources.get(scope); 
     if (hm == null) 
     { 
      hm = Collections.synchronizedMap(new HashMap<String, Object>()); 
      resources.put(scope, hm); 
     } 
     hm.put(name, resource); 
    } 

    public static Object getResource(final String name) 
    { 
     for(ClassLoader scope = getApplicationScope();;scope = scope.getParent()) 
     { 
      final Map<String, Object> hm = resources.get(scope); 
      if ((hm != null) && hm.containsKey(name)) 
      { 
       return hm.get(name); 
      } 
      if (scope == null) break; 
     } 
     return null; 
    } 

    public static void registerLogger(final Logger logger) 
    { 
     registerResource(LOGGER, logger); 
    } 

    public static Logger getLogger() 
    { 
     return (Logger)getResource(LOGGER); 
    }  
} 

등록 로거 (getLogger에 대한 호출하기 전에 등록해야합니다)

이것은 현재 스레드와 연관된 응용 프로그램 (EAR)에 대한 로거를 리턴합니다.

로거에 국한되지 않고 공유하려는 다른 리소스에도 사용할 수 있습니다.

제한 : 당신이 전개 된 EAR ​​

  • 는 ResourceManager 및 로깅 라이브러리에 여러 응용 프로그램/EJB를 패키징 할 경우

    • 실 거예요 작업은 동일하거나 라이브러리와 응용 프로그램보다 높은 클래스 로더에 있어야합니다. 번들 옵션이 있다면 Alexanders 접근법이 더 깨끗합니다. (우리는 기본적으로 서버 레벨에서 java.util.logging을 사용하여 접근하지 못합니다)

  • +0

    참고 자료로이 솔루션은 로거뿐만 아니라 모든 종류의 리소스를 공유하는 데 사용할 수도 있습니다. – Stefan

    +0

    링크가 미래에 죽을 경우를 대비하여 관련 코드 섹션을 답안에 붙여 넣기를 원할 수 있습니다. – rouble

    +0

    이 메서드에 대해주의 할 점은 라이브러리가 인스턴스화되기 전에 ResourceManager.registerLogger()를 호출해야한다는 것입니다. 그렇지 않으면 클래스 로거는 기본 로거를 사용하여 모두 설정됩니다. 이것은 일부 구현의 경우 거래 차단기가 될 수 있습니다. – rouble

    3

    당신은 log4j 태그 질문을 표시 한, 그래서 나는 당신이 사용하는 무엇을 가정합니다.

    귀하의 도서관에서 고유 한 패키지 이름을 사용하기를 바랍니다.

    사실이 경우 실제로 해당 패키지에 대한 로거를 설정할 수 있습니다.

    log4j.category.my.lib.package = INFO, libFileAppender 
    log4j.rootLogger = INFO, rootFileAppender 
    

    모두 libFileAppenderrootFileAppender에 라이브러리에서 메시지를 기록합니다 이렇게.

    이 같은, 그 로거 가산을 해제 할 수 있습니다 당신이 rootFileAppender에 표시 라이브러리에서 메시지를 원하지 않는 경우 :이

    log4j.category.my.lib.package = INFO, libFileAppender 
    log4j.additivity.my.lib.package = false 
    log4j.rootLogger = INFO, rootFileAppender 
    

    을, 당신은 단지 libFileAppender에 메시지가 표시됩니다

    +0

    이 요구 사항을 충족시키지 못합니다. 이를 통해 라이브러리의 모든 로그를 한 곳으로 리디렉션 할 수 있습니다. 그러나 특정 응용 프로그램에서 생성 된 라이브러리의 각 로그는 응용 프로그램의 로그 파일 인 한 곳으로 이동해야합니다. – rouble

    +0

    @prmatta. 이 파일을 WEB-INF/classes에 복사하고 해당 응용 프로그램에 특정한 설치 appender를 작성하면 각 응용 프로그램은 해당 appender에 라이브러리 호출을 기록합니다. 그것이 당신의 요구 사항을 어떻게 충족시키지 못하는지 나는 알지 못합니다. –

    +0

    JVM 수준에서 관리되는 log4j에 대한 로거/어 펜더를 종료 하시겠습니까? 그리고 모든 로거 (라이브러리에 이름이 필요하기 때문에 필요한)에 동일한 이름을 사용하면 동일한 로거를 계속 등록합니다. – Stefan

    관련 문제