사용자 도구

사이트 도구


java:slf4j

Slf4j

  • 여러 로거를 선택해 사용할 수 있게 해주는 로깅 파사드.
  • Jakarta Commons Logging 과 비슷한 기능을 하지만 Slf4j가 더 좋다.
  • 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에서 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

Legacy Logging Framework 제거하고 LogBack 사용

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 설정으로 공통 적용하는 것이 좋다.
  • Spring Context 의존성에서 commons-logging을 제거하고, jcl-over-slfj를 넣는다.
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version>
    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
    <type>jar</type>
</dependency>
 
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>${slf4j.version}</version>
    <scope>runtime</scope>
</dependency>
 
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
    <type>jar</type>
</dependency>

java.util.logging

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 설정

    • groovy 설정
      import ch.qos.logback.classic.jul.LevelChangePropagator
      context = new LevelChangePropagator()
      context.resetJUL = true
    • XML 설정
      <configuration debug="true">
        <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
          <resetJUL>true</resetJUL>
        </contextListener>
        ....
      </configuration>

Logger Wrapping

  • slf4j Logger를 사용할 때 특정 포맷으로 호출할 수 있게 Logger 호출을 감싸는 경우가 있는데, 이 때 log에 항상 로깅 위치가 Wrapper 코드로 남아서 실조 로그를 남기고자하는 코드 위치를 보기 불편해진다.
  • org.slf4j.spi.LocationAwareLogger 를 통해서 이 문제를 해소할 수 있다. 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");

SimpleLogger

  • 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 에서 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

참조

java/slf4j.txt · 마지막으로 수정됨: 2023/12/07 10:32 저자 kwon37xi