如何在单元测试中抑制Spark记录?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在单元测试中抑制Spark记录?相关的知识,希望对你有一定的参考价值。
所以感谢我试过的易于谷歌的博客:
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
将以下代码添加到log4j.properties
目录中的src/test/resources
文件中,如果不存在则创建文件/目录
# 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级别日志(虽然它们在调试时很有用)。
我希望这有帮助。
在我的情况下,我自己的一个库带来了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")
现在我可以在log4j.properties
中添加一个test/resources
文件,现在被Spark使用了。
经过一段时间与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>
请注意,将它放在依赖项列表的开头可能很重要,以确保使用给定的实现而不是其他包可能带来的实现(并且您可以考虑排除这些实现以保持类路径整洁并避免意外冲突)。
派对有点晚了,但我在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"))
您可以使用单独的Logback配置进行测试。根据您的环境,您可能需要使用隐藏日志的内容创建conf/logback-test.xml
。我认为这应该这样做:
<configuration>
<root level="debug">
</root>
</configuration>
据我所知,它捕获所有日志(级别debug
和更高)并且不为它们分配记录器,因此它们被丢弃。更好的选择是为它们配置文件记录器,因此您仍可以根据需要访问日志。
有关详细文档,请参阅http://logback.qos.ch/manual/configuration.html。
最适合我的解决方案是:
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
以上是关于如何在单元测试中抑制Spark记录?的主要内容,如果未能解决你的问题,请参考以下文章
运行单元测试时如何抑制来自 node.js 应用程序的应用程序日志消息?
Python如何抑制单元测试的tkinter mainloop()
使用带有事务的内存数据库进行单元测试时,如何抑制 InMemoryEventId.TransactionIgnoredWarning?