====== Slf4j ======
* 여러 로거를 선택해 사용할 수 있게 해주는 로깅 파사드.
* http://www.slf4j.org/
* Jakarta Commons Logging 과 비슷한 기능을 하지만 Slf4j가 더 좋다.
* [[java:logback|Logback]]과 조합해서 많이 사용한다.
===== 의존성 =====
* ''org.slf4j:slf4j-simple''에 의존성을 걸면 최소한의 slf4j simple logger가 활성화된다.
* ''org.slf4j:slf4j-api''만 의존성 걸면 아무 logger도 작동하지 않는다.
===== 로거 객체 생성 =====
// 자동으로 현재 클래스를 찾아낸다.
import java.lang.invoke.MethodHandles;
private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
===== Eclipse Slf4j Template =====
''Java -> Templates''에서 {{:java:slf4j_eclipse_template.7z|}}를 import 해서 ''slf4j''라고 에디터에서 치면 즉시 Slf4j 의 기본 코드가 작성된다.
===== IntelliJ IDEA Slf4j Live Template =====
**설정 -> Live Templates** 에서 다음 처럼 하나의 항목을 추가해 준다.
* Abbreviation : ''slf4j''
* Template Text :
private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger($CLASS$.class);
* Edit variables
* Name : ''CLASS''
* Expression : ''className()''
* Skip if defined : check
* Use static immport if possible : check
* Shorten FQ names : check
* Applicable in : **Java: declaration**
{{:java:idea_slf4j_live_template.png}}
===== Legacy Logging Framework 제거하고 LogBack 사용 =====
* http://www.slf4j.org/legacy.html
==== Gradle 설정 ====
ext {
slf4jVersion = '1.7.5'
logbackVersion = '1.0.13'
}
List loggers = [
"org.slf4j:slf4j-api:${slf4jVersion}",
"org.slf4j:jcl-over-slf4j:${slf4jVersion}",
"org.slf4j:log4j-over-slf4j:${slf4jVersion}",
"org.slf4j:jul-to-slf4j:${slf4jVersion}",
"ch.qos.logback:logback-core:${logbackVersion}",
"ch.qos.logback:logback-classic:${logbackVersion}"
]
// .. 의존성에 loggers 추가
dependencies {
compile loggers
}
// commons-logging, log4j, jul 의존성 제거
configurations {
all.collect { configuration ->
configuration.exclude group: 'commons-logging', module: 'commons-logging'
configuration.exclude group: 'log4j', module: 'log4j'
configuration.exclude group: 'org.apache.logging.log4j'
configuration.exclude group: 'org.slf4j', module: 'slf4j-log4j12'
configuration.exclude group: 'org.slf4j', module: 'slf4j-jcl'
configuration.exclude group: 'org.slf4j', module: 'slf4j-jdk14'
}
}
==== Spring에서 JCL빼고 Slf4j 사용하기 ====
* 아래는 Maven 설정이며, Gradle 설정으로 공통 적용하는 것이 좋다.
* [[http://www.javacodegeeks.com/2012/11/spring-setting-logging-dependencies.html|Spring: Setting Logging Dependencies]]
* Spring Context 의존성에서 commons-logging을 제거하고, jcl-over-slfj를 넣는다.
org.springframework
spring-context
${spring.version}
commons-logging
commons-logging
jar
org.slf4j
jcl-over-slf4j
${slf4j.version}
runtime
org.slf4j
slf4j-api
${slf4j.version}
jar
==== java.util.logging ====
* http://www.slf4j.org/legacy.html
* [[http://mvnrepository.com/artifact/org.slf4j/jul-to-slf4j|jul-to-slf4j]] 의존성 걸기
* [[springframework:springboot|SpringBoot]] 사용시 ''jul-to-slf4j'' 의존성이 걸려 있으면 아래 모든 설정이 자동으로 이뤄지므로 할 필요 없다. [[https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/Slf4JLoggingSystem.java|Slf4JLoggingSystem.java]] 참조
=== SLF4JBridgeHandler 설정 ===
* [[http://www.slf4j.org/api/org/slf4j/bridge/SLF4JBridgeHandler.html|SLF4JBridgeHandler]]
* 애플리케이션 시작 지점에 코드로 설정
* // Optionally remove existing handlers attached to j.u.l root logger
SLF4JBridgeHandler.removeHandlersForRootLogger(); // (since SLF4J 1.6.5)
// add SLF4JBridgeHandler to j.u.l's root logger, should be done once during
// the initialization phase of your application
SLF4JBridgeHandler.install();
* ''logging.properties''로 설정 (''SLF4JBridgeHandler.install()'' 이거 안할경우에 만)
handlers = org.slf4j.bridge.SLF4JBridgeHandler
=== Logback 설정 ===
* ''LevelChangePropagator'' 필요(http://logback.qos.ch/manual/configuration.html)
* groovy 설정
import ch.qos.logback.classic.jul.LevelChangePropagator
context = new LevelChangePropagator()
context.resetJUL = true
* XML 설정
true
....
===== Logger Wrapping =====
* slf4j Logger를 사용할 때 특정 포맷으로 호출할 수 있게 Logger 호출을 감싸는 경우가 있는데, 이 때 log에 항상 로깅 위치가 Wrapper 코드로 남아서 실조 로그를 남기고자하는 코드 위치를 보기 불편해진다.
* ''org.slf4j.spi.LocationAwareLogger'' 를 통해서 이 문제를 해소할 수 있다. [[https://stackoverflow.com/questions/3491744/wrapping-the-slf4j-api|java - Wrapping the slf4j API]]
private static final String FQCN = Logger.class.getName();
public static void debug(String clazz, String message) {
org.slf4j.Logger logger = LoggerFactory.getLogger(clazz);
if (logger instanceof LocationAwareLogger) {
((LocationAwareLogger) logger).log(null, FQCN, LocationAwareLogger.DEBUG_INT, message, null, null);
} else {
logger.debug(message);
}
}
===== Marker =====
Marker marker = MarkerFactory.getMarker("markerName");
* [[https://examples.javacodegeeks.com/enterprise-java/slf4j/slf4j-markers-example/|SLF4J markers example]]
===== SimpleLogger =====
* [[http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html|SimpleLogger]] 로거 사용하기 싫을 때 쓸만한 기본 구현체
// 버전에 따라 프라퍼티 명이 simpleLogger가 아닌 simplelogger일 수도 있음.
// 버전에 따라 defaultLogLevel -> defaultlog 일 수도 있음.
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info"); // 기본 레젤 지정
System.setProperty("org.slf4j.simpleLogger.log.com.microsoft.sqlserver", "trace"); // 특정 logger의 레벨 지정
===== Test Slf4j - Reflection 사용 =====
* [[java:reflection|Java Reflection]] 에서 ''private static final'' 필드에 관한 사항 참조
TestTargetClass testTarget = new TestTarget();
private Field logField;
private Logger originalLogger;
private Logger mockedLogger;
@BeforeEach
void setUp() throws NoSuchFieldException, IllegalAccessException {
mockedLogger = mock(Logger.class);
Field logField = TestTargetClass.class.getDeclaredField("log");
logField.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(logField, logField.getModifiers() & ~Modifier.FINAL);
originalLogger = (Logger) logField.get(null);
logField.set(null, mockedLogger);
}
@AfterEach
void tearDown() throws IllegalAccessException {
logField.set(null, originalLogger);
}
@Test
void testMethod() {
testTarget.callMethod()
verify(mockedLogger).info("로그 메시지. 로그 인자 {}", "로그인자");
}
// Lombok @Slf4j 로 private static final Logger log 필드 주입
@Slf4j
public static class TestTargetClass {
public void callMethod() {
log.info("로그 메시지. 로그 인자 {}", "로그 인자");
}
}
===== Test Slf4j =====
* [[https://www.simplify4u.org/slf4j-mock/|SLF4J mock]] : slf4j 2.x 까지 반영된 최신 라이브러리
* [[http://projects.lidalia.org.uk/slf4j-test/|SLF4J Test]]
* [[https://github.com/portingle/slf4jtesting|portingle/slf4jtesting: SLF4J Testing library optimised for test concurrency and dependency injection]]
* http://www.spf4j.org/spf4j-slf4j-test/index.html
===== 참조 =====
* [[https://examples.javacodegeeks.com/enterprise-java/slf4j/slf4j-tutorial-beginners/|SLF4J Tutorial for Beginners | Examples Java Code Geeks - 2018]]