本文主要关于spark日志相关的部分内容,包括spark日志位置以及其中一种>日志配置方法,后续可再对本文进行其他配置方法的补充。
注意: 本文中使用的版本是spark2.2.1
最近平台中某租户使用的容器化集群spark集群,提交的spark streaming任务的日志没有进行级别控制,疯狂的刷日志,导致会占满宿主机的存储空间,并且集群均使用的是standalone模式,没有配置删除日志,所以日志太多会影响同一个宿主机下其他租户spark的使用。因此,在今天进行了一下集群的全局配置,直接将日志级别进行了更改,并且也趁这个时间,总结下对于spark日志相关的部分内容,后续有时间再进行其他补充。
Spark应用程序运行日志的存放
在很多情况下,我们需要查看driver和executor在运行spark程序时候产生的日志,以便于我们调试和查找问题。
Driver端日志
Spark Driver端的日志在默认情况下是直接输出在控制台的,driver本身没有stderr和stdout文件,所以只能在控制台输出。不过可以通过配置,将日志打印到文件中,具体配置方式后续再进行说明。
Executor端日志
Spark Executor日志的存放位置,是与Spark的部署模式相关的;Spark目前3种部署方式:Spark Standalone、Mesos、YARN。
- Spark Standalone模式:在spark standalone的部署模式下,可以直接在Master UI界面查看到相应的应用程序的日志,如下图。其中可以看到是有stderr和stdout两种日志,区别就是如果你在程序中使用了println(….)输出语句,并且位置是在executor端执行的,这些信息会在stdout文件里面显示,而在driver端执行的会直接在控制台输出;其余的Spark运行日志会在stderr文件里面显示。
通常,每个job的详细日志输出是在每个worker(slave)节点的work目录下,这个目录可以通过spark-env.sh下配置SPARK_WORKER_DIR参数(Directory to run applications in, which will include both logs and scratch space,默认情况下是SPARK_HOME/work)设置。
此部分,可以进行worker配置使日志自动删除,详情见后续“配置日志”部分。 - Mesos模式:在Mesos模式下,也可以通过Mesos的Master UI界面上看到相关的应用程序日志,这些日志是存储在Mesos Slave的work目录下的,通常情况下通过修改mesos-slave-env.sh或mesos-master-env.sh文件下的MESOS_log_dir参数和MESOS_work_dir参数可以设置slave或master的日志目录和工作目录,默认情况下是master和slave的logs都在/var/log/mesos下。
- YARN模式:在yarn模式下,executor和application master都运行在“containers”内。
YARN在一个application结束后,有2种处理容器内日志的方式。- 其中在yarn模式下,最简单的收集日志的方式是使用Yarn的日志收集工具(yarn logs -applicationId
),这个工具可以收集应用程序的相关日志,但是要求:a.开启了日志聚合功能(yarn.log-aggregation-enable,默认情况下这参数是false);b.application必须运行完,因为yarn必须先聚合这些日志。这种情况下,容器日志将被复制到HDFS,并删除本地日志,从而腾出更多空间。这样的话,这些日志可以在集群任何节点上用yarn logs命令查看:yarn logs -applicationId ,用这个命令会打印出指定应用的所有日志文件的内容,或者也可以直接在HDFS上查看这些日志(HDFS shell或者HDFS API)。这个目录的配置可以在YARN配置中指定(yarn.nodemanager.remote-app-log-dir和yarn.nodemanager.remote-app-log-dir-suffix)。这些日志同样还可以在Spark Web UI上Executors页查看,但是也有前提,需要启动Spark history server和MapReduce history server,再在yarn-site.xml中配置好yarn.log.server.url。Spark history server UI将把你重定向到MapReduce history server以查看这些聚合日志。 - 如果日志聚合没有开启,那么日志文件将在每台机器上的YARN_APP_LOGS_DIR目录保留,通常这个目录指向NodeManager节点(ResourceManager节点下没输出)的/tmp/logs或者$HADOOP_HOME/log/userlogs(这取决于Hadoop版本和安全方式)。查看日志的话,需要到每台机器上查看这些目录。子目录是按application ID和container ID来组织的。这些日志同样可以在 Spark Web UI上Executors页查看,而且这时你不需要运行MapReduce history server。这里还有一个需要注意的地方,在没有开启日志聚合的时候,有一个参数yarn.nodemanager.log.retain-seconds,这个参数指应用程序输入出日志的保存时间,默认是10800,单位s,也就是3个小时,所以有可能超过时间再去查看日志的时候,原来的运行日志就自动删除了。
额外补充:在yarn模式下,kill掉某个job(直接在UI界面或者是终端kill掉任务都是不对的,该任务可能还会继续执行下去,所以要用如下命令才算完全停止该job的执行),yarn application -kill。
- 其中在yarn模式下,最简单的收集日志的方式是使用Yarn的日志收集工具(yarn logs -applicationId
配置日志
Standalone日志自动删除配置
目前,在使用中,平台中的容器化的spark集群因为资源已经由docker+mesos+marathon控制了,一个集群只有一个租户使用,因此均是standalone模式部署的,不过在使用过程中,应用程序日志不会自动删除会占很大空间,因此进行了worker配置,使日志自动删除。
其中,更改了conf/spark-env.sh,设置SPARK_WORKER_OPTS,即在文件内增加:
export SPARK_WORKER_OPTS="-Dspark.worker.cleanup.enabled=true -Dspark.worker.cleanup.interval=3600 -Dspark.worker.cleanup.appDataTtl=604800 -Dspark.worker.ui.compressedLogFileLengthCacheSize=100"
参数解释:
(1)spark.worker.cleanup.enabled(默认false):仅在standalone模式下生效,开启后定期清理worker节点的worker目录下的应用程序日志,并且仅会清理已经停止的application日志。Enable periodic cleanup of worker / application directories. Note that this only affects standalone mode, as YARN works differently. Only the directories of stopped applications are cleaned up.
(2)spark.worker.cleanup.interval(默认1800):清理频率。Controls the interval, in seconds, at which the worker cleans up old application work dirs on the local machine.
(3)spark.worker.cleanup.appDataTtl(604800(7days,7243600)):The number of seconds to retain application work directories on each worker. This is a Time To Live and should depend on the amount of available disk space you have. Application logs and jars are downloaded to each application work dir. Over time, the work dirs can quickly fill up disk space, especially if you run jobs very frequently.
(4)spark.worker.ui.compressedLogFileLengthCacheSize(默认100):For compressed log files, the uncompressed file can only be computed by uncompressing the files. Spark caches the uncompressed file size of compressed log files. This property controls the cache size.
调试屏蔽日志
我们通常会使用IDE(例如Intellij IDEA)开发Spark应用,而程序调试运行时会在控制台中打印出所有的日志信息。它描述了(伪)集群运行、程序执行的所有行为。在很多情况下,这些信息对于我们来说是无关紧要的,我们更关心的是最终结果,无论是正常输出还是异常停止。我们可以通过log4j主动控制日志输出的级别。引入log4j.Logger和log4j.Level,并在对象中设置Logger.getLogger(“org”).setLevel(Level.ERROR),其中getLogger(class)的参数用途是追踪产生此日志的类。
import org.apache.log4j.{Level, Logger}
Logger.getLogger("org.apache.spark").setLevel(Level.WARN)
Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF)
Logger.getLogger("kafka.utils.VerifiableProperties").setLevel(Level.WARN)
这样的话,就可以看到我们关心的信息了。
补充说明参考:使用一个类Log4jUtils来专门处理Logger对象的声明等操作。
spark日志级别设置-log4j
在默认情况下,Spark应用程序的日志级别是INFO的,我们可以自定义Spark应用程序的日志输出级别,其中可以有多种方式:
- 局部应用设置:
针对SparkContext应用,Spark有专门的api设置日志级别,即sc.setLogLevel(级别),但是此方法只针对SparkContext相关的应用,而对Spark-streaming等应用无效果。 - 局部应用设置:
自己写一个log4j.properties文件,在里面设置日志级别,如:# Set everything to be logged to the console log4j.rootCategory=WARN, console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.target=System.err log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
这个文件设置了Spark应用程序在运行的时候会打出WARN级别的日志,然后在spark-submit提交应用程序的时候,通过–files参数,指定上述log4j.properties的文件路径,上传后即可即可使用这个配置打印应用程序的日志。
问题:Note that for the first option, both executors and the application master will share the same log4j configuration, which may cause issues when they run on the same node (e.g. trying to write to the same log file). - 局部应用设置:
在spark-submit的时候,在spark.driver.extraJavaOptions(对Spark Driver)或者spark.executor.extraJavaOptions(对SparkExecutor)增加 -Dlog4j.configuration=。但是在这里需要注意2个问题:如果使用了文件,需要protocol should be explicitly provided;并且这个文件需要存在在所有的节点上(and the file needs to exist locally on all the nodes)。 - 集群全局配置:
针对集群的log级别更改,可以到$SPARK_HOME/conf/log4j.properties文件里面进行修改,比如:# Set everything to be logged to the console log4j.rootCategory=WARN, console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.target=System.err log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n
这样设置,即进行了全局日志的设置。
在standalone模式下,如果不进行配置,集群默认使用的是org/apache/spark/log4j-defaults.properties配置,配置的话则如上操作。但是,如果在全局设置了之后,会同时生效Driver端和Executor端的日志级别。
这次我在更改容器内集群的配置日志级别配置的时候,就是用的此种方式,但是在集群中运行的程序,会通过driver端输出Info日志来表示程序运行的状态,而executor端确实是不需要关注Info日志的内容,因此需要进行driver端和executor端日志分离的情况操作,详情见下下述“Spark Streaming中应用日志的切分”的操作。log4j其他配置
在yarn模式下:
If you need a reference to the proper location to put log files in the YARN so that YARN can properly display and aggregate them, use spark.yarn.app.container.log.dir in your log4j.properties. For example,log4j.appender.file_appender.File=${spark.yarn.app.container.log.dir}/spark.log
For streaming applications, configuring RollingFileAppender and setting file location to YARN’s log directory will avoid disk overflow caused by large log files, and logs can be accessed using YARN’s log utility.
翻译:
如果你需要引用YARN放置日志文件的路径,以便YARN可以正确地展示和聚合日志,请在log4j.properties文件中使用spark.yarn.app.container.log.dir。例如,log4j.appender.file_appender.File=${spark.yarn.app.container.log.dir}/spark.log
对于流式应用,可以配置RollingFileAppender,并将文件路径设置为YARN日志目录,以避免磁盘打满,而且这些日志还可以利用YARN的日志工具访问和查看。
Spark Streaming中应用的日志切分
目前我们所用的集群是standalone模式,在Spark Standalone模式下,spark默认使用org/apache/spark/log4j-defaults.properties配置,所有的日志都记录在stderr里面,由于Spark Streaming应用程序是一直运行的,时间长了以后stderr文件会非常大,占用空间的同时难以让我们调试和定位问题,所以我们需要切分日志,spark原生提供了对Executor日志的切分,Driver日志需要我们单独配置log4j。
Executor端日志切分,在spark应用环境变量中配置:
spark.executor.logs.rolling.strategy time //可以按日期(time)和日志大小(size)来切分日志 spark.executor.logs.rolling.time.interval daily //可以按天、小时、分钟切分 spark.executor.logs.rolling.maxRetainedFiles 7 //保留多少个日志文件,旧的日志自动删除
或者也可以自定义日志生成方式,同上述3方法,只要配置参数spark.executor.extraJavaOptions,指定log4j配置。
Driver日志切分
spark-submit的时候自定义log4j,–driver-java-options “-Dlog4j.configuration=file:/路径/log4j-driver.properties -Dapp.logging.name=NAME” ,app.logging.name在log4j文件里面配置了,用NAME去区分不同spark应用。
使用示例-Driver端单独设置log4j配置,并且设置额外输出日志到文件中:
背景:
集群中的日志级别设置的是WRAN,但是程序中Driver端的日志需要输出INFO级别的日志并且显示在控制台上,因此使用此方法进行了Driver端的日志级别单独设置。并且对于Driver端的日志,如果不进行设置的话,均会输出到控制台中,因此通过设置,尝试将日志输出到文件(这里需要注意的是,log4j的控制仅仅针对于的是日志,对于在程序里执行的print语句,仍然会输出到控制台中,并不会输出到定义的日志文件)。
log4j文件设置:# # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. log4j.rootCategory=INFO, console,FILE # 针对控制台设置:Set everything to be logged to the console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.target=System.err log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n # 针对日志输出到FILE log4j.appender.FILE=org.apache.log4j.FileAppender log4j.appender.FILE.Threshold=DEBUG # 输出到的日志路径:${app.logging.name}为外部引入的应用名称来定义日志文件的名字 log4j.appender.FILE.file=/usr/tools/logs/${app.logging.name}-driver.log log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n # Set the default spark-shell log level to WARN. When running the spark-shell, the # log level for this class is used to overwrite the root logger's log level, so that # the user can have different defaults for the shell and regular Spark apps. log4j.logger.org.apache.spark.repl.Main=WARN # Settings to quiet third party logs that are too verbose log4j.logger.org.spark_project.jetty=WARN log4j.logger.org.spark_project.jetty.util.component.AbstractLifeCycle=ERROR log4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=WARN log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=WARN log4j.logger.org.apache.parquet=ERROR log4j.logger.parquet=ERROR # SPARK-9183: Settings to avoid annoying messages when looking up nonexistent UDFs in SparkSQL with Hive support log4j.logger.org.apache.hadoop.hive.metastore.RetryingHMSHandler=FATAL log4j.logger.org.apache.hadoop.hive.ql.exec.FunctionRegistry=ERROR
提交命令:
spark-submit --master spark://master01:7077,master02:7077 --jars /usr/tools/spark_234gsignalling/sparkStreamingKafka0.8-0.10Jars/spark-streaming-kafka-0-8_2.11-2.2.1.jar,/usr/tools/spark_234gsignalling/sparkStreamingKafka0.8-0.10Jars/kafka_2.11-0.8.2.1.jar,/usr/tools/spark_234gsignalling/sparkStreamingKafka0.8-0.10Jars/metrics-core-2.2.0.jar,/usr/tools/spark_234gsignalling/sparkStreamingKafka0.8-0.10Jars/kafka-clients-0.10.1.1.jar --files "/usr/tools/spark_234gsignalling/kafka_client_jaas.conf" --driver-java-options "-Dlog4j.configuration=file:/usr/tools/spark_234gsignalling/log4j4g.properties -Dapp.logging.name=NAME" --driver-memory 2g --total-executor-cores 36 --executor-cores 2 --executor-memory 10g --class com.sitech.signallingStreaming.Signalling234gMask 4gsignallingSpark-0.10kafka-c60.jar skhconf_4g hdfs://dcoshdfs/spark_log/4g_info_c60log
提交命令中对于此次日志设置的主要是:
--driver-java-options "-Dlog4j.configuration=file:/usr/tools/spark_234gsignalling/log4j4g.properties -Dapp.logging.name=NAME"
参考文章:
- Spark应用程序运行的日志存在哪里
- sparkstreaming日志切分配置
- spark日志log4j配置-控制台+文件同时输出
- spark深入:配置文件与日志(yarn)
- log4j配置1,log4j配置2
- spark2.2-yarn,spark2.2-standalone,spark2.2-mesos
至此,本篇内容完成。