← 학습 카테고리

Learn

Airflow

151개 모듈 · 현재 68번째

Airflow 모듈 68/151 airflow-learn-68

Java SDK - 로깅 프레임워크 연동 (JPL/SLF4J/Log4j2/JUL)

Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation authoring-and-scheduling/language-sdks/java.rst - Logging, System.Logger (JPL), SLF4J 2.x, Log4j 2, java.util.logging, Other frameworks (약 258-412줄)

이 모듈을 다 읽으면

  • 태스크 로그가 Airflow 태스크 로그 저장소로 라우팅되는 각 로깅 프레임워크별 연동 아티팩트를 나열할 수 있다
  • 왜 동일 카테고리(SLF4J 바인딩, System.LoggerFinder)의 구현체를 중복 추가하면 안 되는지 설명할 수 있다
  • java.util.logging에서 AirflowJulHandler.setup()이 왜 필요한지 설명할 수 있다

Java SDK는 JPL(System.Logger), SLF4J 2.x, Log4j 2, java.util.logging 각각에 대해 ServiceLoader 기반 자동 탐색 아티팩트를 제공해 태스크 로그를 Airflow 태스크 로그 저장소로 라우팅하며, 각 프레임워크마다 동일 역할의 구현체를 중복 등록하지 않도록 주의해야 한다.

JPL (System.Logger)과 SLF4J 2.x - ServiceLoader 자동 탐색

Java 9의 새 로깅 파사드인 `java.lang.System.Logger`(JEP 264, 흔히 JPL로 줄여 부름)는 서드파티 API를 끌어들 이지 않고도 라이브러리가 쓸 수 있는 로깅 인터페이스다. `airflow-sdk-jpl` 아티팩트는 `ServiceLoader`를 통해 `AirflowSystemLoggerFinder`를 등록하며, 이는 모든 `System.Logger` 호출을 Airflow 태스크 로그 저장소로 곧장 라우팅한다 - 별도 설정 파일이나 시작 호출이 필요 없고, JAR이 클래스패스에 있기만 하면 `ServiceLoader`가 자동으로 이를 발견한다. 다만 `airflow-sdk-jpl`과 나란히 다른 `System.LoggerFinder` 구현체를 추가해서는 안 된다 - JVM은 `ServiceLoader`로 파인더를 하나만 선택하는데, 여러 provider가 클래스패스에 있으면 어떤 것이 선택될지 예측할 수 없어진다.

SLF4J 바인딩(`airflow-sdk-slf4j`)도 마찬가지로 `ServiceLoader`로 자동 탐색되며 별도 설정이나 시작 호출이 필요 없고, SLF4J API도 함께 딸려오므로 별도로 `slf4j-api`를 추가할 필요가 없다. 역시 `airflow-sdk-slf4j`와 나란히 다른 SLF4J 바인딩(`logback-classic`, `slf4j-simple` 등)을 추가해서는 안 된다 - SLF4J 2.x는 여러 바인딩이 있으면 경고를 내고 그중 하나를 예측 불가능하게 선택한다.

핵심 포인트

  • airflow-sdk-jpl과 airflow-sdk-slf4j 모두 ServiceLoader 메커니즘으로 클래스패스에 JAR만 있으면 별도 설정 파일이나 시작 호출 없이 자동으로 로깅을 Airflow 태스크 로그로 라우팅한다
  • 둘 다 같은 역할을 하는 구현체(다른 System.LoggerFinder나 다른 SLF4J 바인딩)를 동시에 클래스패스에 두면 JVM/SLF4J가 어느 것을 쓸지 예측할 수 없게 되므로, 반드시 하나만 남겨야 한다

Log4j 2와 java.util.logging(JUL)

`airflow-sdk-log4j2`는 `log4j-api`를 전이 의존성으로 선언하므로 이를 별도로 추가할 필요는 없지만, 시작 시 커스텀 `AirflowAppender`를 발견하는 플러그인 로더를 호스트하려면 `log4j-core`를 런타임 클래스패스에 별도로 올려야 한다. 그리고 `log4j2.xml`에 `AirflowAppender`를 직접 선언해야 한다.

`java.util.logging`(JUL)은 `airflow-sdk-jul` 아티팩트를 추가하고, 어떤 태스크가 실행되기 전에 반드시 `AirflowJulHandler.setup()`을 호출해야 한다. 이 호출은 JUL 루트 로거의 기존 핸들러(기본 `ConsoleHandler` 포함)를 지우고 `AirflowJulHandler`로 교체한다 - 기본 `ConsoleHandler`를 그대로 두면 그 stderr 출력을 Airflow가 별도로 `task.stderr`로 ERROR 레벨에 캡처해버려, 각 레코드가 중복되고 레벨도 잘못 표시되는 문제가 생긴다. 대안으로 `logging.properties` 파일에 핸들러를 선언하고, 코디네이터 설정의 `jvm_args`로 `java.util.logging.config.file` 시스템 프로퍼티를 지정해 JUL이 그 파일을 보게 만들 수도 있다.

핵심 포인트

  • Log4j 2는 airflow-sdk-log4j2(API는 자동 포함)에 더해 log4j-core를 런타임 클래스패스에 별도로 올려야 커스텀 AirflowAppender를 로드하는 플러그인 로더가 동작하고, log4j2.xml에 AirflowAppender를 직접 선언해야 한다
  • JUL은 자동 탐색이 아니라 AirflowJulHandler.setup()을 태스크 실행 전에 명시적으로 호출해야 하며, 이는 기본 ConsoleHandler를 제거해 stderr 출력이 task.stderr로 중복·오분류되어 잡히는 문제를 막아준다

그 외 프레임워크 (Logback, Commons Logging)

몇몇 자주 쓰이는 로깅 API는 전용 Airflow 아티팩트 없이도 커버된다. Logback은 그 자체가 SLF4J 바인딩이므로, `logback-classic`을 `airflow-sdk-slf4j`로 교체하기만 하면 되고 태스크 코드는 바꿀 필요가 없다. Apache Commons Logging(JCL)은 `org.slf4j:jcl-over-slf4j`를 통해 SLF4J로 브리지하거나, `org.apache.logging.log4j:log4j-jcl`을 통해 Log4j 2로 브리지할 수 있다.

핵심 포인트

  • Logback은 그 자체가 SLF4J 바인딩이므로 logback-classic을 airflow-sdk-slf4j로 교체하기만 하면 되고 태스크 코드는 바꿀 필요가 없다
  • Apache Commons Logging(JCL)은 jcl-over-slf4j 또는 log4j-jcl로 브리지해 기존 로깅 프레임워크 연동에 편입시킬 수 있다