[java] log4j에서 로깅 전에 isDebugEnabled를 확인하면 성능이 향상됩니까?

로깅을 위해 응용 프로그램에서 Log4J 를 사용 하고 있습니다. 이전에는 다음과 같은 디버그 호출을 사용하고있었습니다.

옵션 1:

logger.debug("some debug text");

그러나 일부 링크는 isDebugEnabled()다음과 같이 먼저 확인하는 것이 좋습니다 .

옵션 2 :

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text");
}

제 질문은 ” 옵션 2가 어떤 식 으로든 성능을 향상 시킵니까? “입니다.

어쨌든 Log4J 프레임 워크는 debugEnabled에 대해 동일한 검사를 수행하기 때문입니다. 옵션 2의 경우 프레임 워크가 isDebugEnabled()여러 번 (각 호출마다) 메소드 를 호출 할 필요가없는 단일 메소드 또는 클래스에서 다중 디버그 명령문을 사용하는 것이 좋습니다 . 이 경우 isDebugEnabled()메소드를 한 번만 호출 하고 Log4J가 레벨을 디버그하도록 구성된 경우 실제로 isDebugEnabled()메소드를 두 번 호출합니다 .

  1. debugEnabled 변수에 값을 할당하는 경우
  2. 실제로는 logger.debug () 메소드에 의해 호출됩니다.

옵션 1에 따라 logger.debug()메소드 또는 클래스에서 여러 명령문을 작성하고 메소드를 호출 debug()하면 옵션 2와 비교하여 Log4J 프레임 워크에 대한 오버 헤드 가 아니라고 생각합니다 isDebugEnabled(). 매우 작은 메소드 (코드 측면에서)이므로 인라인을위한 좋은 후보자가 되십시오.



답변

이 경우 옵션 1이 더 좋습니다.

가드 문 (확인 isDebugEnabled())은 toString()다양한 객체 의 메소드를 호출 하고 결과를 연결하는 경우 로그 메시지의 잠재적으로 비싼 계산을 방지하기 위해 존재 합니다.

주어진 예에서, 로그 메시지는 상수 문자열이므로 로거가 로그를 버릴 수 있도록하면 로거의 사용 가능 여부를 확인하는 것만 큼 효율적이며 분기가 적기 때문에 코드의 복잡성이 줄어 듭니다.

더 좋은 방법은 로그 문이 형식 스펙과 로거가 대체 할 인수 목록을 취하는 로거가 사용 가능한 경우에만 “lazily”인 최신 로깅 프레임 워크를 사용하는 것입니다. 이것이 slf4j가 취하는 접근법 입니다.

자세한 정보는 log4j로 이와 같은 작업을 수행하는 예제와 관련 질문에 대한 답변을 참조하십시오 .


답변

옵션 1에서 메시지 문자열은 상수이므로 조건으로 로깅 명령문을 랩핑하는 데 절대적으로 이득이 없습니다. 반대로, 로그 명령문이 디버그 가능하면 isDebugEnabled()메소드 에서 한 번, 두 번 평가 됩니다. debug()방법. 호출 비용은 isDebugEnabled()5 ~ 30 나노초 정도이며 대부분의 실제 목적에는 무시할 수 있습니다. 따라서 옵션 2는 코드를 오염시키고 다른 이득을 제공하지 않기 때문에 바람직하지 않습니다.


답변

를 사용하면 isDebugEnabled()문자열을 연결하여 로그 메시지를 작성할 때 사용 됩니다.

Var myVar = new MyVar();
log.debug("My var is " + myVar + ", value:" + myVar.someCall());

그러나 귀하의 예에서는 문자열을 로깅하고 연결과 같은 작업을 수행하지 않으므로 속도가 향상되지 않습니다. 따라서 코드에 부풀림을 추가하고 읽기가 더 어려워집니다.

개인적으로 다음과 같이 String 클래스에서 Java 1.5 형식 호출을 사용합니다.

Var myVar = new MyVar();
log.debug(String.format("My var is '%s', value: '%s'", myVar, myVar.someCall()));

나는 많은 최적화가 있는지 의심하지만 읽기가 더 쉽다.

대부분의 로깅 API는 기본적으로 다음과 같은 형식을 제공합니다. 예를 들어 slf4j는 다음을 제공합니다.

logger.debug("My var is {}", myVar);

훨씬 더 읽기 쉽습니다.


답변

Java 8에서는 isDebugEnabled()성능을 향상시키기 위해 사용할 필요가 없습니다 .

https://logging.apache.org/log4j/2.0/manual/api.html#Java_8_lambda_support_for_lazy_logging

import java.util.logging.Logger;
...
Logger.getLogger("hello").info(() -> "Hello " + name);


답변

짧은 버전 : 부울 isDebugEnabled () 검사도 수행 할 수 있습니다.

이유 :
1- 복잡한 논리 / 문자열이 연결된 경우. 디버그 문에 추가되어 이미 체크인했습니다.
2- “복잡한”디버그 명령문에 대한 명령문을 선택적으로 포함 할 필요는 없습니다. 모든 진술은 그런 식으로 포함됩니다.
3- log.debug를 호출하면 로깅 전에 다음을 실행합니다.

if(repository.isDisabled(Level.DEBUG_INT))
return;

이것은 기본적으로 호출 로그와 동일합니다. 또는 고양이. isDebugEnabled ().

하나! 이것이 log4j 개발자들이 생각하는 것입니다 (javadoc에 있으므로 아마도 그것을 따라야합니다).

이것은 방법입니다

public
  boolean isDebugEnabled() {
     if(repository.isDisabled( Level.DEBUG_INT))
      return false;
    return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel());
  }

이것은 그것을위한 javadoc입니다

/**
*  Check whether this category is enabled for the <code>DEBUG</code>
*  Level.
*
*  <p> This function is intended to lessen the computational cost of
*  disabled log debug statements.
*
*  <p> For some <code>cat</code> Category object, when you write,
*  <pre>
*      cat.debug("This is entry number: " + i );
*  </pre>
*
*  <p>You incur the cost constructing the message, concatenatiion in
*  this case, regardless of whether the message is logged or not.
*
*  <p>If you are worried about speed, then you should write
*  <pre>
*    if(cat.isDebugEnabled()) {
*      cat.debug("This is entry number: " + i );
*    }
*  </pre>
*
*  <p>This way you will not incur the cost of parameter
*  construction if debugging is disabled for <code>cat</code>. On
*  the other hand, if the <code>cat</code> is debug enabled, you
*  will incur the cost of evaluating whether the category is debug
*  enabled twice. Once in <code>isDebugEnabled</code> and once in
*  the <code>debug</code>.  This is an insignificant overhead
*  since evaluating a category takes about 1%% of the time it
*  takes to actually log.
*
*  @return boolean - <code>true</code> if this category is debug
*  enabled, <code>false</code> otherwise.
*   */


답변

다른 사람들이 guard 문을 사용하여 언급했듯이 문자열을 만드는 것이 시간이 많이 걸리는 호출 인 경우에만 실제로 유용합니다. 이것의 구체적인 예는 문자열을 만들 때 게으른로드를 트리거하는 것입니다.

Java 또는 Simple SLF4J ( http://www.slf4j.org/manual.html )를 사용하여이 문제점을 피할 수 있습니다 . 이를 통해 다음과 같은 메소드 호출이 가능합니다.

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

디버그가 활성화 된 경우 전달 된 매개 변수 만 문자열로 변환합니다. 이름에서 알 수 있듯이 SLF4J는 외관 만이며 로깅 호출은 log4j로 전달 될 수 있습니다.

이 버전의 “자신의 롤”버전을 쉽게 만들 수도 있습니다.

도움이 되었기를 바랍니다.


답변

옵션 2가 더 좋습니다.

그 자체로는 성능이 향상되지 않습니다. 그러나 성능이 저하되지 않도록합니다. 방법은 다음과 같습니다.

일반적으로 logger.debug (someString);

그러나 일반적으로 응용 프로그램이 커짐에 따라 많은 손이 바뀌고 초보자 개발자는 eSP를 볼 수 있습니다.

logger.debug (str1 + str2 + str3 + str4);

등.

로그 레벨이 ERROR 또는 FATAL로 설정되어 있어도 문자열 연결이 발생합니다! 응용 프로그램에 문자열 연결과 함께 많은 DEBUG 수준 메시지가 포함되어 있으면 특히 jdk 1.4 이하에서 성능이 저하됩니다. (최신 버전의 jdk internall이 stringbuffer.append ()를 수행하는지 확실하지 않습니다).

이것이 옵션 2가 안전한 이유입니다. 문자열 연결조차도 발생하지 않습니다.