Always fun dealing with Spring Boot

I just released version 6.1.0 of Quarkus Google Cloud JSON Logging, the somewhat inaccurately named library for logging in Google Cloud JSON format for JBoss Log Manager, a drop-in replacement for the LogManager from java.util.logging.

The original motivation for creating this release was that Quarkus in version 3.4 switched away from its custom JBoss Log Manager Embedded fork of JBoss Log Manager, rendering my library’s DefaultEmbeddedConfigurator class useless. But as it turns out, the Quarkus extension never used it, so nothing was actually broken by the change. Yay.

Instead I noticed that, somewhat unrelatedly, Spring Boot 3, which the library also supports (I mentioned it is somewhat inaccurately named, yes?), broke the way JBoss Logging (not to be confused with JBoss Log Manager) interacts with everything, especially JBoss Log Manager Embedded, leading logs from its logging facade to be swallowed and consequently never printed.

The good news is that migrating away from JBoss Log Manager Embedded fixes the problem. The real JBoss Log Manager even has another nice feature: You can configure it using a logging.properties file, so a custom configurator factory is not, in fact, needed. You can just create your logging.properties file and set it to use my DefaultConsoleHandler as the handler for your log messages. Or if you prefer, you can inject your own LogContextConfigurator using the standard ServiceLoader mechanism and leave the logging.properties file empty. Great!

Except! Spring Boot likes to make things extra fun. If you use the JavaLoggingSystem, which is what you want to do when you use JBoss Log Manager as your log sink (remember, it is a drop-in replacement for the standard LogManager from java.util.logging), Spring Boot first initializes the root logger, then sets its log level to SEVERE, and finally it asks it to load the configuration file that you supplied. But JBoss Log Manager loads its configuration (or your custom LogContextConfigurator if you supplied one) when it is initialized, so loading it another time means it loads it twice. And that puts you in a pickle because:

  1. If you leave the configuration file empty and configure the message handler some other way, the log level stays at SEVERE and you lose all logs.
  2. If you configure a logger in the configuration file and set its level and message handler, the handler is added twice, so you get double the logs.

So in the end I still had to make the change that I had originally set out to do, which was to add a new DefaultConfiguratorFactory class to replace the old DefaultEmbeddedConfigurator and enable everyone to migrate away from JBoss Log Manager Embedded. With my DefaultConfiguratorFactory, the configuration file still gets loaded twice, but the custom configurator takes care of fixing the list of handlers to a single Google-Cloud-JSON-enabled ConsoleHandler.

Check out the readme in case you want to get in on the fun.