인생을 코딩하다.

[Spring] log4j, Lockback, log4j2 프로젝트에 무엇을 적용하는게 좋을까? 본문

Spring

[Spring] log4j, Lockback, log4j2 프로젝트에 무엇을 적용하는게 좋을까?

Hyung1 2021. 6. 13. 23:56
728x90
반응형

프로젝트에서 이메일 중복 체크 관련해서 이슈가 하나 있었습니다.
클라이언트에게는 json으로 "이미 존재하는 이메일입니다."라고 예외를 전환해서 JSON 통신으로 알려줬습니다.

그런데 생각해보니 실제 서비스를 돌리고 있다고 가정하면, 클라이언트는 알 수 있지만 정작 서버를 만든 저는 알 수가 없었습니다. 알기 위해서는 콘솔창에 찍혀야했습니다. 그래서 Log로 서버에도 기록을 해야겠다는 생각이 들었습니다.

추가로 개발도구를 24시간 실행하고 있진 않을터이니 추후 슬랙으로 정보를 받을 수 있도록 만들어야 할 것 같다는 생각도 들었습니다.. 슬랙은 컴퓨터 앞에 앉아있지 않아도 폰으로도 계속 확인할 수 있을터이니..

위에서 말한 이메일 중복체크 관련 외에도 동작상태나 상황등에 관해 디버깅이나 모니터링을 위해 정보를 꼭 기록해야한다고 생각을 합니다.

System.out.println()으로 정보를 기록하면?

우선 log가 아닌 단순히 System.out.println()으로 찍으면 문제가 있습니다.

PrintStream 클래스의 println()의 내부구조를 살펴보면,

synchronized가 붙어있는걸 확인할 수 있습니다.

그래서 이 것을 쓰면 다른 쓰레드는 Block이 걸리게 됩니다.. 그럼 다른 쓰레드는 일을 할 수가 없기 때문에 성능저하가 발생합니다.

System 로그는 보통 개발 때에만 사용하기 때문에 운영중인 코드에 System.out.println 메소드를 방치하면 I/O 요청이 발생할 때마다 쓸데없는 리소스를 잡아먹게 됩니다.

따라서 Log를 사용하기로 결정했는데, log를 찍기위해 제가 도입할 수 있는 선택지는 log4j, Lockback, log4j2 세 가지가 있었습니다.

무엇을 사용하는 것이 좋을까요? 고민해보았습니다.

Logback과 Log4j의 차이

  • log4j의 후속 버전입니다.
  • logback-classic은 SLF4J를 구현했습니다. logback-classic의 Logger 클래스는 SLF4J의 API 스펙을 구현하므로 SLF4J의 API를 그대로 사용하여도 되는 이점이 있습니다.
  • 아무래도 Logging은 콘솔이나 터미널로 표준입출력을 수행하기도 하지만 반영구적으로 보관하기 위해 .log 등의 File로 관리하기도 합니다.. 따라서 로깅시에 파일 입출력을 수행하는데, logback에서 로깅시 I/O 작업을 하다가 실패하는 경우 logback을 작동시키기 위해 어플리케이션을 재시작할 필요가 없습니다.. FileAppender 및RollingFileAppender의 하위 클래스들은 I/O 오류를 자동 복구할 수 있습니다.
  • logback은 log4j가 제공하는것보다 훨씬 많은 필터링 기능을 제공한다. 특정 사용자(특정 조건)에서의 로그레벨을 변경하는 등의 필터 정책을 사용할 수 있습니다.. (ex: A사용자가 로그인 한 경우에는 DEBUG 레벨로 로깅)
  • logback는 Groovy 코드를 통한 설정을 지원하는데, XML에서 사용하는 설정이 Groovy로 바뀌었을때 어떤식으로 사용되는지도 문서로 지원합니다.
  • logback은 세부적인 공식문서를 제공하며 지속적으로 업데이트됩니다.
  • XML설정뿐만 아니라 XML을 대신할 수 있는 그루비(Grooby) 설정을 지원합니다.. 또한 기존 XML설정을 그루비 설정으로 마이그레이션 하는 도구 또한 지원합니다.

Log4j2의 Logback의 차이

가장 최신에 나온 로깅 프레임워크로써 Apache의 log4j의 다음 버전입니다. logback처럼 필터링 기능과 자동 리로딩을 지원합니다. logback과의 가장 큰 차이는 Multi Thread 환경에서 비동기 로거(Async Logger)의 경우 다른 로깅 프레임워크보다 처리량이 훨씬 많고, 대기 시간이 훨씬 짧다. 또한, Java8부터 도입된 람다식을 지원하고, Lazy Evalutaion을 지원합니다.

Java8 람다 및 지연 평가 : 진정으로 필요한 경우에만 평가되는 람다 문 안에 로그 메시지를 래핑하는 API를 제공합니다.

가비지 관련

이전 버전의 Log4j를 포함한 많은 로깅 라이브러리는 정상 상태 로깅 중에 로그 이벤트 개체, 문자열, 문자 배열, 바이트 배열 등과 같은 임시 개체를 할당합니다. 이는 가비지 수집기에 부담을주고 GC 일시 중지가 발생하는 빈도를 증가시킵니다.

개체와 버퍼가 재사용되고 임시 개체가 가능한 많이 할당되지 않는 "가비지없는" 모드에서 실행된다. 완전히 가비지가 없지만 ThreadLocal 필드를 사용하지 않는 "낮은 가비지"모드도 있습니다. 이것은 Log4j 가 웹 애플리케이션에서 실행 중임을 감지 할 때 기본 모드 입니다.. 마지막으로 모든 가비지 프리 로직을 끄고 대신 "클래식 모드"에서 실행할 수 있습니다.

log4j2의 가비지없는 로깅은 부분적으로 ThreadLocal 필드의 개체를 재사용하고 텍스트를 바이트로 변환 할 때 버퍼를 재사용하여 부분적으로 구현됩니다.

일부 가비지 감소 기능은 ThreadLocals에 의존하지 않고 모든 애플리케이션에 대해 기본적으로 활성화됩니다. Log4j2에서는 중간 문자열, 문자 배열을 만들지 않고 텍스트를 재사용 된 ByteBuffer로 직접 인코딩하여 로그 이벤트를 텍스트로, 텍스트를 바이트로 변환 할 수 있습니다. 및 바이트 배열. 따라서 로깅이 웹 애플리케이션에 대해 완전히 가비지가없는 것은 아니지만 가비지 수집기에 대한 부담은 여전히 크게 줄일 수 있습니다.

logback과의 가장 큰 차이는 Multi Thread 환경에서 비동기 로거(Async Logger)와 관련한 경우입니다.

Log4j와 비교하여 비동기 로거를 사용해보면 평균 시간 및 처리량은 실제로 더 좋았고 엣지 케이스에서는 15K ops / ms 더 빠릅니다.


위의 여러가지 차이점을 설명해 보았습니다. 위의 차이점들을 고려한 결과와 더불어 저는 성능이 빠른 서비스를 만드는것을 원했기 때문에 비동기 환경에서 가장 성능적 이점을 얻을 수 있는 Log4j2를 사용하기로 결정했습니다.

Log4j2를 사용하기 위해선

첫 째,

configurations { all { exclude("org.springframework.boot", "spring-boot-starter-logging") } }

logback을 제거해줘야 합니다. 스프링에선 기본적으로 logback을 기본 설정으로 하기때문에 log4j2를 적용한다 할지라도 스프링부트의 기본설정인 logback 설정으로 로깅을 하게 됩니다.

둘 째,

build.gradle에서

implementation("org.springframework.boot:spring-boot-starter-log4j2")

한 줄만 추가해주면 됩니다. 위 의존성에는 SLF4J의 API 스펙까지 다 들어있기 때문에 위의 의존성만 추가해주면 됩니다.


현재 Log4j2 관련해서 보안적으로 큰 이슈가 있습니다. 사용하시지 마시고 혹시 사용하신다면 현재 이슈를 꼭 확인해보고 잘 대처하시길 바랍니다.


다음 글 : Log4j2의 로그레벨과 성능 향상에 관하여

참고 문헌 :
http://logging.apache.org/log4j/2.x/manual/garbagefree.html
https://minkwon4.tistory.com/161
https://nikoskatsanos.com/blog/2016/08/log4j2-vs-log4j/

728x90
반응형
Comments