创建SparkSession后java.util.logging失效,如何使其正常工作?
解决Quarkus中Spark与java.util.logging兼容问题
问题根源
Spark初始化SparkSession时会默认重写java.util.logging(JUL)的日志处理器配置,替换掉应用原有JUL Handler;停止SparkSession后,Spark不会自动恢复原有配置,导致后续JUL日志无法输出,仅System.out能正常打印。
解决方案1:手动保存并恢复JUL Handler
在创建SparkSession前保存根Logger的原有Handler,停止Spark后移除Spark添加的Handler并恢复原配置:
private static Handler[] originalJulHandlers; public long getParquetFileRowCount(String path) { // 保存应用原有JUL处理器 originalJulHandlers = Logger.getLogger("").getHandlers(); logger.info("SparkSession创建前的日志(正常输出)"); SparkSession spark = SparkSession.builder() .appName("ParquetRowCount") .master("local[*]") .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") .getOrCreate(); try { // 业务逻辑:读取Parquet文件并统计行数 long rowCount = spark.read().parquet(path).count(); System.out.println("统计行数:" + rowCount); logger.info("Spark运行期间的日志(恢复后可正常输出)"); return rowCount; } finally { spark.stop(); // 恢复原有JUL配置 Logger rootLogger = Logger.getLogger(""); // 移除Spark添加的所有Handler for (Handler handler : rootLogger.getHandlers()) { rootLogger.removeHandler(handler); } // 重新添加应用原有Handler for (Handler handler : originalJulHandlers) { rootLogger.addHandler(handler); } logger.info("Spark停止后的日志(已恢复正常输出)"); } }
解决方案2:通过SLF4J桥接统一日志框架
Quarkus默认使用SLF4J+JBoss Logging,将Spark的日志输出桥接到SLF4J,彻底避免JUL配置冲突:
1. 调整Maven依赖
排除Spark自带的JUL相关依赖,添加JUL转SLF4J的桥接器:
<dependencies> <!-- JUL转SLF4J桥接器 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>1.7.36</version> </dependency> <!-- Spark核心依赖,排除冲突日志组件 --> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.12</artifactId> <version>3.5.0</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> </exclusion> <exclusion> <groupId>java.util.logging</groupId> <artifactId>jul-to-slf4j</artifactId> </exclusion> </exclusions> </dependency> <!-- Delta Lake依赖 --> <dependency> <groupId>io.delta</groupId> <artifactId>delta-core_2.12</artifactId> <version>2.4.0</version> </dependency> </dependencies>
2. 初始化日志桥接
在Quarkus启动时配置JUL转SLF4J,关闭JUL默认Handler避免重复日志:
@Startup public class LogBridgeInitializer { void initLoggingBridge() { // 移除JUL默认控制台Handler Logger rootLogger = Logger.getLogger(""); for (Handler handler : rootLogger.getHandlers()) { rootLogger.removeHandler(handler); } // 启用JUL到SLF4J的桥接 SLF4JBridgeHandler.install(); } }
3. 配置Spark使用SLF4J日志工厂
创建SparkSession时指定使用SLF4J处理日志:
SparkSession spark = SparkSession.builder() .appName("ParquetRowCount") .master("local[*]") .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") // 指定Spark使用SLF4J日志工厂 .config("spark.driver.extraJavaOptions", "-Dspark.logging.factory=org.apache.spark.util.logging.Slf4jLoggingFactory") .config("spark.executor.extraJavaOptions", "-Dspark.logging.factory=org.apache.spark.util.logging.Slf4jLoggingFactory") .getOrCreate();
内容的提问来源于stack exchange,提问作者Choto
相关产品推荐
相关产品推荐

