이 글은 2023년 2월 기준 기록입니다.
System.out.println으로 찍은 출력은 콘솔에만 남습니다. 로깅 체계를 갖춘 애플리케이션이라면 이 출력들이 로그 파일 바깥으로 새어 나가는 셈입니다. 표준 출력 스트림 자체를 로거로 갈아 끼우면 이 구멍을 막을 수 있습니다.
OutputStream을 상속해 로거로 흘려보냅니다
핵심은 OutputStream을 상속한 클래스를 하나 만들고, 그것을 PrintStream으로 감싸 System.out·System.err 자리에 꽂는 것입니다.
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import org.slf4j.Logger;
public class LoggingOutputStream extends OutputStream {
public static void redirectSysOutAndSysErr(Logger logger) {
System.setOut(new PrintStream(new LoggingOutputStream(logger, LogLevel.INFO)));
System.setErr(new PrintStream(new LoggingOutputStream(logger, LogLevel.ERROR)));
}
private final ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
private final Logger logger;
private final LogLevel level;
public enum LogLevel {
TRACE, DEBUG, INFO, WARN, ERROR,
}
public LoggingOutputStream(Logger logger, LogLevel level) {
this.logger = logger;
this.level = level;
}
@Override
public void write(int b) {
if (b == '\n') {
String line = baos.toString();
baos.reset();
switch (level) {
case TRACE:
logger.trace(line);
break;
case DEBUG:
logger.debug(line);
break;
case ERROR:
logger.error(line);
break;
case INFO:
logger.info(line);
break;
case WARN:
logger.warn(line);
break;
}
} else {
baos.write(b);
}
}
}
줄바꿈을 만날 때까지 버퍼에 모읍니다
OutputStream이 요구하는 것은 바이트 하나를 쓰는 write(int b) 하나입니다. 다만 로거는 바이트가 아니라 줄 단위로 기록합니다. 그 간극을 ByteArrayOutputStream이 메웁니다.
들어온 바이트가 줄바꿈('\n')이 아니면 그대로 baos에 쌓아 둡니다. 줄바꿈을 만나는 순간 baos.toString()으로 한 줄을 완성해 꺼내고, baos.reset()으로 버퍼를 비워 다음 줄을 받을 준비를 합니다. println 한 번이 로그 한 줄에 대응되는 이유입니다.
완성된 줄은 인스턴스가 들고 있는 LogLevel에 따라 logger.trace·debug·error·info·warn 중 하나로 넘어갑니다. 레벨은 생성자에서 한 번 정해지고 그 뒤로 바뀌지 않으므로, 하나의 스트림은 하나의 레벨만 담당합니다.
표준 출력과 표준 에러를 각각 다른 레벨로 꽂습니다
교체는 redirectSysOutAndSysErr 한 번의 호출로 끝납니다.
System.setOut(new PrintStream(new LoggingOutputStream(logger, LogLevel.INFO)));
System.setErr(new PrintStream(new LoggingOutputStream(logger, LogLevel.ERROR)));
System.out은 INFO 레벨을 쓰는 인스턴스로, System.err는 ERROR 레벨을 쓰는 인스턴스로 각각 교체합니다.
LoggingOutputStream은 OutputStream일 뿐이라 그대로 System.setOut에 넣을 수 없고, PrintStream으로 한 번 감싸야 println 계열 메소드를 갖춘 표준 출력 자리에 들어갑니다.
이 호출 이후로 코드 어디에서 System.out.println을 부르든, 그 출력은 넘겨받은 Logger를 거쳐 나갑니다.
'IT·테크 > 개발' 카테고리의 다른 글
| Project Euler 004 알고리즘 문제 Java (0) | 2026.07.29 |
|---|---|
| Docker Swarm — 여러 도커 호스트를 하나의 클러스터로 묶기 (0) | 2026.07.28 |
| Java 멀티 스레드 — Thread와 Runnable로 작업 만들고 synchronized로 지키기 (0) | 2026.07.28 |
| Java 입출력 — 바이트·문자 스트림과 보조 스트림으로 기능 얹기 (0) | 2026.07.28 |
| Java JDBC — 드라이버 로드부터 Statement·PreparedStatement·ResultSet까지 (0) | 2026.07.28 |