一、目标及思路
1.1 目标
Spark Streaming运行在Yarn上,查看日志不是很方便,而且日志是一个大文件,无用信息过多。所以打算将必要的业务日志和系统错误日志发送到kafka上,然后做后续的日志分析。
本文简单实现了Driver和Executor的日志发送功能,如有问题,请留言或者私信指正,不胜感激~
1.2 思路
-
使用kafka-log4j-appender
-
driver和executor都有自己默认的日志配置,将其覆盖掉即可
1.3 环境
CDH6.3.1
二、实现
2.1 配置文件 log4j.properties
log4j.rootLogger=WARN,console,kafka
# appender kafka 配置项跟下图中所示的参数名相配
log4j.appender.kafka=org.apache.kafka.log4jappender.KafkaLog4jAppender
log4j.appender.kafka.topic=log_test
log4j.appender.kafka.brokerList=10.10.10.156:9092,10.10.10.157:9092,10.10.10.158:9092
log4j.appender.kafka.compressionType=none
log4j.appender.kafka.syncSend=false
log4j.appender.kafka.layout=org.apache.log4j.PatternLayout
log4j.appender.kafka.layout.ConversionPattern=%d %5p (%c:%L) - %m%n
# appender console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %5p (%c:%L) - %m%n
log4j.logger.org.apache.spark.deploy.yarn.SparkRackResolver=ERROR
配置项log4j.appender.kafka.*
跟图中的变量名相匹配
2.2 完整的spark-submit示例
ROOT_DIR=/home/project/spark-demo
JAR_DIR=${ROOT_DIR}/jars
jars=`echo ${JAR_DIR}/*jar | sed 's/ /,/g'`
nohup sudo -uhdfs spark-submit --master yarn \
--executor-memory 1g \
--jars ${jars} \
--files "${ROOT_DIR}/log4j-executor.properties" \
--conf "spark.driver.extraJavaOptions=-Dlog4j.configuration=file:${ROOT_DIR}/log4j.properties" \
--conf "spark.executor.extraJavaOptions=-Dlog4j.configuration=file:log4j-executor.properties" \
--conf "spark.driver.extraClassPath=${ROOT_DIR}/jars/slf4j-api-1.8.0-beta4.jar:${ROOT_DIR}/jars/slf4j-log4j12-1.8.0-beta4.jar" \
--conf "spark.executor.extraClassPath=slf4j-api-1.8.0-beta4.jar:slf4j-log4j12-1.8.0-beta4.jar" \
--class com.smile.demo.spark.example.RealDemo \
spark-streaming-demo.jar \
>> ${ROOT_DIR}/info.log 2>&1 &
三、踩坑经历
3.1 找不到依赖
报错信息
java.lang.ClassNotFoundException: org.apache.kafka.log4jappender.KafkaLog4jAppender
问题排查
1)cd /opt/cloudera/parcels/CDH/jars
在该目录下能找到依赖的jar:kafka-log4j-appender-2.2.1-cdh6.3.1.jar
2)然后cd /opt/cloudera/parcels/CDH/lib/spark/jars
在该目录下未找到依赖的jar包,猜测是我集群中没有安装kafka(未验证)。
解决方案
方案一:在集群的每台机器上执行
cp /opt/cloudera/parcels/CDH/jars/kafka-log4j-appender-2.2.1-cdh6.3.1.jar /opt/cloudera/parcels/CDH/lib/spark/jars
方案二:spark-submit的时候在--jars
参数中配置该jar,作为外部依赖(记得用绝对路径,具体写法见上文中的2.2)
3.2 Log4j的版本冲突
报错信息
Unexpected problem occured during version sanity check
Reported exception:
java.lang.NullPointerException
at org.slf4j.LoggerFactory.versionSanityCheck(LoggerFactory.java:267)
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.apache.kafka.clients.producer.ProducerConfig.<clinit>(ProducerConfig.java:333)
Caused by: java.lang.NullPointerException
at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:418)
问题分析
CDH 6.3.1集群内使用的Jar如下
- kafka-log4j-appender:2.2.1-cdh6.3.3
- slf4j-api:1.7.25
CDH 6.3.1集群内的slf4j包版本是1.7.25,但是kafka-log4j-appender:2.2.1-cdh6.3.3
需要1.8版本的
解决方案
在StackOverflow有该错误的问答,参考其解决方案后,经过验证,以下解决方案可行
1)下载slf4j-api-1.8.0-beta4.jar和slf4j-log4j12-1.8.0-beta4.jar
在[Maven Repository: Search/Browse/Explore](https://mvnrepository.com/)上直接搜索下载,或者在pom文件中添加依赖自动下载
```
<slf4j.version>1.8.0-beta4</slf4j.version>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
```
2)然后在spark-submit
的时候,添加如下配置,完整示例见上文中的2.2
```
--jars "/home/jars/slf4j-api-1.8.0-beta4.jar,/home/jars/slf4j-log4j12-1.8.0-beta4.jar” \
--conf "spark.driver.extraClassPath=/home/jars/slf4j-api-1.8.0-beta4.jar:/home/jars/slf4j-log4j12-1.8.0-beta4.jar" \
--conf "spark.executor.extraClassPath=slf4j-api-1.8.0-beta4.jar:slf4j-log4j12-1.8.0-beta4.jar” \
```
3.3 spark-submit提交的配置参数未生效
问题描述
修改spark.driver.extraJavaOptions
参数配置后,但是并没有生效,查看任务UI中,配置的参数还是上一次提交的参数值
原因分析
我的Spark Streaming程序中,使用了checkpoint的getOrCreate()方法,这个方法是为了记录程序运行信息,方便程序重启时继续之前的运行
解决方案
因为我是在调试程序,所以直接删除了checkpoint文件。删除之后,新的配置信息提交成功。
如果是生产环境的话,该操作还是要慎重~