본문 바로가기

Programming/SpringBoot

[SpringBoot] 로그 메시지 출력 SLF4J (@Slf4j) 로 디버깅하기

 


로그 메시지 출력 SLF4J (@Slf4fj) 로 디버깅하기


 

 

 

1. SLF4J (@Slf4j) 사용 이유


디버깅에 필요한 정보를 출력할 때 System.out.println(...) 을 사용하곤 했는데, 사실 이는 바람직한 방법은 아니다.

 

1. 코드 유지보수 어려움

1-1. 디버깅이 끝나면 println 출력문을 일일이 제거해야 하고

1-2. 많은 출력문이 코드 곳곳에 남아있으면 가독성이 저하된다.

 

2. 성능 문제

2-1. System.out.println() 은 동기적으로 실행된다. 이는 I/O (input/output) 작업이므로 속도 저하를 초래할 수 있다.

특히 반복문에서 많이 호출될 경우 성능 저하가 심해질 수 있다.

 

3. 출력 관리의 어려움

3-1. 모든 출력이 콘솔(console)에 섞여 나오므로 필요한 정보를 찾기 어렵다.

 

 

가장 간단하지만 여러 문제를 초래하는 System.out.println(...) 의 디버깅 대안으로는

1. JUnit 단위 테스트 구현

2. 로그 메시지 출력 등이 있다.

 

이 포스트에서는 두번째 방법인 로그 메시지 출력 기능 SLF4J 를 다룬다.

 

 

 

2. SLF4J 란?


SLF4J(Simple Logging Facade for Java)로깅 프레임워크에 대한 추상화(인터페이스) 역할을 하는 라이브러리다.

 

이게 무슨 소리냐.

Java 에서는 로그를 남길 때 log4j, logback, java.util.logging 같은 다양한 로깅 프레임워크를 사용할 수 있다.

그런데 프로젝트마다 다른 로깅 프레임워크를 쓰면 유지보수가 어려워진다. 

 

이런 문제를 해결하기 위해 로깅 프레임워크의 공통 인터페이스 역할을 해주는 라이브러리가 바로 SLF4J 다.

SLF4J 를 사용하는 코드에서는 Logger 인터페이스만 사용하고, 실제로 어떤 로깅 프레임워크를 쓸지는 설정만 바꾸면 된다.

 

 

 

3. SLF4J 사용하기


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Controller
public class LoggingController {
	
	private static final Logger log = LoggerFactory.getLogger(LoggingController.class);
	
	@GetMapping("/")
	public String index() {
		log.info("info 레벨 로그 테스트");
		return "logging";
	}
}

 

위 코드는 slf4j 의 기본틀이다.

사용할 로깅 프레임워크로 설정하기만 하면 바로 로깅 시스템을 사용할 수 있다.

 

 

Spring Boot 는 logback 을 기본 로깅 프레임워크로 사용하며,

기본적으로 INFO 레벨부터 출력된다.

 

로그 레벨 설명
error 에러 발생
warn 에러까지는 아니지만, 큰 성능 하락 등 위험한 상황
info 서버 시작, 연결 성공 등 중요한 이벤트
debug 디버깅을 위해 필요한 여러가지 정보 출력
trace 사소한 정보 출력

 

로그 레벨은 가장 심각한 error 단계부터 사소한 정보를 출력하는 trace 까지 존재한다.

기본 설정은 INFO 레벨부터 출력하기 때문에 debug 단계의 로그를 출력하기 위해선 application.properties 설정이 필요하다.

 

 

logging.level.root=info # 모든 패키지에 info 레벨 이상 로그 출력
logging.level.net.logging=debug # net.logging 패키지에 속한 클래스들만 debug 레벨 이상 로그 출력

 

전체 패키지에 로그 레벨을 설정할 수도 있고,

특정 패키지의 클래스에만 레벨을 설정할 수도 있다.

 

@GetMapping("/")
public String index() {
    log.info("info 레벨 로그 테스트");
    log.debug("debug 레벨 로그 테스트");
    return "logging";
}

 

 

 

디버깅이 끝나면 application.properties 에 작성한 debug 설정만 지우면 된다.

디폴트 설정이 info 이므로 log.debug(...) 출력은 무시되기 때문이다.

 

 

 

4. @Slf4j 사용하기


Lombok 에서 제공하는 @Slf4j 로깅 어노테이션 사용으로 더 간편한 설정을 할 수 있다.

클래스에 자동으로 @SLF4J 기반의 Logger 객체를 생성해준다.

 

@Controller
@Slf4j
public class LoggingController {
	@GetMapping("/")
	public String index() {
		log.info("info 레벨 로그 테스트");
		log.debug("debug 레벨 로그 테스트");
		return "logging";
	}
}

 

Logger 인터페이스 사용 대신 @Slf4j 어노테이션만 붙여주면 된다.

 

 

간단한 예제로..

form 데이터를 서버로 전송받고, log.debug() 를 통해 로그로 출력해보자.

 

/* edit.html */
<form method="post" th:object="${student}" class="shadow">
    <h1>학생 수정</h1>
    <div class="row">
      <label>학번:</label>
      <input type="text" th:field="*{studentNo}" />
    </div>
    <div class="row">
      <label>이름:</label>
      <input type="text" th:field="*{name}" />
    </div>
    ...
    <button type="submit" class="btn">저장</button>
    <span class="error" th:text="${ errorMsg }"></span>
</form>

 

웹브라우저에서 form 데이터에 위와 같이 입력 후 전송했다.

 

/* StudentController.java */
@PostMapping("edit")
public String edit(Model model, Student student) {
    try {
        log.debug(student.toString()); // 입력 받은 form 데이터 값(student) 출력
        if (StringUtils.hasText(student.getStudentNo()) == false)
            throw new Exception("학번을 입력하세요");
        if (StringUtils.hasText(student.getName()) == false)
            throw new Exception("이름을 입력하세요");
        if (student.getDepartmentId() == 0)
            throw new Exception("학과를 선택하세요");

        // student 객체를 DB에 저장하는 구현 생략
        return "redirect:list";
    }
    catch (Exception ex) {
        model.addAttribute("errorMsg", ex.getMessage());
        return "student/edit";
    }
}

 

서버에 데이터를 전송하면, 다음과 같은 debug 단계의 로그를 확인할 수 있다.