前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何在spark on yarn的环境中把log4j升级到log4j2

如何在spark on yarn的环境中把log4j升级到log4j2

原创
作者头像
跳晓爸
修改2021-03-17 17:50:46
2.9K2
修改2021-03-17 17:50:46
举报
文章被收录于专栏:Spark大数据应用

搞了大半年的Spark了,感觉有不少坑,自己也总结了不少经验,这里就分享一个。

大家知道在spark on yarn中,spark的系统日志都是按照log4j的方式写到每一个node上面的container目录下的,如果要实时看一个application的日志,很麻烦!需要登录到executor所在的node上去tail一个文件,或者通过spark UI在界面上看,executor多了,这个就是麻烦事,要在不同的机器不同的目录中切换!我就在想能不能统一写到每个node的同一个地方,然后通过logstash发送到ELK里面去展示,这样在一个界面就可以看到所有application的日志了。但是这里就有1个很大的问题,log4j写的日志里面没有标明是哪个application写的日志,一大堆日志怎么知道谁是谁写的呢?所以日志里面一定要带进程号之类的标识,但是遗憾的log4j里面不支持,查了下要log4j2.9以后的版本(此时已经是log4j2了)才支持写processId,而spark3.0自带的是log4j-1.2.17.jar,所以升级的事情就来了!

先检查原包:

在/usr/local/spark/jars目录下是下面3个jar包:

log4j-1.2.17.jar

slf4j-api-1.7.30.jar

slf4j-log4j12-1.7.30.jar

最开始本来是想升级到2.9.1的,直接替换上了4个包:

log4j-1.2-api-2.9.1.jar,log4j-api-2.9.1.jar,log4j-core-2.9.1.jar,log4j-web-2.9.1.jar

没那么简答,直接报错:

java.lang.NoClassDefFoundError: org/apache/log4j/spi/Filter

想想应该是缺一些依赖包,查了查,发现filebeat用的就是log4j2,它用了这几个包:

log4j-api-2.12.1.jar

log4j-core-2.12.1.jar

log4j-slf4j-impl-2.12.1.jar

slf4j-api-1.7.25.jar

于是果断升级到2.12,结果报另外的错:

Exception in thread "main" java.lang.StackOverflowError at scala.reflect.io.ZipArchive$.scala$reflect$io$ZipArchive$$dirName(ZipArchive.scala:58)

这里提一下,我的spark application是用scala写的,版本2.12.12.

感觉是scala相关的包有问题,于是google了一下,stackflow上说这是scala2.12.12的bug,但是在2.12.13上fix了,晕!

只有把scala再升一下级:scala-library-2.12.13.jar,scala-reflect-2.12.13.jar,干脆多copy了一个包log4j-api-scala_2.12-12.0.jar过去。

好了,又有问题来了:

initializing Exception in thread "main" java.lang.StackOverflowError at scala.reflect.io.ZipArchive

网上说要升级到log4j-2.14,赶紧撸起袖子开干,升级到2.14,log4j2.14又比2.12多了1个包:

log4j-slf4j18-impl-2.14.1.jar

好勒,部署上去又报错:

Cannot initialize scripting support because this JRE does not support it.

查了下,说是因为要兼容JAVA1.7和1.8,搞了2个slf4j的适配器,所以还要加1个包:

slf4j-api-1.8.0-beta2.jar

这下好了吧!

NND,还是不得行,依然报错:

class scala.tools.nsc.Global overrides final method phaseWithId.()[Lscala/reflect/internal/Phase

晕菜,感觉是scala的错误,找了下源码,这个类在scala-compiler.jar里面,看来又得升级了!

部署,运行,成功了!终于看到进程号了:

21/03/17 15:01:37 4875 [main] INFO Client []: Preparing resources for our AM container

21/03/17 15:01:37 4875 [main] WARN Client []: Neither spark.yarn.jars nor spark.yarn.archive is set, falling back to uploading libraries under SPARK_HOME.

21/03/17 15:01:39 4875 [main] INFO Client []: Uploading resource file:/tmp/spark-5ff720e7-982b-44c0-be81-808a7743f014/__spark_libs__4263140552235112039.zip -> hdfs://mycluster/user/root/.sparkStaging/application_1615920195441_0153/__spark_libs__4263140552235112039.zip

进程号:4875,后面是线程[main]。

满打满算干了2天,搞定了这次日志框架的升级,虽然就为了打印这个进程号,但是log4j2还是比log4j灵活太多,内容也很丰富。后面如何通过filebeat收集日志,发送到kafka,然后logstash从kafka里面取出来发送给ELK,网上这种就是一大把,在此就不赘述了!

log4j2.properties:

status = error

dest = err

name = PropertiesConfig

# 定义变量。指定日志文件的位置和文件名称

property.filename = /bigdata/log/spark.log

filter.threshold.type = ThresholdFilter

# 只记录debug级别以上的日志,大小写无关:(ALL<)TRACE<DEBUG<INFO<WARN<ERROR<FATAL(<OFF)

filter.threshold.level = debug

appenders = console, rolling

appender.console.type = Console

appender.console.name = STDOUT

appender.console.layout.type = PatternLayout

appender.console.layout.pattern = %d{yy/MM/dd HH:mm:ss} %processId [%t] %p %c{1}: %m%n

appender.console.filter.threshold.type = ThresholdFilter

appender.console.filter.threshold.level = error

appender.rolling.type = RollingFile

appender.rolling.name = rolling

appender.rolling.fileName = ${filename}

appender.rolling.append= true

appender.rolling.filePattern = spark-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz

appender.rolling.layout.type = PatternLayout

appender.rolling.layout.pattern = %d{yy/MM/dd HH:mm:ss} %processId [%t] %p %c{1} %x: %m%n

appender.rolling.policies.type = Policies

appender.rolling.policies.size.type = SizeBasedTriggeringPolicy

appender.rolling.policies.size.size=20MB

appender.rolling.strategy.type = DefaultRolloverStrategy

appender.rolling.strategy.max = 20

#定义使用的logger

logger.xxx.name = com.yyy.zzz

logger.xxx.level = info

logger.xxx.additivity = false # 是否向上级logger传递打印信息

#关联名称为rolling的输出源(appender)

logger.aa_performance.appenderRef.rolling.ref = rolling

#根记录器,所有记录器的父辈, 指定根日志的级别

rootLogger.level = info

rootLogger.appenderRef.console.ref = STDOUT

rootLogger.appenderRef.rolling.ref = rolling

记住:调试的时候要把status = error的error改为trace,这样才能看到log4j2初始化的日志,方便看什么地方报错!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
大数据
全栈大数据产品,面向海量数据场景,帮助您 “智理无数,心中有数”!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档