Spark Submit提交Jar至YARN时Log4j2无法生成本地/HDFS日志
我来帮你搞定这个问题——在YARN上跑Spark应用时,自定义Log4j2日志总是不生成,大概率是路径适配、配置加载或者依赖冲突的问题,我给你一步步拆解解决:
1. 先把Log4j2的路径配置适配YARN容器
你直接写本地绝对路径肯定不行,YARN的容器和你提交任务的机器是完全隔离的,容器里根本没有你本地的目录。这里有两种靠谱的配置方式:
方式一:写到YARN容器的日志目录(方便通过YARN聚合查看)
在你的log4j2.xml里用YARN内置的系统变量指定路径,这样日志会直接写到每个容器的专属日志目录:
<Appenders> <File name="AppFileAppender" fileName="${sys:spark.yarn.app.container.log.dir}/my-custom-app.log"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/> </File> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="AppFileAppender"/> </Root> </Loggers>
后续你可以用yarn logs -applicationId <你的应用ID>查看所有容器的日志,包括这个自定义文件。
方式二:写到HDFS(统一存储日志)
如果想把日志统一存在HDFS,用RollingFileAppender配合HDFS路径(注意要确保Spark有HDFS依赖):
<Appenders> <RollingFile name="HdfsRollingAppender" fileName="hdfs:///user/your-username/app-logs/my-app.log" filePattern="hdfs:///user/your-username/app-logs/my-app-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/> <Policies> <SizeBasedTriggeringPolicy size="100MB"/> </Policies> <DefaultRolloverStrategy max="10"/> </RollingFile> </Appenders>
提前用HDFS命令创建好目录并赋权:
hdfs dfs -mkdir -p /user/your-username/app-logs hdfs dfs -chmod 775 /user/your-username/app-logs
2. 修正Spark Submit命令的参数
你用--files上传配置文件的方式不对,还要明确告诉Spark加载这个自定义配置,覆盖默认的日志设置。正确的提交命令应该是这样:
spark-submit \ --class com.your.package.YourMainClass \ --master yarn \ --deploy-mode cluster \ --files ./log4j2.xml#log4j2.xml \ # 给上传的文件加别名,方便容器内引用 --driver-java-options "-Dlog4j.configurationFile=log4j2.xml" \ # 驱动端用自定义配置 --conf spark.executor.extraJavaOptions="-Dlog4j.configurationFile=log4j2.xml" \ # executor端用自定义配置 your-built-app.jar
这里的#log4j2.xml是关键——它会把你本地的log4j2.xml上传到容器后,仍然用这个名字保存,这样-Dlog4j.configurationFile才能找到它。
3. 排除Spark自带的Log4j依赖,避免版本冲突
Spark默认自带Log4j相关依赖,很可能和你自己引入的Log4j2版本冲突,导致配置不生效。在你的pom.xml里排除Spark的Log4j依赖:
<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.12</artifactId> <version>3.3.0</version> <!-- 换成你的Spark版本 --> <scope>provided</scope> <exclusions> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </exclusion> <exclusion> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> </exclusion> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency>
同时确保你自己的Log4j2依赖是完整的:
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.17.2</version> <!-- 选一个稳定的版本 --> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.17.2</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.17.2</version> <!-- 适配SLF4J桥接 --> </dependency>
4. 验证配置是否被正确加载
在你的主类里加一行代码,打印当前Log4j2的配置文件路径,确认是否加载了正确的文件:
import org.apache.logging.log4j.core.LoggerContext; public class YourMainClass { public static void main(String[] args) { // 打印Log4j2配置路径 LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false); System.out.println("Loaded Log4j2 config file: " + context.getConfiguration().getConfigurationSource().getLocation()); // 你的Spark应用代码... SparkSession spark = SparkSession.builder() .appName("YourAppName") .getOrCreate(); // ... } }
提交应用后,用yarn logs -applicationId <你的应用ID>查看日志,如果看到打印的路径是你上传的log4j2.xml,说明配置加载成功了。
5. 检查权限问题
如果还是没生成日志,检查一下:
- 写本地路径时:YARN容器对该目录有没有读写权限(尽量用前面说的
spark.yarn.app.container.log.dir,这个目录是容器自带的,权限没问题) - 写HDFS路径时:运行Spark的用户对HDFS目录有没有读写权限(用
hdfs dfs -ls /user/your-username/app-logs确认)
按照这些步骤来,应该就能解决你的问题了!
内容的提问来源于stack exchange,提问作者Mohit

