Java에서 디버그 출력을 처리하는 올바른 방법은 무엇입니까?


32

현재 Java 프로젝트가 점점 커짐에 따라 코드의 여러 지점에 디버그 출력을 삽입해야 할 필요성도 커지고 있습니다.

테스트 세션의 시작 또는 종료에 따라이 기능을 적절하게 활성화 또는 비활성화하기 위해 일반적으로 private static final boolean DEBUG = false테스트중인 클래스의 시작 부분에 a 를 배치하고 다음 과 같이 간단하게 사용합니다 (예 :).

public MyClass {
  private static final boolean DEBUG = false;

  ... some code ...

  public void myMethod(String s) {
    if (DEBUG) {
      System.out.println(s);
    }
  }
}

등.

그러나 그것은 저를 행복하게하지는 않습니다. 물론 작동하지만 DEBUG를 true로 설정하기에는 너무 많은 클래스가있을 수 있습니다.

반대로, 나는 (많은 다른 사람들처럼) 출력되는 텍스트의 양이 압도적이므로 전체 응용 프로그램을 디버그 모드로두기를 좋아하지 않을 것입니다.

그렇다면 이러한 상황을 아키텍처 적으로 처리하는 올바른 방법이 있습니까 아니면 DEBUG 클래스 멤버를 사용하는 것이 가장 올바른 방법입니까?


14
Java에서 올바른 방법은 로깅에 homebrew 코드를 사용하지 않는 것입니다. 기존 프레임 워크를 선택하고 휠을 재발 명하지 마십시오
gnat

언급 한 것과 같은 이유로 더 복잡한 클래스에서 부울 DEBUG를 사용합니다. 나는 일반적으로 전체 응용 프로그램을 디버깅하고 싶지 않고 클래스 만 문제를 낳습니다. 습관은 내 COBOL 시절에 DISPLAY 문이 사용 가능한 유일한 디버깅 형식이었습니다.
길버트 르 블랑

1
또한 가능한 경우 디버거에 더 의존하고 디버그 문으로 코드를 버리지 않는 것이 좋습니다.
앤드류 T 피넬

1
단위 테스트로 TDD (Test Driven Development)를 연습하십니까? 일단 그렇게하기 시작하면 '디버그 코드'가 크게 줄어 들었습니다.
JW01

답변:


52

로깅 프레임 워크와 로깅 파사드 프레임 워크를보고 싶습니다.

시간이 지남에 따라 많은 사람들이 공통 API에 의존하도록 진화했거나 외관 프레임 워크를 통해 사용을 추상화하고 교체 할 수 있도록 여러 로깅 프레임 워크가 종종 중복 기능을 갖습니다. 필요한 경우.

프레임 워크

일부 로깅 프레임 워크

  • Java Logging Framework (JDK의 일부),
  • Apache Log4J (비트는 오래되었지만 여전히 강력하고 적극적으로 유지됨)
  • LogBack은 (의 제작자 중 하나에 의해, Log4J를보다 더 현대적인 접근 방식을 제공하기 위해 만들어 Log4J를 ).

일부 로깅 파사드

용법

기본 예

이러한 프레임 워크의 대부분은 당신이 (여기에 사용하는 형태의 무언가를 작성할 수 있습니다 것입니다 slf4j-apilogback-core) :

package chapters.introduction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// copied from: http://www.slf4j.org/manual.html
public class HelloWorld {

  public static void main(String[] args) {
    final Logger logger = LoggerFactory.getLogger(HelloWorld.class);

    logger.debug("Hello world, I'm a DEBUG level message");
    logger.info("Hello world, I'm an INFO level message");
    logger.warn("Hello world, I'm a WARNING level message");
    logger.error("Hello world, I'm an ERROR level message");
  }
}

현재 클래스를 사용하여 전용 로거를 작성하면 SLF4J / LogBack이 출력을 형식화하고 로깅 메시지의 출처를 표시 할 수 있습니다.

SLF4J 매뉴얼 에서 언급했듯이 클래스의 일반적인 사용 패턴은 일반적으로 다음과 같습니다.

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

public class MyClass {

    final Logger logger = LoggerFactory.getLogger(MyCLASS.class);

    public void doSomething() {
        // some code here
        logger.debug("this is useful");

        if (isSomeConditionTrue()) {
            logger.info("I entered by conditional block!");
        }
    }
}

그러나 실제로 로거를 다음 형식으로 선언하는 것이 훨씬 일반적입니다.

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

이를 통해 정적 메소드 내에서 로거를 사용할 수 있으며 클래스의 모든 인스턴스간에 공유됩니다. 이것은 당신이 선호하는 형태 일 것입니다. 그러나 Brendan Long이 언급 한대로, 그 의미이해하고 적절하게 결정하기 를 원합니다 (이 관용구 다음의 모든 로깅 프레임 워크에 적용됨).

예를 들어 문자열 매개 변수를 사용하여 명명 된 로거를 작성하는 등 로거를 인스턴스화하는 다른 방법이 있습니다.

Logger logger = LoggerFactory.getLogger("MyModuleName");

디버그 레벨

디버그 수준은 프레임 워크마다 다르지만 공통적 인 수준은 중요합니다 (중요도 순, 악순환, 나쁜 수준, 매우 일반적이거나 매우 희귀 한 수준).

  • TRACE 매우 자세한 정보. 로그에만 작성해야합니다. 검사 점에서 프로그램 흐름을 추적하는 데만 사용됩니다.

  • DEBUG 자세한 정보. 로그에만 작성해야합니다.

  • INFO 주목할만한 런타임 이벤트. 콘솔에 즉시 표시되어야하므로 조금만 사용하십시오.

  • WARNING 런타임 이상과 복구 가능한 오류.

  • ERROR 기타 런타임 오류 또는 예기치 않은 조건

  • FATAL 조기 종료를 유발하는 심각한 오류.

블록과 가드

이제 많은 디버그 문을 작성하려는 코드 섹션이 있다고 가정 해보십시오. 이는 로깅 자체의 영향과 로깅 방법으로 전달할 수있는 매개 변수 생성으로 인해 성능에 빠르게 영향을 줄 수 있습니다.

이런 종류의 문제를 피하기 위해 종종 다음과 같은 형식으로 무언가를 작성하려고합니다.

if (LOGGER.isDebugEnabled()) {
   // lots of debug logging here, or even code that
   // is only used in a debugging context.
   LOGGER.debug(" result: " + heavyComputation());
}

메시지가 출력되지 않을 수 있지만 (예를 들어, 로거가 현재 INFO레벨 이상의 것만 인쇄하도록 구성된 경우) 디버그 명령문 블록 전에이 가드를 사용하지 않은 경우 heavyComputation()메소드가 여전히 실행 된 것입니다 .

구성

구성은 로깅 프레임 워크에 따라 다르지만 대부분 동일한 기술을 제공합니다.

  • 프로그래밍 방식 구성 (런타임에서 API를 통해- 런타임 변경 허용 )
  • 정적 선언적 구성 (시작시 일반적으로 XML 또는 특성 파일을 통해- 처음에 필요할 수 있음 )

또한 대부분 동일한 기능을 제공합니다.

  • 출력 메시지 형식 (타임 스탬프, 마커 등) 구성
  • 출력 레벨 구성
  • 세분화 된 필터 구성 (예 : 패키지 또는 클래스 포함 / 제외)
  • 로깅 할 위치 (콘솔, 파일, 웹 서비스 ...) 및 이전 로그 (예 : 자동 롤링 파일)로 수행 할 작업을 결정하기위한 어 펜더 구성.

다음은 logback.xml파일을 사용하는 선언적 구성의 일반적인 예입니다 .

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

언급 한 바와 같이, 이것은 프레임 워크에 따라 다르며 다른 대안이있을 수도 있습니다 (예를 들어, LogBack은 Groovy 스크립트 사용을 허용합니다). XML 구성 형식은 구현마다 다를 수 있습니다.

더 많은 구성 예는 다음을 참조하십시오.

역사적인 재미

것을 제발 참고 하여 Log4j 버전에서 전환, 순간에 주요 업데이트를보고있다 1.x에서2.X . 더 역사적인 재미 나 혼란을 모두보고 싶을 수 있으며 Log4J 를 선택 하면 2.x 버전을 선호합니다.

Mike Partridge가 언급했듯이 LogBack은 이전 Log4J 팀원이 작성했다는 점에 주목할 가치가 있습니다. Java Logging 프레임 워크의 단점을 해결하기 위해 작성되었습니다. 그리고 다가오는 주요 Log4J 2.x 버전 자체가 이제 LogBack에서 가져온 몇 가지 기능을 통합하고 있습니다.

추천

결론은, 가능한 한 많이 분리 된 상태로 유지하고, 몇 가지를 가지고 놀며, 자신에게 가장 적합한 것을 확인하십시오. 결국 그것은 단지 로깅 프레임 워크 입니다. 사용의 용이성과 개인 취향을 제외하고 매우 구체적인 이유가있는 경우를 제외하고는 이러한 방법 중 어느 것도 괜찮을 것이므로 그 위에 매달릴 필요가 없습니다. 대부분은 필요에 따라 확장 할 수도 있습니다.

여전히 오늘 조합을 선택해야한다면 LogBack + SLF4J로 갈 것입니다. 그러나 몇 년 후에 나에게 물었다면 Apache Commons Logging과 함께 Log4J를 추천 했으므로 종속성을 주시하고 함께 발전하십시오.


1
SLF4J 및 LogBack은 원래 Log4J를 작성한 사람이 작성했습니다.
마이크 파트 리지

4
: 로깅의 성능에 미치는 영향에 대해 걱정 될 수 있습니다 사람들을 위해 slf4j.org/faq.html#logging_performance
마이크 파트 리지

2
그것은 당신이 당신의 로거를해야하는지 여부 분명한 그렇게 아니라고 언급 할만큼 가치의 static이 적은 양의 메모리를 절약 할 수 있기 때문에이 있지만, 어떤 경우에 문제가 발생합니다 slf4j.org/faq.html#declared_static
분석 재개 모니카

1
@ MikePartridge : 링크의 내용을 알고 있지만 여전히 매개 변수 평가를 방해하지는 않습니다. 매개 변수화 된 로깅이 더 성능이 좋은 이유는 로그 메시지 처리가 발생하지 않기 때문입니다 (문자열 특히 연결). 그러나 메소드 호출이 매개 변수로 전달되면 실행됩니다. 따라서 사용 사례에 따라 블록이 유용 할 수 있습니다. 포스트에서 언급했듯이 DEBUG 레벨이 활성화되면 로깅뿐만 아니라 다른 디버그 관련 활동을 그룹화하는 데 유용 할 수 있습니다.
haylem

1
@ haylem-그건 내 실수입니다.
마이크 파트 리지

2

로깅 프레임 워크를 사용하십시오

대부분의 경우 정적 팩토리 메소드가 있습니다.

private static final Logger logger = Logger.create("classname");

그런 다음 다른 수준으로 로깅 코드를 출력 할 수 있습니다.

logger.warning("error message");
logger.info("informational message");
logger.trace("detailed message");

그런 다음 각 클래스에 대해 어떤 메시지를 로그 출력 (파일 또는 stderr)에 작성해야하는지 정의 할 수있는 단일 파일이 있습니다.


1

이것이 바로 log4j 또는 최신 slf4j와 같은 로깅 프레임 워크의 의미입니다. 이를 통해 로깅을 세부적으로 제어하고 응용 프로그램이 실행되는 동안에도 구성 할 수 있습니다.


0

로깅 프레임 워크는 확실히 갈 길입니다. 그러나 좋은 테스트 스위트도 있어야합니다. 테스트 범위가 좋으면 디버그 출력이 필요하지 않을 수도 있습니다.


로깅 프레임 워크를 사용하고 디버그 로깅을 사용할 수있는 경우 정말 나쁜 하루를 보내지 못할 때가 있습니다.
Fortyrunner

1
나는 당신이 로깅을해서는 안된다고 말하지 않았습니다. 먼저 시험이 필요하다고 했어
Dima
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.