작성
·
99
1
저는 컴터 사양이 꽤나 좋아서 인지? 항상 표준출력이 더 빠르더군요.
알아보니 로깅 프레임워크가 느린 이유에는 대략 이런 이유들이 있네요.
로그 출력에 여러 과정을 거침. Logger → Appender → Encoder → Layout → OutputStream
레벨 설정에 의한 필터링
로그 포맷팅 처리
분명 표준출력이 synchronized
에 의해서 멀티 스레드 환경에서 취약한건 맞으나 현재 테스트에서는 그 부분이 로깅 프레임워크의 처리 과정보다는 빨랐던 것 같습니다.
그래서 테스트 환경을 바꿔서 테스트 해보았습니다.
멀티스레드 환경. 스레드 10개. 각 스레드별 100000번 출력
package kr.co.shortenurlservice.log;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class SoutVsAsyncLoggingTest {
private static final int THREAD_COUNT = 10;
private static final int LOG_COUNT = 100_000;
public static void main(String[] args) throws InterruptedException {
// 1. System.out.println() 테스트
long sysoutTime = runTest(i ->
System.out.println("[SYSOUT][" + Thread.currentThread().getName() + "] " + i)
);
// 2. log.info() (AsyncAppender + Console) 테스트
long asyncLogTime = runTest(i ->
log.info("[LOGGER][{}] {}", Thread.currentThread().getName(), i)
);
// 결과 출력
Thread.sleep(5000);
System.out.printf("System.out: %,d ms%n", sysoutTime);
System.out.printf("Async log : %,d ms%n", asyncLogTime);
}
private static long runTest(StringConsumer task) throws InterruptedException {
long start = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
for (int t = 0; t < THREAD_COUNT; t++) {
executor.submit(() -> {
for (int i = 0; i < LOG_COUNT; i++) {
task.accept(i);
}
latch.countDown();
});
}
latch.await();
executor.shutdown();
long end = System.currentTimeMillis();
return end - start;
}
@FunctionalInterface
interface StringConsumer {
void accept(int value);
}
}
또한 test/resources/logback.xml
설정으로 포맷을 날짜 같은것 제외하고 표준출력과 똑같이 맞춰주고 AsyncAppender
를 통해 멀티 스레드 환경에서 차별점이 있도록 비동기 Appender를 추가해줌.
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC_CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="CONSOLE"/>
<queueSize>10240</queueSize>
</appender>
<root level="info">
<appender-ref ref="ASYNC_CONSOLE"/>
</root>
</configuration>
이렇게 하니까 표준출력보다 빠르더군요.
꼭 성능문제뿐 아니라 로그 관리체계 자체가 로깅 프레임워크가 넘사이기 때문에 안쓸 비교불가겠지만..
성능적으로 보더라도 실무 환경이라면 메시지 출력 전후로 비즈니스 로직들이 있기 때문에 임계영역을 통해 스레드를 대시시키는 표준출력을 사용하는것보다는 로깅 프레임워크가 더욱 성능적으로 이점이 있을 것 같습니다.
답변 2
0
dlsrksrhk님 강의 내용에서 더 심화된 분석 글 감사합니다!
AsyncAppender에 대한 내용도 언급할까 하다가 환경에 따라 차이가 나지 않을 수도 있어서 다루지 않았는데, 테스트 해주신 것처럼 표준출력이 오히려 빠른 경우 AsyncAppender로 변경했을 때 표준출력보다도 빨라질 수 있습니다.
마지막에 이야기 해주신 것처럼, 성능 문제를 떠나서 로그 관리 차원에서 로깅 프레임워크 활용하는게 좋고, 실무 환경이라면 표준 출력이 경쟁적인 상태가 될 거라 로깅 프레임워크를 활용하는게 더 높은 성능을 보일겁니다. 😀
남겨주신 내용은 제가 강의 노트란에도 추가해두겠습니다~
감사합니다!!
0
안녕하세요, 인프런 AI 인턴입니다. dlsrksrhk님.
로그 출력 테스트 중 Slf4J(로깅 프레임워크)와 표준 출력(System.out.println)의 성능 차이를 평가하신 결과에 대한 설명을 감사합니다. 이와 관련하여 비슷한 주제로 논의된 기존의 질문과 답변을 참고하시면 좋을 것 같습니다.
표준 출력과 Slf4J의 성능 비교:
synchronized
키워드로 인해 멀티스레드 환경에서는 잠재적 성능 병목을 초래할 수 있습니다.Slf4J를 사용하는 이유:
AsyncAppender 추가 설정의 효과:
AsyncAppender
을 활용하여 로깅 작업을 비동기로 수행하면 로그 출력이 스레드의 주요 작업에서 비동시적으로 빠져나가게 되어 성능에 긍정적인 영향을 미칠 수 있습니다.위 답변이 도움이 되셨기를 바라며, 로깅 프레임워크의 성능 및 설정에 대한 이해를 도울 수 있기를 바랍니다. 추가적으로 궁금한 점이 있으시면, 질문을 이어서 남겨주세요.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 계속 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.