개발 일기

[Java] 로그(Log)/로깅(Logging) 그리고 Java 로깅 프레임워크들 본문

Back-End/Java

[Java] 로그(Log)/로깅(Logging) 그리고 Java 로깅 프레임워크들

개발 일기장 주인 2024. 5. 22. 14:30

 

적용하게된 계기

힘겹게 Nginx와 스프링 프로젝트를 git actions로 배포를 하여 실행을 시켜놓고나니까 그제서야 든 생각이 있었다. 그래서 로그는 어떻게 확인할건데? 내가 매번 docker logs를 찍어서 봐야하나..? 근데 이 로그 양이 많아지면 어떻게 될까? 라는 생각이 문득  들었다. Express로 서비스를 했을때는 nohup이라는 모듈을 통해 파일로 저장했던 기억이 있어서 찾아봤더니 역시 Spring Boot에서도 이런 것을 담당해주는 라이브러리가 존재해서 우선 자바 진영 로깅 프레임워크를 쭉 훑어보겠다.


로깅(Logging)

print(), System.out.println(), console.log() 등으로 개발하는 중간 중간에 로그를 열심히 찍어서 내가 의도한대로 동작하고 있는지 확인하기 위해 썼다.

그러나 스프링 부트에서 래퍼런스를 참고하다보면 Log.d()와 같이 Log 객체를 써서 로그를 남기는 경우도 있었다.

이러한 작업들을 로깅(Logging)이라고 하며 시스템이 동작할 때 시스템의 상태 및 동작 정보를 시간 경과에 따라 기록하는 것을 의미한다. 

그러나 이때 적절한 Level의 로그 기준을 너무 높게 설정하면 원하는 로그를 놓칠 수 있고(의미 있는 로그를 놓칠 수 있음) 그렇다고해서 기준을 너무 낮게 설정하게 되면 너무 많은 양의 로그가 쌓이는 문제점이 발생하여 그 적당한 지점을 찾는 것이 중요하다.

 

자바에서의 로깅

자바는 Log4j, Logback, JUL, JCL 및 slf4j등등 굉장히 많은 로그 관련 프레임워크와 라이브러리가 존재한다. 하나씩 살펴보자.

 

1. System.out.println()

로깅 프레임워크가 존재하지 않던 자바 초창기에 System.out.println()과 System.err.println()을 썼다고 한다.

사실 처음 접하게 됐을때 많이 쓰기도 하고 사실 너무 간단하다.
그러나 구글링을 해보니 지양을 해야한다고 한다. 이유는 아래와 같다.

  •  (휘발성) 로그 내용이 표준 출력으로 출력되어 실제 배포환경에서 사용 시 휘발된다. 그래서 에러를 잡아내고 그것을 수정하기 위해서는 파일로 기록되어야 하지만 그렇지 못하기 때문에 로그의 역할을 완벽하게 해낼 수 없다.
  • (로그 레벨 지정 X) 로그 레벨을 지정할 수 없다.
  • (성능 저하) System.outPrintStream 클래스의 객체인데 그중 println()과 같은 메소드를 살펴보면 "Synchronized"를 포함하고 있다.  이때 PrintStream 객체에 대한 락을 획득하고 하나의 쓰레드가 작업을 완료하면 락을 해제한다. 이는 여러 스레드가 동시에 System.out.println()을 호출할 때, 하나의 스레드만이 println() 메서드를 실행할 수 있고, 다른 스레드는 앞의 스레드가 작업을 완료하고 락을 해제할 때까지 대기해야 한다는 것을 의미하여 잘 못했다가는 많은 Thread들이 이 로그를 남기기 위해 Block될 수도 있다.

PrintStream.class

2. Log4j 탄생

이러한 문제로 인해서 ceki Gulcü가 2001에 Log4j를 개발하여 오픈소스로 제공했다.

  • 여기서 말하는 Log4j는 Log4j2와는 구분된다.
  • 이것으로 Appender, Level, Logger라는 개념이 탄생됐다.
  • 다양한 로그 레벨을 설정할 수 있게 됐고 다양한 Appender를 커스텀하여 사용해 로그 수준 별 저장 방식도 커스텀 가능했다.
  • 레거시 프로젝트에서 많이 사용했지만 2015년 이후로 업데이트가 없어서 요즘은 거의 쓰지않는다.

3. JCL 

Log4j와 함께 JUL(Java Util Logging)도 만들어 졌는데 이렇게 몇몇 로그 프레임워크가 만들어 지고 나서 문제가 서로 호환되지 않아 로깅 프레임워크를 변경하면 소스 코드를 모두 변경해줘야하는 문제점이 있었다.

그래서 Apache에서 Logging 시스템의 Facade Interface로 JCL (Jakarta Common Logging)을 만들어 탄생하게 됐다.

Facade Interface?
소프트웨어 디자인 패턴인 "Facade Pattern"에서 사용되는 인터페이스를 의미한다. Facade 패턴은 복잡한 서브시스템을 단순화하고, 클라이언트가 이 서브시스템을 쉽게 사용할 수 있도록 간단한 인터페이스를 제공하는 구조적인 디자인 패턴

 

JCL은 로깅의 구현체가 변경돼도 기존 소스코드엔 영향이 없도록 하는 것이 목표였다.

그러나 로그레벨 처리, 문자열 연산 등 다양한 문제가 발생하였다.

 

4. slf4j

위의 문제점들로 Log4j를 만든 ceki Gulcü가 모든 로깅 프레임워크의 표준 인테페이스 스펙인 slf4j (Simple Logging Facade for Java)를 개발했다.

slf4j는 JUL, Log4j, Log4j2, Logback 등 다양한 로깅 프레임워크에 대한 인터페이스 역할을 수행한다.

 

5. Logback과 Log4j2

  • ceki Gulcü는 slf4j를 개발하던 비슷한 시기에 Log4j의 후속 프로젝트로 Logback을 개발했다. slf3j의 구현체이며 많이 쓰이는 구현체로 실제 스프링 부트는 slf4j + Logback 조합을 디폴트로 한다.
  • Log4j2는 가장 최신 프레임워크이고 이 또한 slf4j의 구현체이다. Apache에 따르면 멀티 스레드 환경에서 비동기 로거인 Async Logger의 처리량이 대략 18배정도 높고 대기 시간이 더 짧다고 한다.
    그러나 보안 취약점
    이 발생하여 대상 컴퓨터의 모든 권한을 취득하여 피해를 받을 수 있다는 뉴스가 있었다.