无法在 Spring 引导 Web 应用程序中覆盖 java.util.logging.LogManager:在已加载的 class 上获取 java.lang.ClassNotFoundException

Can't override java.util.logging.LogManager in a Spring Boot web application: Getting java.lang.ClassNotFoundException on already loaded class

我正在尝试用我自己的配置覆盖 java.util.logging.LogManager

class CloudwatchHandlerHandler 的一个实现,包括这个 init() 方法:

public static void init() {
    final String julConfigFile = System.getProperty("java.util.logging.config.file");
    if(julConfigFile != null) {
        try (InputStream is = new FileInputStream(julConfigFile)) {
            LogManager logManager = LogManager.getLogManager();
            logManager.reset();
            logManager.readConfiguration(is);
            Logger logger = Logger.getLogger(CloudwatchHandler.class.getName());
            logger.info("LOADED");
        } catch (SecurityException | IOException e) {
            System.err.println(Instant.now() + ": Failed to initialize JUL.");
            e.printStackTrace(System.err);
            throw new RuntimeException(e);
        }
    }
    else {
        System.err.println(Instant.now() + ": java.util.logging.config.file was not specified");
    }

}

主应用程序class

public static void main(String[] args) {
    CloudwatchHandler.init();
    SpringApplication.run(MyApp.class, args);
}

错误

Can't load log handler "mypackage.CloudwatchHandler"
java.lang.ClassNotFoundException: mypackage.CloudwatchHandler
java.lang.ClassNotFoundException: mypackage.CloudwatchHandler
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
        at java.logging/java.util.logging.LogManager.createLoggerHandlers(LogManager.java:1005)
        at java.logging/java.util.logging.LogManager.run(LogManager.java:975)
        at java.logging/java.util.logging.LogManager.run(LogManager.java:971)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
        at java.logging/java.util.logging.LogManager.loadLoggerHandlers(LogManager.java:971)
        at java.logging/java.util.logging.LogManager.initializeGlobalHandlers(LogManager.java:2424)
        at java.logging/java.util.logging.LogManager$RootLogger.accessCheckedHandlers(LogManager.java:2526)
        at java.logging/java.util.logging.Logger.getHandlers(Logger.java:2090)
        at java.logging/java.util.logging.Logger.log(Logger.java:977)
        at java.logging/java.util.logging.Logger.doLog(Logger.java:1007)
        at java.logging/java.util.logging.Logger.log(Logger.java:1030)
        at java.logging/java.util.logging.Logger.info(Logger.java:1803)
        at mypackage.CloudwatchHandler.init(CloudwatchHandler.java:51)
        ... main ...

关于此异常的真正疯狂之处在于导致 ClassNotFoundException 的 class 实际上是当前堆栈帧中的调用者,如堆栈跟踪中所示。很明显它已被找到或不可能 运行.

这是什么原因造成的,我该如何解决?我只想加载我自己的日志处理程序。

Spring开机版本为2.6.3.

如果未部署处理程序以在 system class loader 中加载,则会发生 ClassNotFoundException,因为这是 LogManager 用于查找处理程序的内容。

更新您的测试用例并重试:

public static void main(String[] args) throws Exception {
    System.out.println(ClassLoader.getSystemClassLoader());
    System.out.println(Thread.currentThread().getContextClassLoader());
    System.out.println(CloudwatchHandler.class.getClassLoader());

    //This is what CloudwatchHandler.init(); triggers
    Class.forName(CloudwatchHandler.class.getName(), true, Thread.currentThread().getContextClassLoader());

   //This is what the LogManager is doing
   Class.forName(CloudwatchHandler.class.getName(), true, ClassLoader.getSystemClassLoader());


   //Force load the root handlers.
   Logger.getLogger("").getHandlers();

   CloudwatchHandler.init();
   SpringApplication.run(MyApp.class, args);
}

如果处理程序部署在上下文 class 加载程序中而不是系统 classloader 中,那么您需要更改打包处理程序的方式,以便它对系统可见class装载机。 java.util.logging.config.class 选项是 LogManager 的一部分,它将尝试通过上下文 classloader 加载 classes,这将能够看到您的 classes。对于此选项,您将 init 方法的内容移动到一个新的 class 并让构造函数执行该操作。然后在命令行上将值设置为新配置的 FQCN class.