JULConfigurator.java

package io.jstach.rainbowgum.jul;

import java.util.logging.Logger;

import io.jstach.rainbowgum.LogConfig;
import io.jstach.rainbowgum.LogProperties;
import io.jstach.rainbowgum.spi.RainbowGumServiceProvider;
import io.jstach.rainbowgum.spi.RainbowGumServiceProvider.Configurator;
import io.jstach.svc.ServiceProvider;

/**
 * Will install the JUL handler if the <code>java.logging</code> module is available and
 * is not alreadyi installed.
 */
@ServiceProvider(RainbowGumServiceProvider.class)
public final class JULConfigurator implements Configurator, AutoCloseable {

	/**
	 * If true will not install the JUL handler.
	 */
	public static final String JUL_DISABLE_PROPERTY = "logging.jul.disable";

	/**
	 * If true will not set the root loggers level to whatever the curreint rainbow gums
	 * global level default is.
	 */
	public static final String JUL_LEVEL_DISABLE_PROPERTY = "logging.jul.level.disable";

	private volatile boolean installed = false;

	/**
	 * For service laoder.
	 */
	public JULConfigurator() {
	}

	@Override
	public boolean configure(@SuppressWarnings("exports") LogConfig config, @SuppressWarnings("exports") Pass pass) {
		if (!install(config.properties())) {
			return true;
		}
		else {
			installed = true;
		}
		var disableLevel = config.properties()
			.forKey(JUL_LEVEL_DISABLE_PROPERTY)
			.ofBoolean()
			.or(false)
			.validateNow(JULConfigurator.class);

		if (!disableLevel) {
			var logger = Logger.getLogger("");
			if (logger != null) {
				var systemLevel = traceToAll(config.levelResolver().resolveLevel(""));
				logger.setLevel(julLevel(systemLevel));
			}
		}
		return true;

	}

	private static System.Logger.Level traceToAll(System.Logger.Level level) {
		if (level == System.Logger.Level.TRACE) {
			return System.Logger.Level.ALL;
		}
		return level;
	}

	/**
	 * Will install the JUL handler if not disabled by properties. This method is
	 * currently exposed for testing purposes.
	 * @param properties properties to check
	 * @return true if enabled.
	 */
	public static boolean install(@SuppressWarnings("exports") LogProperties properties) {
		if (properties.forKey(JUL_DISABLE_PROPERTY).ofBoolean().or(false).validateNow(JULConfigurator.class)) {
			return false;
		}
		if (!isLoggingModuleAvailable()) {
			return false;
		}
		if (!SystemLoggerQueueJULHandler.isInstalled()) {
			SystemLoggerQueueJULHandler.install();
		}
		return true;

	}

	private static boolean isLoggingModuleAvailable() {
		ModuleLayer bootLayer = ModuleLayer.boot();
		return bootLayer.findModule("java.logging").isPresent();
	}

	/**
	 * Will test if already installed. This is mainly for testing purpsoes.
	 * @return true if installed.
	 */
	public static boolean isInstalled() {
		return SystemLoggerQueueJULHandler.isInstalled();
	}

	@Override
	public void close() {
		if (installed) {
			var logger = Logger.getLogger("");
			if (logger != null) {
				logger.setLevel(java.util.logging.Level.INFO);
			}
		}

	}

	private java.util.logging.Level julLevel(System.Logger.Level level) {
		var julLevel = switch (level) {
			case TRACE -> java.util.logging.Level.FINEST;
			case DEBUG -> java.util.logging.Level.FINE;
			case INFO -> java.util.logging.Level.INFO;
			case WARNING -> java.util.logging.Level.WARNING;
			case ERROR -> java.util.logging.Level.SEVERE;
			case ALL -> java.util.logging.Level.ALL;
			case OFF -> java.util.logging.Level.OFF;
		};
		return julLevel;
	}

}