在Spark java应用程序中使用log4j2

时间:2017-02-27 22:07:17

标签: apache-spark log4j log4j2

我正在尝试在我的Spark作业中使用log4j 2 记录器。基本要求: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>

我想念的任何想法?

4 个答案:

答案 0 :(得分:4)

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

在实践方面,这意味着:

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

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

答案 1 :(得分: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参数。根据{{​​3}},所有这些库将被添加到驱动程序和执行程序的类路径中,因此它应该以相同的方式工作。

答案 2 :(得分:0)

尝试使用--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  

答案 3 :(得分:0)

如果在您自己的一个依赖项中使用了log4j2,那么很容易将所有配置文件配对并使用一个或两个高级记录器IF的编程配置,并且仅在找不到配置文件时。< / p>

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

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;
    }
}