我有一个使用Spark的Scala Maven项目,我正在尝试使用Logback实现日志记录。我正在将我的应用程序编译到jar,并部署到安装了Spark发行版的EC2实例。 我的pom.xml包含Spark和Logback的依赖项,如下所示:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_${scala.binary.version}</artifactId>
<version>${spark.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
提交我的Spark应用程序时,我在命令行上打印出slf4j绑定。如果我使用java执行jar代码,则绑定是Logback。但是,如果我使用Spark(即spark-submit),则绑定到log4j。
val logger: Logger = LoggerFactory.getLogger(this.getClass)
val sc: SparkContext = new SparkContext()
val rdd = sc.textFile("myFile.txt")
val slb: StaticLoggerBinder = StaticLoggerBinder.getSingleton
System.out.println("Logger Instance: " + slb.getLoggerFactory)
System.out.println("Logger Class Type: " + slb.getLoggerFactoryClassStr)
产量
Logger Instance: org.slf4j.impl.Log4jLoggerFactory@a64e035
Logger Class Type: org.slf4j.impl.Log4jLoggerFactory
我知道log4j-1.2.17.jar
和slf4j-log4j12-1.7.16.jar
都在/ usr / local / spark / jars中,而且尽管在我的pom.xml中排除了,但Spark最有可能引用这些jar,因为如果我删除它们我在spark-submit的运行时给了一个ClassNotFoundException。
我的问题是:是否有办法使用Logback在我的应用程序中实现本机日志记录,同时保留Spark的内部日志记录功能。理想情况下,我想将我的Logback应用程序日志写入文件,并允许Spark日志仍显示在STDOUT。
答案 0 :(得分:6)
我遇到了一个非常类似的问题。
我们的版本与您的版本类似(但我们使用了sbt
)并在此处详细说明:https://stackoverflow.com/a/45479379/1549135
在本地运行此解决方案,然后spark-submit
将忽略所有排除项和新的日志记录框架(logback
),因为spark的类路径优先于部署的jar。由于它包含log4j 1.2.xx
,因此只需加载它并忽略我们的设置。
我使用过多种来源。但引用Spark 1.6.1 docs(也适用于Spark latest / 2.2.0):
<强> spark.driver.extraClassPath 强>
要添加到驱动程序类路径的额外类路径条目。 注意:在客户端模式下,不能直接在应用程序中通过SparkConf设置此配置,因为驱动程序JVM已在此时启动。相反,请通过--driver-class-path命令行选项或默认属性文件中设置它。
<强> spark.executor.extraClassPath 强>
要添加到执行程序的类路径的额外类路径条目。这主要是为了向后兼容旧版本的Spark。用户通常不需要设置此选项。
这里没有写的是extraClassPath
优先于默认Spark的类路径!
所以现在解决方案应该非常明显。
- log4j-over-slf4j-1.7.25.jar
- logback-classic-1.2.3.jar
- logback-core-1.2.3.jar
spark-submit
:libs="/absolute/path/to/libs/*"
spark-submit \
...
--master yarn \
--conf "spark.driver.extraClassPath=$libs" \
--conf "spark.executor.extraClassPath=$libs" \
...
/my/application/application-fat.jar \
param1 param2
我还不确定你是否可以将这些罐放在HDFS上。我们将它们放在应用程序jar旁边。
奇怪的是,使用Spark 1.6.1
我还在docs中找到了这个选项:
spark.driver.userClassPathFirst , spark.executor.userClassPathFirst
(实验)在驱动程序中加载类时,是否优先使用用户添加的jar优先于Spark自己的jar。此功能可用于缓解Spark的依赖项和用户依赖项之间的冲突。它目前是一个实验性功能。这仅在群集模式下使用。
但只需设置:
--conf "spark.driver.userClassPathFirst=true" \
--conf "spark.executor.userClassPathFirst=true" \
对我不起作用。所以我很高兴使用extraClassPath
!
干杯!
logback.xml
如果您在向Spark添加logback.xml
时遇到任何问题,我的问题可能会帮到您:
Pass system property to spark-submit and read file from classpath or custom path
答案 1 :(得分:3)
我遇到了同样的问题:我试图使用logback配置文件。我尝试了许多排列,但我没有让它发挥作用。
我使用这个SBT依赖项通过grizzled-slf4j访问logback:
"org.clapper" %% "grizzled-slf4j" % "1.3.0",
一旦我添加了log4j配置文件:
src/main/resources/log4j.properties/log4j.properties files.
我的日志工作正常。
答案 2 :(得分:0)
经过多次努力,我找到了另一种解决方案:库阴影。在我使用着色org.slf4j
之后,我的应用程序日志与spark日志分开。此外,我的应用程序jar中的logback.xml
很荣幸。
https://github.com/pjreddie/darknet/blob/master/python/darknet.py,在这种情况下,它归结为:
assemblyShadeRules in assembly += ShadeRule.rename(s"org.slf4j.**" -> "your_favourite_prefix.@0").inAll
在您的build.sbt
设置中。
附注 :如果您不确定是否实际出现了阴影,请在某些存档浏览器中打开您的jar并检查目录结构是否反映了阴影,在这种情况下,您的jar应包含路径/your_favourite_prefix/org/slf4j
,但不应包含/org/slf4j
答案 3 :(得分:0)
我将logback和log4j-to-slf4j以及其他依赖项和src / main / resources / logback.xml打包在一个胖子罐中。
当我运行“火花提交”时
--conf "spark.driver.userClassPathFirst=true" \
--conf "spark.executor.userClassPathFirst=true"
所有日志记录都是通过logback处理的。
答案 4 :(得分:0)
我必须修改Atais提出的解决方案才能使其在集群模式下工作。 这对我有用:
libs="/absolute/path/to/libs/*"
spark-submit \
--master yarn \
--deploy-mode cluster \
... \
--jars $libs \
--conf spark.driver.extraClassPath=log4j-over-slf4j-1.7.25.jar:logback-classic-1.2.3.jar:logback-core-1.2.3.jar:logstash-logback-encoder-6.4.jar \
--conf spark.executor.extraClassPath=log4j-over-slf4j-1.7.25.jar:logback-classic-1.2.3.jar:logback-core-1.2.3.jar:logstash-logback-encoder-6.4.jar \
/my/application/application-fat.jar \
param1 param2
根本原因是jar并非对所有节点都可用,并且必须显式地可用(即使使用--jars提交之后)。
更新:进一步完善解决方案。您还可以将罐子作为URL列表传递,即--jars url1,url2,url3
。这些jar仍必须添加到类路径中,以优先于log4j。