developer tip

Log4J : 로거 인스턴스 생성 전략

optionbox 2020. 11. 5. 07:56
반응형

Log4J : 로거 인스턴스 생성 전략


새로운 Java 프로젝트에 Log4J 로깅 프레임 워크를 사용하기로 결정했습니다. Logger 인스턴스를 생성 / 관리하기 위해 어떤 전략을 사용해야하며 그 이유가 궁금합니다.

  • 클래스 당 하나의 Logger 인스턴스 예 :

    class Foo {
        private static final Logger log = Logger.getLogger(Foo.class);
    }
    
  • 스레드 당 하나의 Logger 인스턴스
  • 애플리케이션 당 하나의 Logger 인스턴스
  • 수평 슬라이싱 : 애플리케이션의 각 계층 (예 : 뷰 계층, 컨트롤러 계층 및 지속성 계층)에있는 Logger 인스턴스 하나
  • 수직 슬라이싱 : 애플리케이션의 기능 파티션 내 Logger 인스턴스 1 개

참고 :이 문제는 다음 기사에서 이미 어느 정도 고려되었습니다.

Log4j 로거 생성의 오버 헤드는 무엇입니까?


일반적으로 좋은 논리적 구성 요소이기 때문에 클래스별로 로거를 설정합니다. 스레드는 이미 로그 메시지의 일부이므로 (필터에 표시되는 경우) 이러한 방식으로 로거를 분할하는 것은 중복 될 수 있습니다.

응용 프로그램 또는 레이어 기반 로거와 관련하여 문제는 해당 Logger 개체를 고정 할 위치를 찾아야한다는 것입니다. 정말 큰 문제는 아닙니다. 더 큰 문제는 일부 클래스가 여러 응용 프로그램의 여러 수준에서 사용될 수 있다는 것입니다 ... 로거를 올바르게 가져 오는 것이 어려울 수 있습니다. 또는 적어도 까다 롭습니다.

... 그리고 마지막으로 원하는 것은 로깅 설정에서 잘못된 가정입니다.

응용 프로그램과 레이어에 관심이 있고 분리 지점이 쉬운 경우 NDC가 적합합니다. 코드가 때때로 약간 과도 할 수 있지만 Foo.bar ()가 레이어 Y의 응용 프로그램 X에서 호출되었음을 보여주는 정확한 컨텍스트 스택에 의해 얼마나 많이 저장되었는지 모르겠습니다.


가장 많이 사용되는 전략은 클래스별로 로거를 만드는 것입니다. 새 스레드를 만들면 유용한 이름을 지정하여 로깅을 쉽게 구분할 수 있습니다.

클래스별로 로거를 생성하면 클래스의 패키지 구조에서 로깅을 켜고 끌 수 있다는 이점이 있습니다.

log4j.logger.org.apache = INFO
log4j.logger.com.example = DEBUG
log4j.logger.com.example.verbose = ERROR

위의 코드는 모든 아파치 라이브러리 코드를 INFO레벨로 설정 DEBUG하고 verbose 패키지를 제외하고 로깅을 자신의 코드에서 레벨로 전환 합니다.


이것이 최선의 방법은 아니라고 확신하지만, 코드 줄을 절약하기 전에 응용 프로그램 시작 시간을 절약했습니다. 특히 붙여 넣을 때 :

Logger logger = Logger.getLogger(MyClass.class);

... 개발자는 종종 "MyClass"를 현재 클래스 이름으로 변경하는 것을 잊고 여러 로거가 항상 잘못된 위치를 가리 킵니다. 이것은 나쁘다.

나는 가끔 다음과 같이 썼다.

static Logger logger = LogUtil.getInstance(); 

과:

class LogUtil {
   public Logger getInstance() {
      String callingClassName = 
         Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName();
      return Logger.getLogger(callingClassName);
   }
}

해당 코드의 "2"는 틀렸을 수 있지만 요점은 있습니다. 개발자가 실제로 이것을 잘못 입력하거나 오류를 발생시키는 방법을 갖지 않도록 (클래스로드시, 정적 변수로) 성능 저하를 가져옵니다.

나는 일반적으로 런타임에 개발자 오류를 방지하기 위해 성능을 잃는 것에 감격하지 않지만 싱글 톤으로 발생하면 한 번? 종종 나에게 좋은 거래처럼 들립니다.


다른 사람들이 말했듯이 클래스별로 Logger를 만들 것입니다.

private final static Logger LOGGER = Logger.getLogger(Foo.class);

또는

private final Logger logger = Logger.getLogger(this.getClass());

그러나 과거에는 로거에 다른 정보가있는 것이 유용하다는 것을 알았습니다. 예를 들어 웹 사이트가있는 경우 모든 로그 메시지에 사용자 ID를 포함 할 수 있습니다. 이렇게하면 사용자가 수행하는 모든 작업을 추적 할 수 있습니다 (문제 디버깅 등에 매우 유용함).

이를 수행하는 가장 쉬운 방법은 MDC를 사용하는 것이지만 사용자 ID를 포함하는 이름으로 클래스의 각 인스턴스에 대해 생성 된 Logger를 사용할 수 있습니다.

MDC 사용의 또 다른 장점은 SL4J를 사용하는 경우 MDC의 값에 따라 설정을 변경할 수 있다는 것입니다. 따라서 DEBUG 수준에서 특정 사용자의 모든 활동을 기록하고 다른 모든 사용자는 ERROR 상태로 두려면 할 수 있습니다. MDC에 따라 다른 출력을 다른 위치로 리디렉션 할 수도 있습니다.

유용한 링크 :

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html

http://www.slf4j.org/api/index.html?org/slf4j/MDC.html


  • 클래스 당 하나의 로거를 만듭니다.
  • Commons Logging 이 필요한 종속성이있는 경우 (아마도) Commons Logging에 slf4j의 브리지사용하십시오 . Commons Logging 인터페이스를 사용하여 로거 (클래스 당)를 인스턴스화합니다.private static final Log log = LogFactory.getLog(MyClass.class);
  • 바로 가기를 사용하여 IDE에서이 패턴을 나타냅니다. 이를 위해 IDEA의 라이브 템플릿 을 사용합니다.
  • NDC (문자열의 스레드 로컬 스택) 또는 MDC ( 문자열의 스레드 로컬 맵 →?)를 사용하여 스레드에 컨텍스트 정보를 제공합니다 .

템플릿의 예 :

private static final Log log = LogFactory.getLog($class$.class); // live template 'log'

if (log.isDebugEnabled())
    log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ...

또 다른 옵션 : 로깅시 AspectJ 크로스 컷팅을 시도 할 수 있습니다 . 여기에서 확인 하십시오. 로깅을 단순화하십시오 . ( AOP 를 사용하지 않으려면 slf4j 를 볼 수 있습니다 )

//Without AOP

    Class A{
       methodx(){
        logger.info("INFO");
       }
    }

    Class B{
       methody(){
        logger.info("INFO");
       }
    }

//With AOP

    Class A{
       methodx(){
         ......
       }
    }

    Class B{
       methody(){
         ......
       }
    }

    Class LoggingInterceptor{

       //Catched defined method before process
       public void before(...xyz){
         logger.info("INFO" + ...xyz);
       }

       //Catched defined method after processed          
       public void after(...xyz){
         logger.info("INFO" + ...xyz);
       }
       .....

    }

추신 : AOP 가 더 좋을 것입니다. DRY (Do n't Repeat Yourself) 방식입니다.


클래스 이름에 연결되지 않은 사용자 정의 로거를 만드는 가장 쉽고 쉬운 방법은 다음과 같습니다.

// create logger
Logger customLogger = Logger.getLogger("myCustomLogName");

// create log file, where messages will be sent, 
// you can also use console appender
FileAppender fileAppender = new FileAppender(new PatternLayout(), 
                                             "/home/user/some.log");

// sometimes you can call this if you reuse this logger 
// to avoid useless traces
customLogger.removeAllAppenders();

// tell to logger where to write
customLogger.addAppender(fileAppender);

 // send message (of type :: info, you can use also error, warn, etc)
customLogger.info("Hello! message from custom logger");

이제 같은 클래스에 다른 로거가 필요하면 문제 없습니다. :) 새 로거를 만드십시오.

// create logger
Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName");

이제 위의 코드를보고 새 fileappender를 만들어 출력이 다른 파일로 전송되도록합니다.

이것은 (적어도) 두 가지 상황에 유용합니다.

  • 정보에서 별도의 오류를 원하고 경고 할 때

  • 여러 프로세스를 관리하고 각 프로세스의 출력이 필요한 경우

추신. 질문이 있습니까? 편하게 물어보세요! :)


Common convention is "a logger pr class and use the class name as its name". This is good advice.

My personal experience is that this logger variable should NOT be declared static but an instance variable which is retrieved for each new. This allows the logging framework to treat two calls differently depending on where they come from. A static variable is the same for ALL instances of that class (in that class loader).

Also you should learn all the possibilities with your logging backend of choice. You may have possibilities you did not expect possible.


When deploying multiple EARs / WARs, it may be better to package the log4j.jar higher up in the classloader hierarchy.
i.e. not in WAR or EAR, but in the System-classloader of your container, otherwise multiple Log4J instances will write to the same file concurrently leading to strange behaviour.


If your application is following SOA principles, for every service A you'll have the following components:

  1. A Controller
  2. A Service Implementation
  3. A Executor
  4. A Persistance

So it makes life easier to have a aController.log aService.log aExecutor.log and aPersistance.log

This is a layer based separation so all your Remoting/REST/SOAP classes will write to the aController.log

All your scheduling mechanism, backend service etc will write to aService.log

And all task executions are written to aExecutor.log and so on.

If you have a multi-threaded executor you might have to use a log accumulator or another technique to properly align log messages for multiple threads.

This way you'll always have 4 log files which is not alot and not too less, I'm telling you from experience this makes life really easier.

참고URL : https://stackoverflow.com/questions/1770076/log4j-strategies-for-creating-logger-instances

반응형