실무에서는 System.out.println()과 같은 시스템 콘솔을 사용해서 필요한 정보를 출력하거나 디버깅하지 않고, 별도의 logging 라이브러리를 사용하여 디버깅이나 타임스탬프 등 정해진 양식에 맞추어 화면 상이나 파일 로그를 남길 목적으로 사용합니다.
이번 게시글은 목차는 아래와 같습니다.
1. logging의 종류
logging 관련 프레임워크는 대표적으로 log4j, logback, log4j2, 그리고 그것을 통합해서 인터페이스로 제공하는 SLF4J 라이브러리가 있습니다. log4j -> logback -> log4j2 순서로 등장하였으며, logback과 log4j는 둘 다 log4j를 기반으로 하고 있기 때문에 설정이나 사용 방법이 유사합니다. log4j는 2015년 8월 5일 이후로 지원이 종료했기 때문에 이를 제외하고 알아보겠습니다.
logback
1. log4j이후에 출시된 보다 향상되고 가장 널리 사용되고 있는 Java logging 프레임 워크 중 하나입니다.
2. slf4j의 구현체로써 동작하기에 Spring Boot환경에서는 spring-boot-starter-web안에 spring-boot-starter-loggin 의 logback이 기본적으로 포함되어 있어서 별다른 dependency 추가 없이 사용할 수 있습니다.
log4j2
1. 가장 최신에 나온 logging 프레임워크로써 Apache log4j의 다음 버전입니다.
2. logback처럼 필터링 기능과 자동 리로딩을 지원합니다.
3. logback과 가장 큰 차이는 Multi Thread 환경에서 비동기 로거(Async Logger)의 경우 다른 logging 프레임워크보다 처리량이 훨씬 많고, 대기시간이 훨씬 짧습니다.
4. Java8부터 도입된 람다식을 지원합니다.
아래 이미지는 Multi Thread 환경에서 logging 프레임워크의 성능을 비교한 차트입니다.
https://logging.apache.org/log4j/2.x/performance.html
2. Spring Boot에서의 logging 사용 방법
그렇다면 코드를 통해 어떻게 logging을 사용하고, 어떻게 이루어져 있는지 한번 살펴보겠습니다.
package springboot.MVCBasic.basic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LogTestController {
private final Logger log = LoggerFactory.getLogger(getClass());
@GetMapping("log-test")
public String logTest() {
String name = "Spring";
log.trace("trace log = {}", name);
log.debug("debug log = {}", name);
log.info("info log = {}", name);
log.warn("warn log = {}", name);
log.error("error log = {}", name);
return "ok";
}
}
보시는 바와 같이 log를 사용하기 위해서는 먼저 선언을 해주어야 합니다. 그런 다음 log의 종류에는 trace, debug, info, warn, error이 있는데 각각의 의미는 아래 표와 같습니다. (보통 개발 서버는 debug, 운영 서버는 info로 사용합니다.)
Level | Color | Mean |
1. error | Red | 사용자 요청을 처리하는 중 발생한 문제 |
2. warn | Yellow | 처리 가능한 문제이지만, 향후 시스템 에러의 원인이 될 수 있는 문제 |
3. info | Green | 로그인이나 상태 변경과 같은 정보성 메시지 |
4. debug | Green | 개발시 디버깅 목적으로 출력하는 메시지 |
5. trace | Green | debug 보다 좀 더 상세한 메세지 |
특정 로그 레벨을 지정하면 해당 로그 레벨의 상위 우선순위 로그가 모두 출력됩니다. 예를 들어 특정 로그 레벨을 info로 지정하면 info, warn, error 로그가 전부 출력됩니다.
즉 특정 로그 레벨이 info라면 아래와 같이 출력됩니다.
2024-01-10 17:38:15.537 INFO 31713 --- [nio-8080-exec-1] LogService
2024-01-10 17:38:15.537 ERROR 31713 --- [nio-8080-exec-1] LogService
2024-01-10 17:38:15.537 WARN 31713 --- [nio-8080-exec-1] LogService
로그 레벨의 default 값은 info이며 로그 레벨을 설정하는 방법은 아래와 같습니다.
1. application.yml에서 설정할 때
# 패키지와 그 하위 로그 레벨 설정 (디폴트 값은 info)
logging:
level:
springboot.MVCBasic: debug #springboot.MVCBasic 패키지의 하위 레벨 설정
2. application.properties에서 설정할 때
#전체 로그 레벨 설정(기본 info)
logging.level.root=info
#hello.springmvc 패키지와 그 하위 로그 레벨 설정
logging.level.hello.springmvc=debug
우리는 위 예제 코드에서 private final Logger log = LoggerFactory.getLogger(getClass()); 를 선언해준 것을 떠올릴 수 있습니다. 사실 lombok의 @slf4j 애노테이션만 선언해준다면 아래와 별도로 선언해 주지 않고도 사용할 수 있습니다.
package springboot.MVCBasic.basic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class LogTestController {
@GetMapping("log-test")
public String logTest() {
String name = "Spring";
log.trace("trace log = {}", name);
log.debug("debug log = {}", name);
log.info("info log = {}", name);
log.warn("warn log = {}", name);
log.error("error log = {}", name);
return "ok";
}
}
여기서 코드를 보다 보면 누군가는 log.trace("trace log = {}", name);처럼 사용하지 않고 log.trace("trace log" + name); 로 하면 안 되나?라고 의문이 드실 수도 있습니다. 이 부분에 대해서는 먼저 결론적으로 말씀드리자면 후자의 방식은 성능이 좋지 않습니다.
그 이유는 후자의 방식은 logging level에 포함되어 있지 않아서 출력할 필요가 없음에도 일단 문자열을 서로 더하는 연산이 수행되기 때문에 비용면에서 손해를 보기 때문입니다. 따라서 전자의 방식으로 사용하는 것을 권합니다.
3. System.out.println()을 사용하지 않고 logging을 사용하는 이유
그렇다면 왜 굳이 println문으로 찍으면 편할 것을 굳이 logging을 이용할까요? 그 이유는 logging을 사용했을 때는 아래와 같은 장점이 있기 때문입니다.
logging을 사용했을 때의 장점 (중요)
- 스레드 정보, 클래스 이름 같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있다.
- 로그 레벨에 따라 개발서버에서는 모든 로그를 출력하고, 운영서버에서는 출력하지 않는 등 로그를 상황에 맞게 조절할 수 있다.
- 시스템 아웃 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등, 로그를 별도의 위치에 남길 수 있다.
- 특히 파일로 남길 때에는 일별, 특정 용량에 따라 로그를 분할하는 것도 가능하다.
- println을 썼을 때보다 내부 버퍼링, 멀티 스레드 등의 환경에서 훨씬 좋다
참고
'BACKEND > 스프링 Spring' 카테고리의 다른 글
Spring MVC jQuery $ 사용 예제 (0) | 2024.01.14 |
---|---|
Spring MVC를 사용하여 Ajax 요청 (0) | 2024.01.14 |
인텔리제이 Port 8080 was already in use 해결 (0) | 2024.01.09 |
spring 게시판 인텔리제이로 Ajax 댓글 기능 구현 (1) | 2024.01.04 |
spring 게시판 인텔리제이로 페이징 처리 (1) | 2024.01.04 |