원래 개발중에 문제가 생겨서 디버깅을 할 때 println 으로 중간 값들을 출력하곤 했다.
그래서 디버깅이 끝나면
적었던 출력문들을 하나하나 찾아다니며 지워야했고
그러다 가끔 놓쳐서 몇몇 출력문들이
PR 후 메인 브랜치에 병합까지 된 경우도 있었다.
이 문제는
스프링부트의 기본 로거인 Logback 프레임워크를 사용하면 해결할 수 있다!
그런데 사실 이미 프로젝트 내에서 Logback 프레임워크를 사용하고 있었다.
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.orm.jdbc.bind: TRACE
org.hibernate.orm.jdbc.extract: TRACE
org.springframework.security: DEBUG
이런 식으로 hibernate 와 security 의 로그 레벨을 설정해서 사용하고 있었는데
처음 배울 때 강사님께서 그냥 쿼리 결과를 확인할 수 있는 설정이라고만 알려주셔서
지금까지 hibernate 의 내부 기능을 사용하고 있는 줄 알았다!
해당 로깅 프레임워크의 로그 단계는 5가지로 이루어지는데
가장 심각한 내용부터 사소한 내용까지 나뉘어진다.
치명적인 오류가 발생했을 때 출력되는 로그레벨로,
즉시 조치가 필요한 상황을 나타낸다.
하지만 Logback 프레임워크에서는
사용할 수 있도록 설정되어 있지는 않다.
오류가 발생했을 때 출력되는 로그레벨로,
애플리케이션의 문제를 진단하는 데 사용된다.
log.error("주문 저장 실패! 주문 ID: {}", orderId);
경고성 메시지를 출력하는 로그레벨로,
당장의 오류는 아니지만 잠재적인 문제를 나타낸다.
log.warn("사용자가 잘못된 비밀번호를 여러 번 입력했습니다. ID: {}", userId);
일반적인 정보성 메시지를 출력하는 로그레벨로,
운영 환경에서도 유용한 정보를 출력한다.
log.info("사용자가 로그인했습니다. ID: {}", userId);
디버깅 목적으로 사용되는 로그레벨로, 개발 및 테스트 환경에서 유용하다.
디버깅 상황에서 println 말고 사용하는 것이 좋다.
log.debug("요청 파라미터: {}", requestParams);
가장 상세한 로그레벨로, 주로 개발 중에 사용된다.
위에 있는 hibernate 의 쿼리 결과를 확인하는 수준의 상세한 로깅이다.
log.trace("메서드 시작: processOrder() 실행");
이렇게 단계로 나뉘어져 있기 때문에 특정 로그 레벨을 설정할 수 있는데,
그러면 해당 단계보다 상위 단계의 로깅들이 출력되게 된다.
예를들어 출력 단계를 DEBUG 로 설정하면,
FATAL, ERROR, WARN, INFO, DEBUG 레벨의 로그가 모두 출력되는 것이다.
그래서 개발과 테스트 단계에서는 DEBUG나 TRACE 정도의 수준으로 사용할 수 있고,
실제 배포 및 운영 단계에서는 INFO 정도로 설정해 사용한다고 한다.
로깅을 사용하기 전에 가장 먼저 application.yml 에서 설정을 해야한다.
특별한 추가 설정을 하지 않으면 기본적으로는 INFO 단계로 설정되어 있어서
(FATAL,) ERROR, WARN, INFO 로그만 출력되는 것이다.
logging:
level:
root: DEBUG
이렇게 전체 프로젝트에 대해서 DEBUG로 설정하면
log.debug 로그도 출력될 수 있다.
하지만 실제 운영과정에서 출력되면 안되기 때문에
applicaion-dev.yml 과 applicaion-prod.yml 이렇게
설정 파일을 나눠서 설정하는게 바람직하다.
설정이 끝났다면 로직 내에서 사용하면 된다.
package com.example.demo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
// Logger 객체 생성
private static final Logger log = LoggerFactory.getLogger(MyController.class);
@GetMapping("/log")
public String logTest() {
log.trace("This is a TRACE level message");
log.debug("This is a DEBUG level message");
log.info("This is an INFO level message");
log.warn("This is a WARN level message");
log.error("This is an ERROR level message");
return "Logs are printed in the console!";
}
}
로거 객체를 생성한 다음 로깅을 하는 방식이다.
위에서 DEBUG 단계만 출력되도록 설정했기 때문에 위의 내용에서
log.trace("This is a TRACE level message"); 의 내용은 출력되지 않을 것이다.
package com.example.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j // @Slf4j 어노테이션을 사용하면 log 객체가 자동으로 생성됨
public class MyController {
@GetMapping("/log")
public String logTest() {
log.trace("This is a TRACE level message");
log.debug("This is a DEBUG level message");
log.info("This is an INFO level message");
log.warn("This is a WARN level message");
log.error("This is an ERROR level message");
return "Logs are printed in the console!";
}
}
Lombok 을 사용하면 컴파일시 로그객체를 생성하기에 간단하게 로깅을 할 수 있다.
Slf4j 는 Simple Logging Facade for Java 라고 하는데
사실 직관적으로 바로 생각나지는 않는 것 같다.
package com.example.demo
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class MyController {
// Logger 객체 생성
private val log: Logger = LoggerFactory.getLogger(MyController::class.java)
@GetMapping("/log")
fun logTest(): String {
log.trace("This is a TRACE level message")
log.debug("This is a DEBUG level message")
log.info("This is an INFO level message")
log.warn("This is a WARN level message")
log.error("This is an ERROR level message")
return "Logs are printed in the console!"
}
}
자바와 큰 차이는 없다.
그리고 이전 프로젝트에서 팀원 한분이
로그들을 모아서 파일로 정리하는 기능을 구현하신 적이 있었다.
이는 println 으로는 쉽게 구현할 수 없는 기능으로
서비스의 상태를 체크하는데 유용했다.
다음에 한번 직접 구현해봐야겠다.
또한 찾아보면서 로그를 남길 때
비동기로 할 수도 있다고 한다.
동기로 출력할 때
성능에 영향을 미칠 수 있어서
비동기로 배치처리하는 것이 가능하다는데
이것도 더 알아봐야겠다.
디버깅과 그 외의 개발을 하다보면
서비스의 현재 상태를 확인해야하는 일이 정말 많다.
그럴때 체계적인 로깅 프레임워크를 사용한다면,
효율적 효과적으로 확인할 수 있을 것이다.
특히 println 말고 log.debug 를 사용하자!