You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:17:20