zl程序教程

您现在的位置是:首页 >  大数据

当前栏目

spark如何自定义log4j配置 及 yarn的应用日志存储

2023-09-11 14:16:24 时间

在实际工作中,需要针对spark任务的日志输出进行自定义。

1. 相关知识

1.1 spark 日志介绍

以spark2.4.x为例,spark中提供了log4j的方式记录日志,目前使用的版本是log4j-1.2.17,基于properties方式配置

可以在$SPARK_HOME/conf/下,将 log4j.properties.template 文件copy为 log4j.properties 来启用log4j配置。但这个配置为全局配置,不能单独配置某个job的运行日志。

在Spark的conf目录下,把log4j.properties.template修改为log4j.properties,内容如下:

可以看出,针对repl.Main(即交互模式下)、jetty使用WARN级别,parquet使用ERROR级别等,其它使用INFO级别,日志输出到console

可以根据需要调整不同模块的日志级别。

# Set everything to be logged to the console
log4j.rootCategory=INFO, 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

# 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=INFO
log4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=INFO
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

1.2 yarn模式下应用的日志存储

在YARN术语中,执行者和应用程序masters在“容器”内部运行。在应用程序完成后,YARN有两种处理容器日志的模式。

如果打开日志聚合(使用 yarn.log-aggregation-enable配置),容器日志将复制到HDFS中,而本地计算机上的日志将被删除。

1.2.1 重要的日志相关的参数

yarn-site.xml中有几个配置项与日志的保存与删除,本地job执行文件的保留与删除有关。若不做配置,日志和job.jar会在application执行结束后被直接删除掉。

参数名称说明
yarn.log-aggregation-enabletrue执行结束后收集(聚合)各个container本地的日志
yarn.nodemanager.remote-app-log-dir/app-logs聚合日志后在hdfs的存放地址
yarn.nodemanager.remote-app-log-dir-suffixlogs聚合日志存放的后缀,存放地址由 ${remote-app-log-dir}/${user}/{thisParam}构成
yarn.log-aggregation.retain-seconds2592000  聚合日志在hdfs上的保留时间,以秒为单位,到时后被删除,保留30天后删除
yarn.log.server.urlhttp://hostname:19888/jobhistory/logsmapreduce JobHistoryServer log server的地址
yarn.log.server.web-service.urlhttp://hostname:8188/ws/v1/applicationhistoryApplicationHistoryServer的log server的地址
yarn.nodemanager.local-dirs/hadoop/yarn/local存放 application 运行的本地文件(计算过程中的中间数据存储)的根目录,执行完毕后删除,按用户名存储
yarn.nodemanager.log-dirs/hadoop/yarn/log存放 container 运行的本地日志的根目录,执行完毕后删除,按用户名存储
yarn.nodemanager.log.retain-seconds604800            本地日志的保留时间,只有aggregation没有enable时才生效 ,保留7天后删除
yarn.nodemanager.delete.debug-delay-sec0yarn 的 DeletionService 在任务结束多长时间后,删除本地化的日志(yarn.nodemanager.local-dirs)                                       和container的运行日志(yarn.nodemanager.log-dirs)。 这个时间最好设置的大一点

1.2.2 通过yarn logs查看日志

查看日志可以通过 yarn logs 命令从群集中的任何位置查看。

yarn logs -applicationId <app ID>

功能命令
查看am的日志yarn logs -applicationId application_1619146133746_0023 -am 1
显示各container日志的汇总信息yarn logs -applicationId application_1619146133746_0023 -show_container_log_info
查看某一个container的日志yarn logs -applicationId application_1619146133746_0023 --containerId container_e05_1619146133746_0023_01_000002
显示某个Application的containerId信息yarn logs -applicationId application_1619146133746_0023 -show_application_log_info
查看某一个container指定日志文件的日志yarn logs -applicationId application_1619146133746_0023 --containerId container_e05_1619146133746_0023_01_000002 -log_files driver.log,executor.log,stdout,stderr

该命令会将指定的应用程序日志从所有的容器中打印所有的日志内容,可以使用HDFS shell 或API直接在HDFS中查看容器日志文件。

日志所在的目录参考YARN配置(yarn.nodemanager.remote-app-log-dir和 yarn.nodemanager.remote-app-log-dir-suffix

    <property>
      <name>yarn.nodemanager.remote-app-log-dir</name>
      <value>/app-logs</value>
    </property>

    <property>
      <name>yarn.nodemanager.remote-app-log-dir-suffix</name>
      <value>logs</value>
    </property>

  

1.2.3 通过spark historyserver查看

日志可以通过Spark history serverk web中某一个application 页面的Executors 标签页查看。

需要运行如下历史服务器

Spark HistoryServer【org.apache.spark.deploy.history.HistoryServer】、

MapReduce历史服务器【org.apache.hadoop.mapreduce.v2.hs.JobHistoryServer】、

应用历史服务器【org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryServer】

并在yarn-site.xml正确配置yarn.log.server.url

Spark HistoryServer UI上的日志URL会将通过NodeManger的服务将日志重定向到MapReduce历史记录服务器以显示聚合日志。

如日志从http://hdp01:8042/node/containerlogs/container_e05_1619146133746_0023_01_000001/root/stdout?start=-4096 重定向到:http://hdp03:19888/jobhistory/logs/hdp01:45454/container_e05_1619146133746_0023_01_000001/container_e05_1619146133746_0023_01_000001/root/stdout?start=-4096

1.2.4. 日志未聚合情况

如果日志聚合未打开时,日志将保存在每台计算机上的本地yarn.nodemanager.log-dirs,通常配置为 /tmp/logs 或 /hadoop/yarn/log,取决于Hadoop版本和安装配置。

查看容器的日志需要转到包含它们的主机并查看此目录。子目录按应用程序ID / 容器ID组织日志文件。

日志可以通过Spark history server web中某一个application 页面的Executors 标签页查看,并且不需要运行MapReduce历史记录服务器。

要查看每个容器的启动环境,请增加 yarn.nodemanager.delete.debug-delay-sec 一个较大的值(例如36000),然后通过 yarn.nodemanager.local-dirs 在启动容器的节点上访问应用程序缓存。

该目录包含启动脚本,JAR以及用于启动每个容器的所有环境变量。这个过程特别适用于调试类路径问题。

(请注意,启用此功能需要具有集群设置的管理权限并重新启动所有节点管理器。因此,这不适用于托管群集)。

2. 如何自定义log4j配置

官方文档:https://spark.apache.org/docs/latest/running-on-yarn.html  或 https://spark.apache.org/docs/2.4.7/running-on-yarn.html

需要分别为应用主程序(即driver端)、执行程序(即executor端)自定义log4j配置,自定义配置的方法如下:

  1. 通过--files上传log4j的配置文件,

  2. 在针对driver的配置spark.driver.extraJavaOptions或针对executors的配置spark.executor.extraJavaOptions增加-Dlog4j.configuration=<location of configuration file>配置.  注意:1. 如果使用本地文件,则file:文件协议必须添加,且保证都有节点都有此文件   2. driver与executor最好不要使用同一个log4j配置文件,因为如果driver、executor运行在相同的节点下,使用同一个文件可能会导致问题

  3. 通过更新 $SPARK_CONF_DIR/log4j.properties 文件实现。此文件与其它配置文件会在应用启动时自动上传。注意1、2的优先级高于这个。

注意:

1. driver端还可以使用spark-submit的--driver-java-options参数去配置。

2. 如果您需要一个对正确位置的引用来将日志文件放在YARN中,以便YARN能够正确地显示和聚合它们,在log4j中使用spark.yarn.app.container.log.dir。在程序执行完成时,本地的日志会被删除

3. 对于流式应用程序,配置RollingFileAppender并将文件位置设置到YARN的日志目录将避免大日志文件导致的磁盘溢出,并且可以使用YARN的日志工具程序访问日志。

4. 如果想将日志输出到本地文件,则需要将日志文件路径配置为绝对目录,且每个节点都存在,并且spark程序的执行用户有写的权限。注意:日志不会自动删除,需要自己手工删除。

5.  spark.yarn.app.container.log.dir  即YARN NodeManager Log directories/applicationId/containerId

    如配置目录是:/hadoop/yarn/log

    则日志位于 /hadoop/yarn/log/application_1609229902865_47270/container_e04_1609229902865_47270_01_000002/driver.log

3. 配置示例

3.1. 启动脚本示例

注意: --jars 可以根据需要增加一些业务依赖的第三方jar, 示例中的slf4j-api不是必须的。

# yarn client提交
"spark.driver.extraJavaOptions=-Dlog4j.configuration=file:///opt/test/conf/log4j-driver.xml" --conf "spark.executor.extraJavaOptions=-Dlog4j.configuration=log4j-executor.xml" --files=/opt/test/conf/log4j-driver.xml,/opt/test/conf/log4j-executor.xml --jars "/opt/test/slf4j-api-1.7.25.jar" spark-examples_2.11-2.4.7.jar 10


# yarn cluster提交,使用spark.driver.extraJavaOptions
 spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster --driver-memory 1g --executor-memory 1g  --executor-cores 1  --conf "spark.driver.extraJavaOptions=-Dlog4j.configuration=log4j-driver.xml" --conf "spark.executor.extraJavaOptions=-Dlog4j.configuration=log4j-executor.xml" --files=/opt/test/conf/log4j-driver.xml,/opt/test/conf/log4j-executor.xml --jars "/opt/test/slf4j-api-1.7.25.jar" spark-examples_2.11-2.4.7.jar 10


# yarn cluster提交,使用driver-java-options 
 spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster --driver-memory 1g --executor-memory 1g  --executor-cores 1 --driver-java-options "-Dlog4j.configuration=log4j-driver.xml" --conf "spark.executor.extraJavaOptions=-Dlog4j.configuration=log4j-executor.xml" --files=/opt/test/conf/log4j-driver.xml,/opt/test/conf/log4j-executor.xml --jars "/opt/test/slf4j-api-1.7.25.jar" spark-examples_2.11-2.4.7.jar 10

3.2. 测试源代码

object SparkPi {
  val logger: Logger = LoggerFactory.getLogger(this.getClass)

  def main(args: Array[String]) {
    logger.info(s"args size={},{}", args.length, args(0))
    logger.info(s"计算pi")

    logger.info(s"创建spark session")
    val spark = SparkSession
      .builder
      .appName("Spark Pi")
      .master("local[2]")
      .getOrCreate()

    val slices = if (args.length > 0) args(0).toInt else 2
    logger.info("打印参数:{}", args)
    val n = math.min(100000L * slices, Int.MaxValue).toInt // avoid overflow
    val count = spark.sparkContext.parallelize(1 until n, slices).map { i =>
      val x = random * 2 - 1
      val y = random * 2 - 1
      if(i % 10000 == 0) {
        logger.info("i:{},x:{},y:{}", i+"", x+"", y+"")
      }
      if (x * x + y * y <= 1) 1 else 0
    }.reduce(_ + _)
    logger.info(s"Pi is roughly ${4.0 * count / (n - 1)}")
    logger.info(s"退出")
    spark.stop()
    logger.info(s"退出完成")
  }
}

3.3. log4j配置示例

log4j-driver.xml  log4j-executor.xml可以根据需要进行调整,但file的配置即日志文件名称不要相同,如driver.log, executor.log

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
        <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
                <param name="encoding" value="UTF-8" />
                <param name="target" value="System.out" />
                <layout class="org.apache.log4j.PatternLayout">
                        <param name="ConversionPattern" value="%d %p [%c:%L] - %m%n" />
                </layout>
        </appender>

        <appender name="FileAppender" class="org.apache.log4j.RollingFileAppender">
                <param name="file" value="${spark.yarn.app.container.log.dir}/executor.log" />
                <param name="append" value="true" />
                <param name="encoding" value="UTF-8" />
                <param name="maxFileSize" value="1073741824" />
                <param name="maxBackupIndex" value="10" />
                <layout class="org.apache.log4j.PatternLayout">
                        <param name="ConversionPattern" value="%d{yyy-MM-dd HH\:mm\:ss,SSS} %p [%c:%L] - %m%n" />
                </layout>
        </appender>

        <appender name="ASYNCFileAppender" class="org.apache.log4j.AsyncAppender">
                <param name="BufferSize" value="16384" />
                <appender-ref ref="FileAppender" />
        </appender>

        <logger name="org.apache.spark" additivity="false">
                <level value="WARN"/>
                <appender-ref ref="CONSOLE" />
        </logger>
         <logger name="org.apache.spark.examples" additivity="false">
                <level value="INFO"/>
                <appender-ref ref="CONSOLE"/>
        </logger>
        <logger name="org.spark_project" additivity="false">
                <level value="WARN"/>
                <appender-ref ref="FileAppender"/>
        </logger>

        <root>
                <level value="INFO" />
               <appender-ref ref="CONSOLE" />
               <appender-ref ref="ASYNCFileAppender" />
        </root>

</log4j:configuration>

4. 日志输出示例