eelseungmin

System.out.print()로 로깅하지 않는 이유

by eelseungmin

들어가며

System.out.print(), System.out.println().

학부 시절 자바를 배울 때 굉장히 많이 사용한 메서드이기도 하고, 알고리즘 문제를 풀 때 아직도 많이 사용하는 메서드이다. 이 메서드가 하는 일은 전달한 인자를 문자열 형태로 콘솔에 출력하는 것이다.

그런데 최근 프로젝트의 로깅 작업을 하면서 문득 System.out.print()로 로깅하지 않는 이유가 궁금해졌다.

 

이유

1. 파일 형태로 남길 수 없다.

System.out.print() 메서드는 아무리 사용해도 콘솔에만 문자열이 출력될 뿐이다.

로깅을 하는 목적을 생각해 보면 이는 굉장히 치명적인 단점이다.

프로그램의 동작과 에러를 기록으로 남기고 이를 확인해서 문제를 해결해야 하는데, 콘솔에만 기록된다면 이를 디버깅에 활용하기 굉장히 힘들다. 혹시라도 애플리케이션이 종료되거나 뻗어버려서 재시작이 필요한 경우엔 다 날아가버릴 것이다. 수십 일 분량의 로그가 쌓여있다면 로그 관리는 더더욱 어려워진다.

 

로깅 프레임워크를 사용하면 파일 형태로 저장하는 기능뿐만이 아니라 날짜 별 파일 분리, 최대 용량, 최대 개수까지 설정이 가능하다.

 

2. 사용하기 불편하다.

// Slf4j
log.error("에러 발생");
          
// System.out.print
System.out.print(A);
System.out.print("에러 발생");
System.out.print(C);
...

위 예시를 보면 알 수 있듯이 로깅 프레임워크를 이용해서 로그를 남기면 두 번째 이미지와 같이 시간, 로그 레벨, PID, 스레드, 로깅된 메서드 이름...등등 다양한 정보를 특정 패턴으로 등록하여 모든 로그에 남도록 할 수 있다.

그러나 System.out.print()로 이러한 로그를 비슷하게 남기려고 해도 다수의 인자와 print()문이 필요하고, 이를 직접 작성하는 건 굉장히 피곤한 작업일 것이다.

 

로그 출력 레벨을 분리할 수 없는 것도 큰 단점이다. 상황에 따라 로그로 남기지 말아야 할 데이터나 특정 상황에선 의미가 없는 로그들을 출력 레벨을 이용해 로그가 남지 않도록 쉽게 제어할 수 있지만, print()는 전부 콘솔에 출력해 버린다.

 

3. 성능이 떨어진다.

// PrintStream.class
public void print(int i) {
        write(String.valueOf(i));
}

private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }

System.out.print() 메서드는 PrintStream 클래스에 구현되어 있고, write() 메서드를 이어서 호출한다. write 메서드는 syncronized 블록을 가지고 있다.

syncronized는 동기화를 위해서 사용하는 키워드인데, 간단히 설명하자면 객체들은 lock을 하나씩 갖고 있고, 객체 안에 해당 키워드를 사용하면 A 스레드에서 블록에 있는 명령이 실행될 때 객체의 lock을 획득한다. 이때 C나 D, 혹은 다른 스레드는 블록에 있는 명령이 A 스레드에서 모두 수행되기 전까지 lock을 획득할 수 없다.

 

즉, 블록에 있는 명령을 수행하고 싶으면 lock을 얻기 위해 기다려야 한다.

 

스프링에 내장된 톰캣은 멀티스레드로 여러 요청을 동시에 처리한다.

수많은 스레드가 전부 print()를 사용해서 하나의 스레드에서 print()를 처리하기를 기다려야 한다고 생각해 보자. 처리가 굉장히 느려질 것이다.

 

'Note > etc' 카테고리의 다른 글

웹소켓 알아보기  (0) 2024.09.08
Synchronous vs Asynchronous  (2) 2024.06.30
Block I/O vs Non-Block I/O  (0) 2024.06.23
SSR vs CSR  (0) 2024.04.06

블로그의 정보

eel.log

eelseungmin

활동하기