【问题标题】:Separating application logs in Logback from Spark Logs in log4j将 Logback 中的应用程序日志与 log4j 中的 Spark 日志分开
【发布时间】:2017-06-26 20:05:54
【问题描述】:

我有一个使用 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 执行 jars 代码,则绑定到 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.jarslf4j-log4j12-1.7.16.jar 都在 /usr/local/spark/jars 中,尽管我的 pom.xml 中排除了这些 jars,但 Spark 很可能引用了这些 jars,因为如果我删除它们,我就是在 spark-submit 运行时给出 ClassNotFoundException。

我的问题是:有没有办法使用 Logback 在我的应用程序中实现本机日志记录,同时保留 Spark 的内部日志记录功能。理想情况下,我想将我的 Logback 应用程序日志写入文件并允许 Spark 日志仍显示在 STDOUT 中。

【问题讨论】:

    标签: scala maven logging apache-spark jar


    【解决方案1】:

    我遇到了同样的问题:我试图使用 logback 配置文件。我尝试了很多排列,但我没有让它工作。

    我正在使用这个 SBT 依赖项通过 grizzled-slf4j 访问 logback:

    "org.clapper" %% "grizzled-slf4j" % "1.3.0",
    

    一旦我添加了 log4j 配置文件:

    src/main/resources/log4j.properties/log4j.properties files.
    

    我的日志记录工作正常。

    【讨论】:

    • 最终使用了相同的方法,不幸的是这仍然使用 log4j 作为底层框架。仍然想知道是否有人能够配置 Logback。
    • 我浪费了很多时间寻找。但我想了解自己。
    【解决方案2】:

    我遇到了一个非常相似的问题。

    我们的构建与您的相似(但我们使用了sbt)并在此处详细描述:https://stackoverflow.com/a/45479379/1549135

    在本地运行此解决方案可以正常工作,但随后 spark-submit忽略所有排除项和新的日志记录框架 (logback),因为 spark 的类路径优先于部署的罐子。由于它包含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的类路径!

    所以现在解决方案应该很明显了。

    1。下载这些罐子:

    - log4j-over-slf4j-1.7.25.jar
    - logback-classic-1.2.3.jar
    - logback-core-1.2.3.jar
    

    2。运行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
    

    我只是不确定您是否可以将这些 jar 放在 HDFS 上。我们将它们放在本地应用程序 jar 旁边。

    userClassPathFirst

    奇怪的是,使用Spark 1.6.1 我也在文档中找到了这个选项:

    spark.driver.userClassPathFirstspark.executor.userClassPathFirst

    (实验性)在驱动程序中加载类时,是否让用户添加的 jar 优先于 Spark 自己的 jar。此功能可用于缓解 Spark 的依赖项和用户依赖项之间的冲突。它目前是一个实验性功能。这仅用于集群模式。

    但只是设置:

    --conf "spark.driver.userClassPathFirst=true" \
    --conf "spark.executor.userClassPathFirst=true" \
    

    对我不起作用。所以我很乐意使用extraClassPath

    干杯!


    加载中logback.xml

    如果您在将logback.xml 加载到 Spark 时遇到任何问题,我的问题可能会帮助您: Pass system property to spark-submit and read file from classpath or custom path

    【讨论】:

    • 我正在尝试遵循这个,因为我有同样的问题,我实际上希望 spark-submit 将日志发送到 loggy,所以我使用 logback 和 loggly appender。我使用 maven 和一个 uber jar,其中包含要在 spark-submit 中发送的所有文件。但是那里的类路径是什么?我没有看到任何可行的绝对路径。你能澄清那部分吗?
    • 我不明白这个问题:“但是那里的类路径是什么?”我认为你最好提出你的问题并描述你在那里做了什么。
    • 发现了我的问题。我无法让它与你上面所说的一起工作,但我可以通过对 org.slf4j 进行着色(如@matemaciek 的回答中所述)。将在我上面发布的票中详细说明我的整个解决方案。
    • 这是因为你不能将 logback 打包在 fat jar 中。它们必须在我的解决方案中从外部提供。这是由于 spark 中的类路径优先级。
    【解决方案3】:

    经过一番努力,我找到了另一个解决方案:库阴影

    在我为 org.slf4j 着色后,我的应用程序日志与 spark 日志分离。此外,我的应用程序 jar 中的 logback.xml 很荣幸。

    Here you can find information on library shading in sbt,在这种情况下归结为:

    assemblyShadeRules in assembly += ShadeRule.rename("org.slf4j.**" -> "your_favourite_prefix.@0").inAll
    

    在您的build.sbt 设置中。


    旁注:如果您不确定是否真的发生了阴影,请在某个存档浏览器中打开您的 jar,并检查目录结构是否反映了阴影,在这种情况下,您的 jar 应该包含路径/your_favourite_prefix/org/slf4j,但不是/org/slf4j

    【讨论】:

    • 我的意图是使用我选择的框架:应用程序和火花,我的解决方案允许它。但是是的,如果你想要单独的配置阴影很好
    • 这是在我的案例中唯一有效的解决方案。所有其他提到的选项(以及它们的各种组合)都不成功
    • 我在 maven 中成功着色 slf4j,但收到错误:SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder"。 SLF4J:默认为无操作 (NOP) 记录器实现 SLF4J:有关详细信息,请参阅 slf4j.org/codes.html#StaticLoggerBinder。还有什么你没有提到的需要设置的吗?
    【解决方案4】:

    我将 logback 和 log4j-to-slf4j 以及我的其他依赖项和 src/main/resources/logback.xml 打包在一个胖 jar 中。

    当我运行 spark-submit 时

    --conf "spark.driver.userClassPathFirst=true" \
    --conf "spark.executor.userClassPathFirst=true"
    

    所有日志记录都由 logback 处理。

    【讨论】:

    • 我在尝试时收到以下错误:线程“主”java.lang.LinkageError 中的异常:加载程序约束违规:解析方法时“org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg /slf4j/ILoggerFactory;"当前类 org/slf4j/LoggerFactory 的类加载器(org/apache/spark/util/ChildFirstURLClassLoader 的实例)和方法定义类 org 的类加载器(sun/misc/Launcher$AppClassLoader 的实例) /slf4 j/impl/StaticLoggerBinder,对于签名中使用的类型 org/slf4j/ILoggerFactory 有不同的 Class 对象
    【解决方案5】:

    我不得不修改 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
    

    根本原因是 jars 并非对所有节点都可用,必须明确提供(即使在使用 --jars 提交之后)。

    更新:进一步完善了解决方案。您还可以将罐子作为 url 列表传递,即--jars url1,url2,url3。这些 jar 仍然必须添加到类路径中才能优先于 log4j。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-12
      • 1970-01-01
      • 2019-04-03
      • 2015-04-02
      相关资源
      最近更新 更多