SystemLoggingFactory.java

package io.jstach.rainbowgum.jdk.systemlogger;

import io.jstach.rainbowgum.LogProperties;
import io.jstach.rainbowgum.systemlogger.RainbowGumSystemLoggerFinder;
import io.jstach.svc.ServiceProvider;

/**
 * System Logger rainbow gum implementation. Unlike the SLF4J implementation
 * <strong>Rainbow Gum does not cache System Loggers</strong> by name!
 * <p>
 * This no longer eagerly installs the <code>java.util.logging</code> handler itself -
 * that happens via the normal
 * {@link io.jstach.rainbowgum.spi.RainbowGumServiceProvider.Configurator} pass (see
 * {@code io.jstach.rainbowgum.jul.JULConfigurator}, found via
 * {@link java.util.ServiceLoader} if the <code>rainbowgum-jul</code> artifact is present)
 * once a real Rainbow Gum actually loads, whether triggered eagerly by this very class
 * (see {@link RainbowGumSystemLoggerFinder.InitOption#CHECK}) or later by SLF4J. Any
 * <code>java.util.logging</code> calls made before that point are no worse off than the
 * System.Logger events this class already queues until a real Rainbow Gum loads.
 * <p>
 * To keep just the handler installation disabled while still depending on it, see
 * {@code io.jstach.rainbowgum.jul.JULConfigurator#JUL_DISABLE_PROPERTY}.
 * <p>
 * <b>GraalVM native image</b>: this module bundles a {@code native-image.properties} (at
 * {@code META-INF/native-image/io.jstach.rainbowgum/rainbowgum-jdk/}) with
 * {@code --initialize-at-build-time} for this class plus the handful of core classes
 * ({@code RainbowGumHolder}, {@link io.jstach.rainbowgum.LogProperties} and its
 * {@code StandardProperties} helper, {@code PropertySuccess}, {@code ServiceLoaderCache})
 * its constructor's lazily-deferred property/{@code RainbowGum.getOrNull()} lookup
 * touches on first real use. This is still needed even though nothing here does eager
 * work in its own constructor: the JDK's own internals call {@code System.getLogger(...)}
 * incidentally, for their own diagnostics, from unrelated code paths that end up
 * reachable during a real native-image build (one observed trigger:
 * {@code com.oracle.svm.core.jdk.TrustStoreManagerFeature} loading the default trust
 * store at build time, unrelated to anything this module does), and whichever registered
 * {@code System.LoggerFinder} is on the classpath, namely this class, gets swept up
 * regardless. What actually gets resolved and frozen in is just the cheap "nothing bound
 * yet" state (no real {@code RainbowGum} has loaded, so {@code RainbowGum.getOrNull()}
 * returns {@code null} and property lookups fall back to real system properties), the
 * same state any freshly started JVM begins in, not anything environment-specific baked
 * in from the build. native-image's own embedded configuration discovery picks this up
 * from this module's jar automatically, no extra plugin or flag needed on the consuming
 * side; see {@code rainbowgum-systemlogger}'s own bundled {@code native-image.properties}
 * for the companion flags this one usually needs alongside it.
 *
 * @see #INITIALIZE_RAINBOW_GUM_PROPERTY
 */
@ServiceProvider(System.LoggerFinder.class)
public final class SystemLoggingFactory extends RainbowGumSystemLoggerFinder {

	/**
	 * Initialization flag.
	 * @see RainbowGumSystemLoggerFinder.InitOption
	 */
	public static final String INITIALIZE_RAINBOW_GUM_PROPERTY = RainbowGumSystemLoggerFinder.INITIALIZE_RAINBOW_GUM_PROPERTY;

	/**
	 * No-Arg for Service Loader.
	 */
	public SystemLoggingFactory() {
		/*
		 * Deferring LogProperties.findGlobalProperties() itself into the lazy supplier,
		 * not just initOption(...), matters: that method checks RainbowGum.getOrNull()
		 * and falls back to real system properties, both of which must be resolved
		 * against whatever is true when a logger is actually first requested, not
		 * whatever happened to be true the moment this ServiceLoader-constructed instance
		 * came into being (see RainbowGumSystemLoggerFinder's own javadoc on why its
		 * constructor no longer does any of this eagerly either).
		 */
		super(() -> initOption(LogProperties.findGlobalProperties()));
	}

	/**
	 * For testing that needs to supply properties directly rather than through the no-arg
	 * constructor's {@link LogProperties#findGlobalProperties()}. Package-private rather
	 * than {@code protected}: this class is {@code final}, so {@code protected} would not
	 * actually grant any access beyond package-private (no subclassing is possible either
	 * way).
	 * @param properties properties to resolve the init option from.
	 */
	SystemLoggingFactory(LogProperties properties) {
		super(() -> initOption(properties));
	}

}