python logging 모듈을 사용하지 않고 log4j를 사용하는 이유?
Pyspark는 JVM 기반으로 동작한다. python에서 실행되지만 내부적으로 JVM을 통해 Spark 작업을 수행하기 때문에 python logging 모듈은 JVM 로그를 제어할 수 없다.
또한 Spark는 multi-cluster에서 실행되기 때문에 여러 노드에서 로그를 수집해야하는데, Log4J는 이러한 Spark 클러스터의 실행 환경과 잘 맞는다.(=분산 환경에서 효과적인 로깅을 제공.)
Log4는 최적화된 비동기 로깅을 지원해 성능이 뛰어나다. → RollingFileAppender를 사용하면 로그파일 관리가 쉽다.
Spark 자체가 Log4J를 기본적으로 사용한다. logging 모듈을 사용하게 되면 Spark 로그와 통합 관리가 어렵기 때문에 Log4J를 사용하는 것이다!

각 Executor들은 각기 다른 JVM 위에서 실행이 되고, 애초에 Hadoop과 Spark는 이런 다중 병렬 프로그래밍에 대해 신경쓰지 않고 대규모의 데이터를 수집, 가공, 처리할 수 있도록 도와주는 프레임워크이다. 그렇기 때문에 우리는 각 driver와 exetutors가 어느 위치의 JVM에서 실행되고 있는지를 알 필요가 없는데 프로그램이 구동을 시작하면 각 JVM 단위로 로그파일이 기록이 될 것이다. 이때 log4j.appender.file.File = ${logfile_path}/${logfile_name}.log을 설정해서 로그 파일 저장 경로를 설정하면 해당 경로에 로그를 (HDFS에) 저장하고 YARN UI에서 조회할 수 있다.
단, 이때 YARN의 로그 수집 기능은 Log4J와는 독립적이며, yarn.log-aggregation.enable=true 설정이 필요하다는 것을 알고 있어야 한다.
Log4j의 구성요소
- LoggerLogger는 여려개의 Appender를 가질 수 있다. (=출력 대상 여러개일수 있다.)
- ex) TRACE → DEBUG → INFO → WARN → ERROR → FATAL
- → 오른쪽으로 갈 수록 심각한 문제들만 로그에 기록하는 것이다.
- 로그 레벨을 지정해서 특정 레벨 이상의 로그만 출력하도록 설정할수도 있다.
- Logger는 실제로 로그를 기록하는 객체로, 애플리케이션에서 이벤트를 로깅하는 역할을 한다.
- Configurations
- Log4J는 XML 또는 Properties 파일을 사용해 로깅을 설정할 수 있다.
- Appender(출력 대상)ex) ConsoleAppender(console에 출력), FileAppender(file에 출력), RollingFileAppender(파일 크기 커지면 자동으로 분할 저장), SyslogAppender(운영체제의 syslog로 전송)
- Appender는 로그를 출력하는 대상(콘솔, 파일, DB 등)을 설정하는 요소이다.
log4j 오픈소스 링크
Apache log4j 1.2 -
End of Life On August 5, 2015 the Logging Services Project Management Committee announced that Log4j 1.x had reached end of life. For complete text of the announcement please see the Apache Blog. Users of Log4j 1 are recommended to upgrade to Apache Log4j
logging.apache.org
JVM variables을 구성하는 방법은?
아래 절차를 거쳐 JVM variables를 구성할 수 있다.
- Create a Log4J configuration file
- Configure Spark JVM to pickup the Log4J configuration file
- Create a Python Class to get Spark’s Log4J instance and use it
Logj4 properties 구성
log4j properties를 구성하기 위해 아래 log4j.properties 파일을 보고 구성요소를 파악해보자.
아래는 log4j.properties 파일의 전문이다.
# Set everything to be logged to the console
log4j.rootCategory=WARN, console
# define console appender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
#application log
log4j.logger.guru.learningjournal.spark.examples=INFO, console, file
log4j.additivity.guru.learningjournal.spark.examples=false
#define rolling file appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=${spark.yarn.app.container.log.dir}/${logfile.name}.log
#define following in Java System
# -Dlog4j.configuration=file:log4j.properties
# -Dlogfile.name=hello-spark
# -Dspark.yarn.app.container.log.dir=app-logs
log4j.appender.file.ImmediateFlush=true
log4j.appender.file.Append=false
log4j.appender.file.MaxFileSize=500MB
log4j.appender.file.MaxBackupIndex=2
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
# Recommendations from Spark template
log4j.logger.org.apache.spark.repl.Main=WARN
log4j.logger.org.spark_project.jetty=WARN
log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO
log4j.logger.org.apache.parquet=ERROR
log4j.logger.parquet=ERROR
log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL
log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR
1) 기본 로그 설정
log4j.rootCategory는 루트 로그 레벨을 설정하는 부분이고, 아래 설정에서는 WARN 이상의 로그만 출력하도록, 로그가 console로 출력되도록 설정했다.
log4j.rootCategory=WARN, console
2) Console Appender 설정
log4j.appender.console은 콘솔에 로그를 출력하는 Appender이다. System.out은 표준 출력으로 로그 전송을 의미하고, 로그 형식은 날짜와 시간이 표시되는 ConversionPattern 형식으로 정의했다.
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
3) 애플리케이션 전용 로그 설정
특정 패키지의 로그 레벨을 INFO로 설정한다.
아까 루트 설정에서는 로그 레벨이 WARN이었는데, log4j.appender.guru.learnjournam.spark.examples의 로그 레벨만 INFO 레벨부터 출력하도록 설정한 것이다.
log4j.aditivity=false라는 것은 부모 로거에 로그를 전달하지 않는다는 뜻이다.
log4j.appender.guru.learningjournal.spark.examples=INFO, console, file
log4j.additivity.guru.learningjournal.spark.examples=false
4) 파일(Appender) 설정 (feat. RollingFileAppender)
RollingFileAppender를 사용하면, 일정 크기가 되면 새로운 파일을 만들고, 이전 파일을 보관한다.
두번째 줄은 로그 파일 경로를 설정해주는 것이다. 로그파일 저장경로인 spark.yarn.app.container.log.dir과 logfile.name은 실행시에 -Dlogfile.name=hello-spark같은 방식으로 전달해줘야한다.
ex) 예를 들어서 spark-submit을 실행하는 경우,
spark-submit --driver-java-options "-Dlogfile.name=hello-spark -Dspark.yarn.app.container.log.dir=app-logs" HelloSpark.py
절대경로로 저장하려면 아예 명시적으로 파일 경로를 "log4j.appender.file.File=logs/hello-spark"와 같이 정의한다.
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=${spark.yarn.app.container.log.dir}/${logfile.name}.log
5) 파일(Appender) 속성 설정
위 properties 파일에서 설정한 File Appender 옵션은 아래와 같다.
ImmediateFlust = true -> 로그가 발생할 때마다 즉시 파일에 기록(성능 저하 가능)
Append=false -> 실행할 때마다 로그 파일을 새로 생성(덮어쓰기) -> 기존 로그에 이어서 저장하려면 true로 설정
MaxBackupIndex=2 -> 최대 2개의 백업 파일 유지
예를 들면 hello-spark.log, hello-spark.log.1, hello-spark.log.2 파일 그 이상이 되면 가장 오래된 파일이 삭제된다.
log4j.appender.file.ImmediateFlush=true
log4j.appender.file.Append=false
log4j.appender.file.MaxFileSize=500MB
log4j.appender.file.MaxBackupIndex=2
6) 파일 로그 형식 설정
appender.layout 설정을 통해 파일 로그의 형식을 설정할 수 있다.
ConversionPattern 옵션을 사용하면 콘솔 출력과 동일한 형식을 사용한다.
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
7) Spark 내부 로그 필터링
Spark 내부 로그 필터링 옵션은 Spark 내부의 특정 모듈과 관련된 로그 레벨을 설정해 불필요한 로그를 줄이는 역할을 한다. 불필요한 DEBUG/INFO 로그가 많이 나오기 때문에 로그를 줄이려는 목적으로 설정했다. 아래 정의된 옵션은 다음과 같다.
- Spark REPL 로그를 WARN 이상만 출력
- Jetty 관련 로그도 WARN 이상만 출력
- Jetty 라이프사이클 관련 로그는 ERROR 이상만 출력
- Parquet 관련 불필요한 로그 제거
- Hive Metastore 로그 중 치명적인 오류만 출력
log4j.logger.org.apache.spark.repl.Main=WARN
log4j.logger.org.spark_project.jetty=WARN
log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR
log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=INFO
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO
log4j.logger.org.apache.parquet=ERROR
log4j.logger.parquet=ERROR
log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL
log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR