Programming Insight 2013.06.18 11:42


Java에서 사용되는 System.out의 경우 디폴트로 콘솔에 출력되도록 지정이 되어있다. Java를 처음 접할 때는 System.out이 마법의 키워드인양 무조건 변경이 불가능 하다라고 생각하는 경우가 있는데 이는 오산이다.

System.out을 잔뜩 사용해서 콘솔에 로그를 출력하던 프로그램이 있다. 그런데 만약 이것을 파일로 출력하는 방식으로 바꾸어야 한다면 어떻게 할 것인가? 단순히 생각하면 파일에 출력하는 유틸성 객체를 하나 만든다음 System.out을 사용한 모든 코드를 새로 만든 객체를 사용하는 방식으로 변경하면 될 것이다.

하지만 이보다 단순한 방법은 현재 콘솔에 출력을 하고 있는 System.out의 PrintStream을 파일에 출력하는 PrintStream으로 교체해 주는 방법이다. 이 방식을 사용한다면 기존에 System.out을 사용했던 코드들의 수정을 피할 수 있으며, 차후에 화면에 다시 출력하고 싶을 때에도 System.out을 사용하는 코드의 변경이 필요치 않다.

좀 더 구체적으로 설명하자면, System.out은 System 클래스의 정적 인스턴스인데, 이는 단순한 PrintStream 객체의 인스턴스이다. 따라서 이 PrintStream을 다른 곳에 출력을 하도록 구현되어 있는 인스턴스로 바꿔준다면, System.out을 콘솔이 아닌 다른 곳에 출력하도록 할 수 있다.

public class StdOutRedirect {
    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("log.txt");
        PrintStream printStream = new PrintStream(new FileOutputStream(file));

        // console로 출력
        System.out.println("Hello World!");
        System.err.println("ERROR!");
        
        PrintStream sysout = System.out;
        
        // standard out과 err을 file로 변경
        System.setOut(printStream);
        System.setErr(printStream);
        
        // file로 출력
        System.out.println("Hello World!");
        System.err.println("ERROR!");
        
        // standard out을 console로 다시 변경
        System.setOut(sysout);
        
        // out은 console로 출력, err은 파일로 출력
        System.out.println("Hello World!");
        System.err.println("ERROR!");
        
        printStream.close();
    }
}

위의 코드에서는 System.out은 콘솔에 출력하도록 두고 System.err은 로그파일에 출력하도록 하였다. 즉, 기본적인 내용은 화면에 출력하고, 중요한 에러들은 로그파일에 출력하도록 한 것이다. 만약 System.err을 콘솔과 로그파일 모두에 출력하고자 한다면, PrintStream이나 더 상위의 클래스인 OutputStream을 상속해서 여러곳의 stream에 출력하도록 구현을 하면 될 것이다. 이에 대한 예제코드는 이 곳에서 살펴볼 수 있다.

이처럼 단순한 방법으로 Java의 standard stream을 redirect하는 법을 살펴보았다. 하지만, 이러한 redirection을 사용하게 되면 다른 사람과 협업을 할 경우 매우 큰 혼란이 발생할 수도 있으므로 개인적으로는 이 방식을 추천하지 않는다. 가장 추천하고 싶은 방법은 개발의 처음단계부터 System.out을 사용하지 않는 것이다. System.out을 사용하지 않고 처음부터 다양한 곳에 출력을 할 수 있는 유틸성 클래스를 구현을 해놓고 처음부터 사용하는 것이다. 이러한 유틸성 클래스를 직접 구현하기 힘들거나 효율성이 걱정된다면 Apache Log4j나 JDK의 Logging API를 사용해도 무방하다. (개인적으로는 직접 구현하는 것보다는 이 방식을 추천한다. 그리고 JDK의 Logging API보다는 Apache의 Log4j를 사용하는 것을 추천한다)


2014.11.28 추가

예전에는 로깅에 대해서 잘 몰라서 Apache Log4j를 추천했지만 지금은 SLF4J 인터페이스에 Logback을 사용하는 것을 적극 권장한다. SLF4J는 Simple Logging Facade의 약자로써 Facade 패턴을 사용한 로깅 인터페이스이다. SLF4J를 사용할 경우 로그를 찍는 코드가 실제 로깅 라이브러리를 참조하지 않고 SLF4J에서 제공하는 Facade를 이용하기 때문에, 배포 시 로깅 라이브러리의 변경이 필요하더라도 코드의 수정없이 JAR만 교체하는 방식을 취할 수 있다는 장점이 있다.

신고
Trackback 0 Comment 3
  1. 양현식 2014.05.20 18:18 신고

    좋은 글 올려주셔서 감사합니다. 많은 도움이 되었습니다.

  2. justant 2014.11.07 18:06 신고

    좋은 포스팅 감사합니다 로그값찍어야 했는데,, ^^
    다 찍으니 20메가컼,,, 감사해요~!

  3. donggov 2016.04.03 14:23 신고

    좋은글 잘읽고 갑니다.