|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.internal.logger; |
|
|
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.util.function.BiFunction; |
|
import java.lang.System.LoggerFinder; |
|
import java.lang.System.Logger; |
|
import java.lang.ref.WeakReference; |
|
import java.util.Objects; |
|
import jdk.internal.misc.VM; |
|
import sun.util.logging.PlatformLogger; |
|
|
|
|
|
|
|
|
|
*/ |
|
public final class LazyLoggers { |
|
|
|
static final RuntimePermission LOGGERFINDER_PERMISSION = |
|
new RuntimePermission("loggerFinder"); |
|
|
|
private LazyLoggers() { |
|
throw new InternalError(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final class LazyLoggerFactories<L extends Logger> { |
|
|
|
|
|
|
|
|
|
*/ |
|
final BiFunction<String, Module, L> loggerSupplier; |
|
|
|
|
|
public LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier) { |
|
this(Objects.requireNonNull(loggerSupplier), |
|
(Void)null); |
|
} |
|
|
|
private LazyLoggerFactories(BiFunction<String, Module, L> loggerSupplier, |
|
Void unused) { |
|
this.loggerSupplier = loggerSupplier; |
|
} |
|
|
|
} |
|
|
|
static interface LoggerAccessor { |
|
|
|
|
|
|
|
*/ |
|
public String getLoggerName(); |
|
|
|
|
|
|
|
|
|
*/ |
|
public Logger wrapped(); |
|
|
|
|
|
|
|
|
|
*/ |
|
public PlatformLogger.Bridge platform(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final class LazyLoggerAccessor implements LoggerAccessor { |
|
|
|
|
|
final LazyLoggerFactories<? extends Logger> factories; |
|
|
|
|
|
private final WeakReference<Module> moduleRef; |
|
|
|
|
|
final String name; |
|
// The plain logger SPI object - null until it is accessed for the |
|
|
|
private volatile Logger w; |
|
|
|
private volatile PlatformLogger.Bridge p; |
|
|
|
|
|
private LazyLoggerAccessor(String name, |
|
LazyLoggerFactories<? extends Logger> factories, |
|
Module module) { |
|
this(Objects.requireNonNull(name), Objects.requireNonNull(factories), |
|
Objects.requireNonNull(module), null); |
|
} |
|
|
|
private LazyLoggerAccessor(String name, |
|
LazyLoggerFactories<? extends Logger> factories, |
|
Module module, Void unused) { |
|
this.name = name; |
|
this.factories = factories; |
|
this.moduleRef = new WeakReference<>(module); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public String getLoggerName() { |
|
return name; |
|
} |
|
|
|
// must be called in synchronized block |
|
|
|
private void setWrappedIfNotSet(Logger wrapped) { |
|
if (w == null) { |
|
w = wrapped; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Logger wrapped() { |
|
Logger wrapped = w; |
|
if (wrapped != null) return wrapped; |
|
// Wrapped logger not created yet: create it. |
|
// BootstrapLogger has the logic to decide whether to invoke the |
|
// SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) |
|
|
|
wrapped = BootstrapLogger.getLogger(this); |
|
synchronized(this) { |
|
|
|
setWrappedIfNotSet(wrapped); |
|
return w; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public PlatformLogger.Bridge platform() { |
|
// We can afford to return the platform view of the previous |
|
// logger - if that view is not null. |
|
// Because that view will either be the BootstrapLogger, which |
|
// will redirect to the new wrapper properly, or the temporary |
|
// logger - which in effect is equivalent to logging something |
|
|
|
PlatformLogger.Bridge platform = p; |
|
if (platform != null) return platform; |
|
synchronized (this) { |
|
if (w != null) { |
|
if (p == null) p = PlatformLogger.Bridge.convert(w); |
|
return p; |
|
} |
|
} |
|
// If we reach here it means that the wrapped logger may not |
|
// have been created yet: attempt to create it. |
|
// BootstrapLogger has the logic to decide whether to invoke the |
|
// SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger) |
|
|
|
final Logger wrapped = BootstrapLogger.getLogger(this); |
|
synchronized(this) { |
|
|
|
setWrappedIfNotSet(wrapped); |
|
if (p == null) p = PlatformLogger.Bridge.convert(w); |
|
return p; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void release(SimpleConsoleLogger temporary, boolean replace) { |
|
PlatformLogger.ConfigurableBridge.LoggerConfiguration conf = |
|
PlatformLogger.ConfigurableBridge.getLoggerConfiguration(temporary); |
|
PlatformLogger.Level level = conf != null |
|
? conf.getPlatformLevel() |
|
: null; |
|
synchronized (this) { |
|
if (this.w == temporary) { |
|
this.w = null; this.p = null; |
|
} |
|
} |
|
PlatformLogger.Bridge platform = replace || level != null |
|
? this.platform() : null; |
|
|
|
if (level != null) { |
|
conf = (platform != null && platform != temporary) |
|
? PlatformLogger.ConfigurableBridge.getLoggerConfiguration(platform) |
|
: null; |
|
if (conf != null) conf.setPlatformLevel(level); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Logger getConcreteLogger(BootstrapLogger bootstrap) { |
|
assert VM.isBooted(); |
|
synchronized(this) { |
|
|
|
if (this.w == bootstrap) { |
|
this.w = null; this.p = null; |
|
} |
|
} |
|
return this.wrapped(); |
|
} |
|
|
|
PlatformLogger.Bridge getConcretePlatformLogger(BootstrapLogger bootstrap) { |
|
assert VM.isBooted(); |
|
synchronized(this) { |
|
|
|
if (this.w == bootstrap) { |
|
this.w = null; this.p = null; |
|
} |
|
} |
|
return this.platform(); |
|
} |
|
|
|
|
|
Logger createLogger() { |
|
final Module module = moduleRef.get(); |
|
if (module == null) { |
|
throw new IllegalStateException("The module for which this logger" |
|
+ " was created has been garbage collected"); |
|
} |
|
return this.factories.loggerSupplier.apply(name, module); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static LazyLoggerAccessor makeAccessor(String name, |
|
LazyLoggerFactories<? extends Logger> factories, Module module) { |
|
return new LazyLoggerAccessor(name, factories, module); |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static class LazyLoggerWrapper |
|
extends AbstractLoggerWrapper<Logger> { |
|
|
|
final LoggerAccessor loggerAccessor; |
|
|
|
public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) { |
|
this(Objects.requireNonNull(loggerSinkSupplier), (Void)null); |
|
} |
|
|
|
private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier, |
|
Void unused) { |
|
this.loggerAccessor = loggerSinkSupplier; |
|
} |
|
|
|
@Override |
|
final Logger wrapped() { |
|
return loggerAccessor.wrapped(); |
|
} |
|
|
|
@Override |
|
PlatformLogger.Bridge platformProxy() { |
|
return loggerAccessor.platform(); |
|
} |
|
|
|
} |
|
|
|
|
|
private static volatile LoggerFinder provider; |
|
@SuppressWarnings("removal") |
|
private static LoggerFinder accessLoggerFinder() { |
|
LoggerFinder prov = provider; |
|
if (prov == null) { |
|
// no need to lock: it doesn't matter if we call |
|
// getLoggerFinder() twice - since LoggerFinder already caches |
|
// the result. |
|
// This is just an optimization to avoid the cost of calling |
|
|
|
final SecurityManager sm = System.getSecurityManager(); |
|
prov = sm == null ? LoggerFinder.getLoggerFinder() : |
|
AccessController.doPrivileged( |
|
(PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder); |
|
provider = prov; |
|
} |
|
return prov; |
|
} |
|
|
|
// Avoid using lambda here as lazy loggers could be created early |
|
|
|
private static final BiFunction<String, Module, Logger> loggerSupplier = |
|
new BiFunction<>() { |
|
@Override |
|
public Logger apply(String name, Module module) { |
|
return LazyLoggers.getLoggerFromFinder(name, module); |
|
} |
|
}; |
|
|
|
private static final LazyLoggerFactories<Logger> factories = |
|
new LazyLoggerFactories<>(loggerSupplier); |
|
|
|
|
|
|
|
// A concrete implementation of Logger that delegates to a System.Logger, |
|
// but only creates the System.Logger instance lazily when it's used for |
|
// the first time. |
|
// The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies |
|
// on the logic embedded in BootstrapLogger to avoid loading the concrete |
|
// logger provider until the VM has finished booting. |
|
|
|
private static final class JdkLazyLogger extends LazyLoggerWrapper { |
|
JdkLazyLogger(String name, Module module) { |
|
this(LazyLoggerAccessor.makeAccessor(name, factories, module), |
|
(Void)null); |
|
} |
|
private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) { |
|
super(holder); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("removal") |
|
static Logger getLoggerFromFinder(String name, Module module) { |
|
final SecurityManager sm = System.getSecurityManager(); |
|
if (sm == null) { |
|
return accessLoggerFinder().getLogger(name, module); |
|
} else { |
|
return AccessController.doPrivileged((PrivilegedAction<Logger>) |
|
() -> {return accessLoggerFinder().getLogger(name, module);}, |
|
null, LOGGERFINDER_PERMISSION); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final Logger getLogger(String name, Module module) { |
|
if (DefaultLoggerFinder.isSystem(module)) { |
|
return getLazyLogger(name, module); |
|
} else { |
|
return getLoggerFromFinder(name, module); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final Logger getLazyLogger(String name, Module module) { |
|
|
|
// BootstrapLogger has the logic to determine whether a LazyLogger |
|
// should be used. Usually, it is worth it only if: |
|
// - the VM is not yet booted |
|
// - or, the backend is JUL and there is no configuration |
|
// - or, the backend is a custom backend, as we don't know what |
|
// that is going to load... |
|
// So if for instance the VM is booted and we use JUL with a custom |
|
|
|
final boolean useLazyLogger = BootstrapLogger.useLazyLoggers(); |
|
if (useLazyLogger) { |
|
return new JdkLazyLogger(name, module); |
|
} else { |
|
|
|
return getLoggerFromFinder(name, module); |
|
} |
|
} |
|
|
|
} |