【问题标题】:Using log4j2 in Spark java application在 Spark java 应用程序中使用 log4j2
【发布时间】:2017-02-27 22:07:17
【问题描述】:

我正在尝试在我的 Spark 作业中使用 log4j2 记录器。基本要求:log4j2 配置位于类路径之外,因此我需要明确指定其位置。当我直接在 IDE 中运行我的代码而不使用 spark-submit 时,log4j2 运行良好。但是,当我使用spark-submit 向 Spark 集群提交相同的代码时,它无法找到 log42 配置并回退到默认的旧 log4j。

启动器命令

${SPARK_HOME}/bin/spark-submit \
--class my.app.JobDriver \  
--verbose \
--master 'local[*]' \
--files "log4j2.xml" \
--conf spark.executor.extraJavaOptions="-Dlog4j.configurationFile=log4j2.xml" \
--conf spark.driver.extraJavaOptions="-Dlog4j.configurationFile=log4j2.xml" \
myapp-SNAPSHOT.jar

maven 中的 Log4j2 依赖项

<dependencies>
. . . 
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <!-- Bridge log4j to log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-1.2-api</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
        <!-- Bridge slf4j to log4j2 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j2.version}</version>
        </dependency>
 <dependencies>

有什么我可能会错过的想法吗?

【问题讨论】:

    标签: apache-spark log4j log4j2


    【解决方案1】:

    目前在 Spark 中显然没有官方对 log4j2 的支持。以下是关于该主题的详细讨论:https://issues.apache.org/jira/browse/SPARK-6305

    在实际方面,这意味着:

    1. 如果您可以访问 Spark 配置和 jar 并且可以修改它们,则在手动将 log4j2 jar 添加到 SPARK_CLASSPATH 并将log4j2 配置文件提供给 Spark 后,您仍然可以使用log4j2

    2. 如果您在托管 Spark 集群上运行并且无法访问 Spark jars/configs,那么您仍然可以使用 log4j2,但是它的使用将仅限于在驱动程序端执行的代码。执行器运行的任何代码部分都将使用 Spark 执行器记录器(旧的 log4j)

    【讨论】:

    • 你能解释一下怎么做吗?我确实可以完全访问 Spark,并且可以修改配置文件、jar 等。我下载了一些 log4j2 jar 并将它们放在“extra_jars”目录中,并使用类路径在 spark-env.sh 上引用了它们。然而,Spark 直到使用旧的 log4j 作为内部日志......
    • 提醒一下,如果log4j2 jar已经添加到executor类路径中,那么log4j2无论如何都会被使用,所以必须添加log4j2.xml文件到executor中。
    【解决方案2】:

    Spark 回退到 log4j,因为它可能无法在启动期间初始化日志系统(您的应用程序代码未添加到类路径)。

    如果您被允许在集群节点上放置新文件,则在所有这些节点上创建目录(例如 /opt/spark_extras),将所有 log4j2 jar 放在那里,并向 spark-submit 添加两个配置选项:

    --conf spark.executor.extraClassPath=/opt/spark_extras/*
    --conf spark.driver.extraClassPath=/opt/spark_extras/*
    

    然后库将被添加到类路径中。

    如果您无权修改集群上的文件,您可以尝试另一种方法。使用 --jars 将所有 log4j2 jar 添加到 spark-submit 参数。根据documentation,所有这些库都将添加到驱动程序和执行程序的类路径中,因此它应该以相同的方式工作。

    【讨论】:

    • 是的,显然这是根本原因:“Spark 无法正确初始化日志系统”。关于此的一些额外细节:
    【解决方案3】:

    尝试使用 --driver-java-options

    ${SPARK_HOME}/bin/spark-submit \    
    --class my.app.JobDriver \      
    --verbose \    
    --master 'local[*]' \    
    --files "log4j2.xml" \    
    --driver-java-options "-Dlog4j.configuration=log4j2.xml" \    
    --jars log4j-api-2.8.jar,log4j-core-2.8.jar,log4j-1.2-api-2.8.jar \    
    myapp-SNAPSHOT.jar  
    

    【讨论】:

    • 我认为您缺少为驱动程序和执行程序将这些 jar 添加到 ExtraClassPath 中
    【解决方案4】:

    如果在您自己的依赖项之一中使用 log4j2,则很容易对所有配置文件进行双向传递,并为一两个高级记录器使用编程配置,如果并且仅当找不到配置文件时。

    下面的代码可以解决问题。只需将记录器命名为您的顶级记录器即可。

    private static boolean configured = false;
    
    private static void buildLog()
    {
        try
        {
    
            final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
            System.out.println("Configuration found at "+ctx.getConfiguration().toString());
    
            if(ctx.getConfiguration().toString().contains(".config.DefaultConfiguration"))
            {
    
                System.out.println("\n\n\nNo log4j2 config available. Configuring programmatically\n\n");
    
                ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory
                        .newConfigurationBuilder();
    
                builder.setStatusLevel(Level.ERROR);
                builder.setConfigurationName("IkodaLogBuilder");
    
                AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE")
                        .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
                appenderBuilder.add(builder.newLayout("PatternLayout").addAttribute("pattern",
                        "%d [%t]  %msg%n%throwable"));
                builder.add(appenderBuilder);               
    
                LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout").addAttribute("pattern",
                        "%d [%t] %-5level: %msg%n");
    
                appenderBuilder = builder.newAppender("file", "File").addAttribute("fileName", "./logs/ikoda.log")
                        .add(layoutBuilder);
                builder.add(appenderBuilder);
    
    
                builder.add(builder.newLogger("ikoda", Level.DEBUG)
                        .add(builder.newAppenderRef("file"))
                        .add(builder.newAppenderRef("Stdout"))
                        .addAttribute("additivity", false));
    
                builder.add(builder.newRootLogger(Level.DEBUG)
                        .add(builder.newAppenderRef("file"))
                        .add(builder.newAppenderRef("Stdout")));
                ((org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false)).start(builder.build());
                ctx.updateLoggers();
            }
            else
            {
                System.out.println("Configuration file found.");
            }
            configured=true;
        }
        catch(Exception e)
        {
            System.out.println("\n\n\n\nFAILED TO CONFIGURE LOG4J2"+e.getMessage());
            configured=true;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-02
      • 1970-01-01
      • 2017-09-23
      • 2021-01-26
      • 1970-01-01
      • 2016-04-04
      • 1970-01-01
      相关资源
      最近更新 更多