Logging with SLF4J and Logback
들어가며 > 왜 간단하게 System.out.print()으로 로그를 출력하지 않을까? 유연성을 위해서 선택 가능한 우선순위(`DEBUG`, `INFO`...) 수준 이상의 출력 메시지를 표시할 수 있음 모든 모듈이나 특정 모듈/클래스에 대해서만 메시지를 출력할 수 있음 이러한 메시지들의 형식을 어떻게 지정할지 제어할 수 있음 어디로 보낼지 결정할 수 있음 Logging Framework 네이티브인 `java.util.logging`은 잘 사용하지 않음 `Log4J` 몇 년간 사실상 표준(de facto)이었음 `Logback` Log4J의 개발자가 만든 후속작으로 현재 많은 프로젝트에서 사용됨 `SLF4J` (Simple Logging Facade for Java) Facade 패턴을 사용하는 인터페이스로 Log4J, Logback 등 실제 로깅 프레임워크의 공통된 인터페이스 역할 Facade Pattern (퍼사드 패턴) !Facade Pattern 클라이언트는 퍼사드(Facade)에 요청을 보내는 방식으로 서브시스템과 소통하며, 퍼사드는 해당 요청을 적절한 서브시스템 객체로 전달함 실제 작업은 서브시스템 객체들이 수행하지만, 퍼사드는 자신의 인터페이스를 서브시스템 인터페이스로 변환하기 위한 작업을 수행해야 할 수 있음 클라이언트가 서브 시스템에 직접 접근하지 않아도 됨 ※ 캡슐화(정보 은닉)와 목적이 다름 SLF4J (Simple Logging Facade for Java) !SLF4J `java.util.logging`, `Log4j`, `Logback` 등 다양한 로깅 프레임워크에 대해 간단한 퍼사드(혹은 추상화 레이어) 역할 slf4japi2.0.12.jar 하나를 의존성에 추가해야 함 클래스패스에 로깅 프레임워크와 연결(binding)되는 구현체가 없다면, SLF4J는 아무 동작도 하지 않는(nooperation) 상태로 동작 Logback > log4j의 후속작으로 더 빠르고 가벼움 !Logback logbackclassic 모듈을 사용하려면, 클래스패스에 다음 JAR 파일들이 반드시 포함되어야 한다. slf4japi.jar logbackcore.jar logbackclassic.jar ※ 스프링부트에서 `springbootstarterlogging`은 로킹 프레임 워크를 포함함 위 예제는 logback 관련 클래스는 전혀 사용하지 않았다는 점에 주목 오직 SLF4J의 클래스만 import 하면 됨 따라서, 코드는 SLF4J API만 사용하고, Logback이 존재한다는 사실을 몰라도 됨 Logger Logger는 이름을 가진 엔티티(객체)로 이름을 대소문자를 구분하며 계층적인 이름 규칙을 따름 `kr.ac.hansung` 이름의 Logger는 `kr.ac.hansung.cse` Logger의 부모 root logger는 계층 구조의 최상위 Logging Level > Logger에 레벨을 지정할 수 있다 로그 레벨은 `TRACE` → `DEBUG` → `INFO` → `WARN` → `ERROR` 순으로 중요도(심각도)가 점점 높아짐 명시적으로 레벨이 지정되어 있지 않다면 가장 가까운 상위 로거의 레벨을 상속 받음 root logger는 항상 레벨이 지정되어 있으며, 기본값은 `INFO`임 예시 Logger name Assigned level Effective level root `DEBUG` `DEBUG` X `INFO` `INFO` X.Y `none` `INFO` X.Y.Z `ERROR` `ERROR` Printing method 어떤 출력 메서드를 사용하는지에 따라 로그 요청의 레벨이 결정됨 → `logger.warn("hello");`는 `WARN` 레벨의 로그 요청 !Logback 로그 요청의 레벨이 해당 로거의 유효 레벨(effective level)보다 크거나 같으면, 그 로그 요청은 활성화(enabled)되었다고 한다. 그렇지 않으면, 즉 로깅 요청의 레벨이 더 낮으면, 그 로그 요청은 비활성화(disabled)되었다고 한다. 로그 요청 허용 여부 (`YES` = 출력됨, `NO` = 무시됨) Logging Request `p` ↓ \ Logger Effective Level `q` → TRACE DEBUG INFO WARN ERROR OFF TRACE YES NO NO NO NO NO DEBUG YES YES NO NO NO NO INFO YES YES YES NO NO NO WARN YES YES YES YES NO NO ERROR YES YES YES YES YES NO Appenders 로그 요청은 하나 이상의 출력 대상(destination)으로 출력될 수 있음 각 출력 대상은 appender(출력기)로 표현되며, 다음과 같은 종류 가능 → `console`, `files` (plain text, HTML...), `remote socket servers`, `databases` !Appenders > 특정 로거의 활성화된 로그 요청은, 해당 로거에 설정된 모든 appender뿐 아니라 상위 계층의 appender들에게도 전달된다. 즉, appender는 로거 계층에서 누적적으로 상속된다.이를 Appender Additivity이라고 부른다. 예를 들어, root 로거에 `console appender`가 추가되어 있다면 모든 활성 로그는 최소한 콘솔에 출력된다. 여기에 추가로 어떤 로거 L에 `file appender`를 설정하면, 및 그 하위 로거의 로그는 파일과 콘솔에 모두 출력된다. 하지만 이러한 기본 동작은 설정을 통해 변경할 수 있다. 즉, additivity 플래그를 `false`로 설정하면 상위 appender 상속을 막을 수 있다. Logger Name Attached Appenders Additivity Flag Output Targets Comment `root` A1 not applicable A1 Since the root logger stands at the top of the logger hierarchy, the additivity flag does not apply to it. `x` Ax1, Ax2 true A1, Ax1, Ax2 Appenders of "x" and of root. `x.y` none true A1, Ax1, Ax2 Appenders of "x" and of root. `x.y.z` Axyz1 true A1, Ax1, Ax2, Axyz1 Appenders of "x.y.z", "x" and of root. `security` Asec false Asec No appender accumulation since the additivity flag is set to false. Only appender Asec will be used. `security.access` none true Asec Only appenders of "security" because the additivity flag in "security" is set to false. ConsoleAppender 패턴 의미 `%d{pattern}` 로그 발생 시각 `%thread` 스레드 이름 `%5level` 로그 레벨 (5자 너비로 좌측 정렬) `%logger{length}` 로거 이름 (최대 길이 지정 가능) `%msg` 로그 메시지 FileAppender > 로그는 콘솔과 파일(test.dat)에 동시에 출력 RollingFileAppender 롤오버 파일(Rollover files): 로그를 하나의 파일에 기록하다가, 특정 조건이 되면 새로운 출력 파일로 전환하는 방식 이러한 조건은 롤링 정책(rolling policies)으로 지정하며, 가장 많이 사용되는 것은 TimeBasedRollingPolicy로, → 매달, 매주, 매일, 매시간마다 새 로그 파일로 전환됩니다. Logback Configuration classpath에 위치한 `logback.xml` 파일 !Logback Configuration