I am trying to override java.util.logging.LogManager
with my own configuration:
class CloudwatchHandler is an implementation of Handler
and includes this init()
method:
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");
}
}
Application main class
public static void main(String[] args) {
CloudwatchHandler.init();
SpringApplication.run(MyApp.class, args);
}
Error
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$4.run(LogManager.java:975)
at java.logging/java.util.logging.LogManager$4.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 ...
The really crazy thing about this exception is that the class causing the ClassNotFoundException
is actually a caller in the current stack frame, as seen in the stack trace. So clearly it has been FOUND or it couldn't be running.
What's causing this and how can I fix it? I just want to load my own log handler.
Spring Boot version is 2.6.3.
CodePudding user response:
ClassNotFoundException can occur if the Handler is not deployed to load in the system class loader as that is what the LogManager uses to find handlers.
Update your test case and try again:
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);
}
If it is that the handler is deployed in the context class loader and not in the system classloader then you need to change how you package the handler so it is visible to the system classloader. You might be able to use the java.util.logging.config.class
option to gain access to the context classloader. For this option you move the contents of your init method to a new class and have the constructor perform the action. On the command line then set the value to the FQCN of the new config class.