【问题标题】:How to suppress Spark logging in unit tests?如何在单元测试中抑制 Spark 日志记录?
【发布时间】:2014-12-02 12:03:21
【问题描述】:

感谢我尝试过的易于搜索的博客:

import org.specs2.mutable.Specification

class SparkEngineSpecs extends Specification {
  sequential

  def setLogLevels(level: Level, loggers: Seq[String]): Map[String, Level] = loggers.map(loggerName => {
    val logger = Logger.getLogger(loggerName)
    val prevLevel = logger.getLevel
    logger.setLevel(level)
    loggerName -> prevLevel
  }).toMap

  setLogLevels(Level.WARN, Seq("spark", "org.eclipse.jetty", "akka"))

  val sc = new SparkContext(new SparkConf().setMaster("local").setAppName("Test Spark Engine"))

  // ... my unit tests

但不幸的是它不起作用,我仍然得到很多火花输出,例如:

14/12/02 12:01:56 INFO MemoryStore: Block broadcast_4 of size 4184 dropped from memory (free 583461216)
14/12/02 12:01:56 INFO ContextCleaner: Cleaned broadcast 4
14/12/02 12:01:56 INFO ContextCleaner: Cleaned shuffle 4
14/12/02 12:01:56 INFO ShuffleBlockManager: Deleted all files for shuffle 4

【问题讨论】:

    标签: scala log4j apache-spark


    【解决方案1】:

    将以下代码添加到src/test/resources目录内的log4j.properties文件中,如果文件/目录不存在则创建

    # Change this to set Spark log level
    log4j.logger.org.apache.spark=WARN
    
    # Silence akka remoting
    log4j.logger.Remoting=WARN
    
    # Ignore messages below warning level from Jetty, because it's a bit verbose
    log4j.logger.org.eclipse.jetty=WARN
    

    当我运行我的单元测试时(我使用的是 JUnit 和 Maven),我只收到 WARN 级别的日志,换句话说,不再有 INFO 级别的日志混乱(尽管它们有时对调试很有用)。

    我希望这会有所帮助。

    【讨论】:

    • 适用于具有 specs2 的 SBT
    • 感谢@Emre。它就像 intelliJ idea 中的 java 魅力一样。
    • 新版本使用 log4j2.properties(特别是 2.4.0)
    【解决方案2】:

    就我而言,我自己的一个库将 logback-classic 纳入其中。这在开始时就出现了警告:

    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Found binding in [jar:file:/home/alex/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.1.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: Found binding in [jar:file:/home/alex/.ivy2/cache/org.slf4j/slf4j-log4j12/jars/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    

    我通过从依赖项中排除它来解决这个问题:

    "com.mystuff" % "mylib" % "1.0.0" exclude("ch.qos.logback", "logback-classic")
    

    现在我可以在 test/resources 中添加一个 log4j.properties 文件,现在 Spark 会使用它。

    【讨论】:

      【解决方案3】:

      在对 Spark 日志输出也苦苦挣扎了一段时间后,我找到了一个 blog post,其中包含我特别喜欢的解决方案。

      如果您使用 slf4j,可以简单地交换底层日志实现。测试范围的一个很好的候选者是 slf4j-nop,它小心翼翼地获取日志输出并将其放在阳光不照耀的地方。

      使用 Maven 时,您可以将以下内容添加到依赖项列表的顶部:

      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.12</version>
        <scope>provided</scope>
      </dependency>
      
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-nop</artifactId>
        <version>1.7.12</version>
        <scope>test</scope>
      </dependency>
      

      请注意,将它放在依赖项列表的开头可能很重要,以确保使用给定的实现而不是其他包可能附带的实现(并且您可以考虑将其排除以保持您的类路径整洁,避免意外冲突)。

      【讨论】:

        【解决方案4】:

        您可以使用单独的 Logback 配置进行测试。根据您的环境,您可能只需要使用隐藏日志的东西创建conf/logback-test.xml。我认为应该这样做:

        <configuration>
          <root level="debug">
          </root>
        </configuration>
        

        据我了解,这会捕获所有日志(级别 debug 及更高级别)并且不为它们分配任何记录器,因此它们会被丢弃。更好的选择是为它们配置文件记录器,这样您仍然可以根据需要访问日志。

        有关详细文档,请参阅http://logback.qos.ch/manual/configuration.html

        【讨论】:

        • 感谢您的回答,所以我尝试在“src/test/resources/conf”中添加一个名为“logback-test.xml”的文件,其中包含您提供的内容(也尝试使用“警告”级别),但它没有效果:(
        • 我真的不确定这一切,对不起。但是我们的项目在测试期间将日志定向到一个文件,所以我们只需要弄清楚它是如何设置的:)。 logback-test.xml&lt;project root&gt;/conf 这里。我们的源代码中没有提到该文件,因此它可能是一个神奇的默认位置。我没有看到任何其他内容...如果我删除该文件,我会在测试期间开始获取日志输出(从warn 向上)。我们使用 Scalatest。
        • 我试着把它放在proj-root/conf/ 没有运气。我有点困惑,自从文件名和内容引用火花以来,它是如何被挑选出来的。与此同时,我正在使用一个非常 hacky 的脚本,它使用正则表达式执行 grep -v,这有点删除了 spark 日志。
        • 粘贴到test/resources/logback.xml
        【解决方案5】:

        聚会有点晚了,但我在spark example code 找到了这个:

        def setStreamingLogLevels() {
            val log4jInitialized = Logger.getRootLogger.getAllAppenders.hasMoreElements
            if (!log4jInitialized) {
                // We first log something to initialize Spark's default logging, then we override the
                // logging level.
                logInfo("Setting log level to [WARN] for streaming example." +
                " To override add a custom log4j.properties to the classpath.")
                Logger.getRootLogger.setLevel(Level.WARN)
            }
        }
        

        我还发现,如果您像下面这样调用 setLogLevels,您的代码会为我减少很多输出。

        setLogLevels(Level.WARN, Seq("spark", "org", "akka"))
        

        【讨论】:

          【解决方案6】:

          对我来说最简单的解决方案是:

          cp $SPARK_HOME/conf/log4j.properties.template $YOUR_PROJECT/src/test/resources/log4j.properties
          sed -i -e 's/log4j.rootCategory=INFO/log4j.rootCategory=WARN/g' $YOUR_PROJECT/src/test/resources/log4j.properties
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-01-26
            • 2018-11-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-07-22
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多