====== 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]]