Spark Standalone集群模式下提交的应用未按Worker节点log4j配置生成日志的问题排查
嘿,我来帮你捋清楚这个问题——核心原因是Spark的日志体系分成了守护进程日志和应用程序日志两个完全独立的部分,你当前的配置只覆盖了Worker守护进程本身的日志,这就是为啥看不到应用相关日志(比如Kafka消费者日志)和部分OOM日志的原因。
一、为什么应用日志没出现在Worker的集中日志里?
Worker节点的log4j配置只管Worker自身的运维操作:比如启动、接收Master的任务、管理Executor进程的生命周期这些。而你提交的Spark应用,它的Executor是单独的JVM进程,Driver(集群模式下)也是独立进程,它们的日志默认会写到Worker节点的$SPARK_WORKER_DIR/app-<应用ID>/目录下的本地文件里,根本不会继承Worker的log4j配置。
解决方法:统一应用的日志配置到Syslog
你需要给应用单独配置log4j,让它把日志也发往Master的集中日志系统:
- 先创建一个应用专属的
app-log4j.properties,参考你现有的Worker配置,加上SyslogAppender:
log.level=INFO log4j.rootLogger=${log.level}, SYSLOG log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender log4j.appender.SYSLOG.threshold=${log.level} log4j.appender.SYSLOG.syslogHost=<Master节点IP>:514 # 直接指向Master的rsyslog log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout log4j.appender.SYSLOG.layout.conversionPattern=spark/app %d{ISO8601} %p %c: %m%n # 这里可以加上你需要的日志过滤,比如Kafka的日志 log4j.logger.org.apache.kafka=INFO # 确保OOM错误能被捕获 log4j.logger.java.lang=ERROR
- 然后通过两种方式让应用加载这个配置:
- 提交应用时指定:用
spark-submit的参数传递:spark-submit \ --files /etc/user/spark/app-log4j.properties \ --conf spark.driver.extraJavaOptions="-Dlog4j.configuration=file:app-log4j.properties" \ --conf spark.executor.extraJavaOptions="-Dlog4j.configuration=file:app-log4j.properties" \ your-application.jar - 全局配置(推荐):在
spark-defaults.conf里添加以下配置,所有应用都会自动继承:spark.driver.extraJavaOptions=-Dlog4j.configuration=file:///etc/user/spark/app-log4j.properties spark.executor.extraJavaOptions=-Dlog4j.configuration=file:///etc/user/spark/app-log4j.properties
- 提交应用时指定:用
这样应用的Driver和Executor日志就会被发送到Master的集中日志里了。
二、为什么看不到java.lang.OutOfMemoryError: Java heap space日志?
这个OOM错误可能发生在三个地方,得分别排查:
- Worker守护进程本身:如果是Worker自己OOM,日志应该会出现在你配置的
spark-worker.log里(因为你设置了-Dlog.file=spark-worker.log),但如果进程直接被系统kill,可能syslog没来得及接收,这时候直接去Worker节点看本地的spark-worker.log文件。 - Executor进程:Executor的OOM日志会写在自己的日志文件里,或者如果你配置了上面的应用log4j,就会发往Syslog。另外,Worker的日志里会记录Executor的退出事件(比如
Executor <ID> exited with status 137)——137就是进程被OOM killer干掉的信号,这时候你需要去对应Worker的$SPARK_WORKER_DIR/app-<应用ID>/目录下找Executor的日志文件,看具体的OOM堆栈。 - Driver进程:如果是客户端模式提交应用(默认),Driver日志会在你提交应用的机器上;如果是集群模式,Driver运行在Worker上,日志同样在
$SPARK_WORKER_DIR/app-<应用ID>/的driver日志文件里。
额外优化:确保OOM日志被捕获
在应用的app-log4j.properties里一定要加上:
log4j.logger.java.lang=ERROR
这样JVM抛出的OOM错误会被log4j捕获并输出到Syslog里。
三、当前Worker配置的小优化建议
你现在的Worker log4j只配置了SyslogAppender,一旦syslog服务出问题,Worker的日志就没地方存了,建议同时加上FileAppender作为备份:
log.active.appenders=SYSLOG, FILE # 添加FileAppender配置 log4j.appender.FILE=org.apache.log4j.FileAppender log4j.appender.FILE.file=${SPARK_WORKER_DIR}/spark-worker.log log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
这样即使集中日志出问题,你也能在Worker本地找到日志排查。
另外,你的systemd服务配置是对的,SPARK_DAEMON_JAVA_OPTS已经正确传递了log4j配置,不用改。
总结步骤
- 创建应用专属的log4j配置,指向Master的rsyslog服务器,确保捕获OOM和应用日志。
- 通过
spark-submit参数或spark-defaults.conf让应用加载这个配置。 - 给Worker的log4j添加FileAppender作为本地备份。
- 根据OOM发生的位置,对应查看Worker本地日志、Executor日志或Driver日志。
内容的提问来源于stack exchange,提问作者Filip

