배경
자바 스프링 -> 코틀린 스프링으로 갈아타면서, Slf4j을 당연하게 사용했는데 코틀린은 롬북을 잘 사용하지 않기도 하고, 이참에 다른 로거와의 성능을 비교해서 써보자 하고 다른 로그 작성 방법을 찾아보게 되었다.
로거 종류
SLF4J 기본 parameterized
- 로그 수준에 따라서 로그가 남겨진다. 즉 logger.info, logger.debug로 지정하면 레벨에 따라 표시 여부가 졀정된다.
- 문제점은 로그가 현재 로그 수준에 해당하지 않으면 남겨지지 않는데, 로그에서 호출하는 객체의 메서드는 그대로 호출된다.
- 즉 비용이 있는 메서드면 쓸데없는 성능 저하로 이어진다.
- String 결합만 막힐 뿐, 인자 평가 자체는 막지 못한다 → 불완전한 Lazy
- Object, 비싼 메서드는 실행이 되긴 한다는 것
해결..?
- 람다를 사용하면 로그 수준 조건문을 사용하지 않아도 성능 영향 없이 가능하다.
-> but Object 가변인자만 받을 수 있고, 람다를 사용할 방법은 없다.. 결국 조건문 사용해야한다.
해결 시도 1 - 조건문
if (logger.isDebugEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("{}", foo.veryExpensiveMethod());
}
-> 문장마다 매번 체크 붙여줘야 해서 귀찮다..
SLF4J Supplier (2.0+)
logger.debug("{}", () -> foo.veryExpensiveMethod());
- 이렇게 하면 인자를 람다로 감싸게 됨, 조건문에 걸려야 실행된다.
- 그렇지만 인자를 람다로 감싸야 진짜 Lazy 방법이다.
- 그런데 인자를 감싸면 인자가 많을수록 람다도 여러 개 필요해서 아주 번거롭다.
Kotlin-logging
사용 방법
logger.debug { "${foo.veryExpensiveMethod()}" }
- 위의 문제 해결하려면 인자마다 람다로 감싸야 하는데, 코틀린에서는 함수 자체가 람다를 인자로 받는 구조라서 해결된다.
- {} 안의 코드 전체가 하나의 람다 블록이다. 함수 호출을 포함한 문자열 생성 자체가 람다 안에 있어서, debug 레벨이 활성화됐을 때만 블록 전체가 실행된다.
내부적으로 if (isDebugEnabled) logger.debug(msg().toString()) 으로 동작한다.
결론
Kotlin-logging은 조건문과 SLF4J Supplier와 동일한 효과를 가지면서, 매번 조건문을 쓰지 않아도 되고 인자마다 람다를 감싸지 않아도 된다.