前言
spark默认采用log4j作为日志框架,并且采用${SPARK_HOME}/conf/log4j.properties作为默认的日志配置,默认如下:
log4j.rootLogger=${root.logger}
root.logger=INFO,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n
log4j.logger.org.apache.spark.repl.Main=WARN
log4j.logger.org.eclipse.jetty=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
从上述配置可知,仅开启了console输出,而spark在启动ApplicationMaster与Executor时,会将日志console输出重定向到stderr文件中,将system.out重定向到stdout中。以ApplicationMaster为例,其启动命令为:
/bin/bash -c /usr/java/jdk1.8//bin/java
-server -Xmx1024m
-Djava.io.tmpdir=/yarn/nm/usercache/root/appcache/application_id/application_id/tmp
-Dspark.yarn.app.container.log.dir=/yarn/container-logs/application_id/application_id
-XX:MaxPermSize=256m org.apache.spark.deploy.yarn.ApplicationMaster
--class 'com.clife.data.xx'
--jar file:xxx.jar
--executor-memory 2048m --executor-cores 1
--properties-file /yarn/nm/usercache/root/appcache/application_id/application_id/__spark_conf__/__spark_conf__.properties
1> /yarn/container-logs/application_id/application_id/stdout
2> /yarn/container-logs/application_id/application_id/stderr
stderr不支持自动切割与删除,对于spark stream job来说会造成stderr文件非常大的情况,不仅影响磁盘存储空间,对问题的排查和定位也并不友好。本文来介绍如何通过log4j(笔者尝试替换spark集群日志框架为logback,暂未成功)来实现日志重定向到file并支持切割与自动删除,同时支持从spark web ui中跳转到日志中。
配置log4j
全局替换
从上文可知,spark启动job时采用了${SPARK_HOME}/conf/log4j.properties作为log4j的配置文件,是否可以修改该配置文件,将日志写到文件中,并且支持自动切割和回滚即可?当然可以,关闭console appender,仅开放file appender。如下配置:
log4j.rootCategory= INFO, file
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
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
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.MaxFileSize=200MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.file=${spark.yarn.app.container.log.dir}/container
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p [%C{3}:%M:%L] %m%n
log4j.appender.file.encoding=UTF-8
log4j.logger.com.clife=DEBUG
log4j.logger.org.apache.spark.repl.Main=WARN
log4j.logger.org.eclipse.jetty=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
上述配置中log4j.appender.file.file=${spark.yarn.app.container.log.dir}/spark.log的${spark.yarn.app.container.log.dir}值来自启动Application Master和Executor的启动命令-Dspark.yarn.app.container.log.dir=/yarn/container-logs/application_id/application_id,如此便可以将file appender日志输出到stderr同级目录下,支持yarn日志聚合。那如何通过spark web ui查看当前日志文件呢?只需要将原来的stderr收到修改为container即可,如图:当前application可用
上面讲述了如何进行全局替换,本节讲解如何对单个application使用特定的log4j配置。参照:Spark log4j 日志配置。在spark-submit时,增加如下配置:
spark-submit \
--files ${CONF_DIR}/log4j-driver.properties,${CONF_DIR}/log4j-executor.properties \
--driver-java-options "-Dlog4j.configuration=log4j-driver.properties" \
--conf spark.executor.extraJavaOptions="-Dlog4j.configuration=log4j-executor.properties" \
这里支持对driver和executor使用不同的配置,输出到不同的配置文件中。其实在spark on yarn模式下,driver和executor属于不同的container,所以可以让driver和executor使用同一份配置。
spark web ui
在平台研发中,希望的统一日志输出格式和配置,并且可以spark web ui上能够直接跳转到用户的日志文件中。如下图:
当前表格在源码org.apache.spark.ui.exec.ExecutorsPage.scala的render方法中构建,
然后重新编译spark-assembly,替换集群中的jar,即可。
关于源码编译,笔者使用的spark-1.6.0-cdh5.7.2,需要注意maven版本(笔者使用3.3.3),以及配置cloudera repository。