/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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;
This class is a factory for Lazy Loggers; only system loggers can be
Lazy Loggers.
/**
* This class is a factory for Lazy Loggers; only system loggers can be
* Lazy Loggers.
*/
public final class LazyLoggers {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
private LazyLoggers() {
throw new InternalError();
}
This class is used to hold the factories that a Lazy Logger will use
to create (or map) its wrapped logger.
Type parameters:
/**
* This class is used to hold the factories that a Lazy Logger will use
* to create (or map) its wrapped logger.
* @param <L> {@link Logger} or a subclass of {@link Logger}.
*/
private static final class LazyLoggerFactories<L extends Logger> {
A factory method to create an SPI logger.
Usually, this will be something like LazyLoggers::getSystemLogger.
/**
* A factory method to create an SPI logger.
* Usually, this will be something like LazyLoggers::getSystemLogger.
*/
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 {
The logger name.
Returns: The name of the logger that is / will be lazily created.
/**
* The logger name.
* @return The name of the logger that is / will be lazily created.
*/
public String getLoggerName();
Returns the wrapped logger object.
Returns: the wrapped logger object.
/**
* Returns the wrapped logger object.
* @return the wrapped logger object.
*/
public Logger wrapped();
A PlatformLogger.Bridge view of the wrapped logger object.
Returns: A PlatformLogger.Bridge view of the wrapped logger object.
/**
* A PlatformLogger.Bridge view of the wrapped logger object.
* @return A PlatformLogger.Bridge view of the wrapped logger object.
*/
public PlatformLogger.Bridge platform();
}
The LazyLoggerAccessor class holds all the logic that delays the creation
of the SPI logger until such a time that the VM is booted and the logger
is actually used for logging.
This class uses the services of the BootstrapLogger class to instantiate
temporary loggers if appropriate.
/**
* The LazyLoggerAccessor class holds all the logic that delays the creation
* of the SPI logger until such a time that the VM is booted and the logger
* is actually used for logging.
*
* This class uses the services of the BootstrapLogger class to instantiate
* temporary loggers if appropriate.
*/
static final class LazyLoggerAccessor implements LoggerAccessor {
// The factories that will be used to create the logger lazyly
final LazyLoggerFactories<? extends Logger> factories;
// We need to pass the actual caller module when creating the logger.
private final WeakReference<Module> moduleRef;
// The name of the logger that will be created lazyly
final String name;
// The plain logger SPI object - null until it is accessed for the
// first time.
private volatile Logger w;
// A PlatformLogger.Bridge view of 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);
}
The logger name.
Returns: The name of the logger that is / will be lazily created.
/**
* The logger name.
* @return The name of the logger that is / will be lazily created.
*/
@Override
public String getLoggerName() {
return name;
}
// must be called in synchronized block
// set wrapped logger if not set
private void setWrappedIfNotSet(Logger wrapped) {
if (w == null) {
w = wrapped;
}
}
Returns the logger SPI object, creating it if 'w' is still null.
Returns: the logger SPI object.
/**
* Returns the logger SPI object, creating it if 'w' is still null.
* @return the logger SPI object.
*/
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)
// logger.
wrapped = BootstrapLogger.getLogger(this);
synchronized(this) {
// if w has already been in between, simply drop 'wrapped'.
setWrappedIfNotSet(wrapped);
return w;
}
}
A PlatformLogger.Bridge view of the wrapped logger.
Returns: A PlatformLogger.Bridge view of the wrapped logger.
/**
* A PlatformLogger.Bridge view of the wrapped logger.
* @return A PlatformLogger.Bridge view of the wrapped logger.
*/
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
// just before the application initialized LogManager.
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)
// logger.
final Logger wrapped = BootstrapLogger.getLogger(this);
synchronized(this) {
// if w has already been set, simply drop 'wrapped'.
setWrappedIfNotSet(wrapped);
if (p == null) p = PlatformLogger.Bridge.convert(w);
return p;
}
}
Makes this accessor release a temporary logger. This method is called by BootstrapLogger when JUL is the default backend and LogManager is initialized, in order to replace temporary SimpleConsoleLoggers by real JUL loggers. See BootstrapLogger for more details. If replace
is true
, then this method will force the accessor to eagerly recreate its wrapped logger. Note: passing replace=false
is no guarantee that the method will not actually replace the released logger. Params: - temporary – The temporary logger too be released.
- replace – Whether the released logger should be eagerly
replaced.
/**
* Makes this accessor release a temporary logger.
* This method is called
* by BootstrapLogger when JUL is the default backend and LogManager
* is initialized, in order to replace temporary SimpleConsoleLoggers by
* real JUL loggers. See BootstrapLogger for more details.
* If {@code replace} is {@code true}, then this method will force
* the accessor to eagerly recreate its wrapped logger.
* Note: passing {@code replace=false} is no guarantee that the
* method will not actually replace the released logger.
* @param temporary The temporary logger too be released.
* @param replace Whether the released logger should be eagerly
* replaced.
*/
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);
}
}
Replace 'w' by the real SPI logger and flush the log messages pending
in the temporary 'bootstrap' Logger. Called by BootstrapLogger when
this accessor's bootstrap logger is accessed and BootstrapLogger
notices that the VM is no longer booting.
Params: - bootstrap – This accessor's bootstrap logger (usually this is 'w').
/**
* Replace 'w' by the real SPI logger and flush the log messages pending
* in the temporary 'bootstrap' Logger. Called by BootstrapLogger when
* this accessor's bootstrap logger is accessed and BootstrapLogger
* notices that the VM is no longer booting.
* @param bootstrap This accessor's bootstrap logger (usually this is 'w').
*/
Logger getConcreteLogger(BootstrapLogger bootstrap) {
assert VM.isBooted();
synchronized(this) {
// another thread may have already invoked flush()
if (this.w == bootstrap) {
this.w = null; this.p = null;
}
}
return this.wrapped();
}
PlatformLogger.Bridge getConcretePlatformLogger(BootstrapLogger bootstrap) {
assert VM.isBooted();
synchronized(this) {
// another thread may have already invoked flush()
if (this.w == bootstrap) {
this.w = null; this.p = null;
}
}
return this.platform();
}
// Creates the wrapped logger by invoking the SPI.
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);
}
Creates a new lazy logger accessor for the named logger. The given
factories will be use when it becomes necessary to actually create
the logger.
Params: - name – The logger name.
- factories – The factories that should be used to create the
wrapped logger.
Type parameters: - <T> – An interface that extends
Logger
.
Returns: A new LazyLoggerAccessor.
/**
* Creates a new lazy logger accessor for the named logger. The given
* factories will be use when it becomes necessary to actually create
* the logger.
* @param <T> An interface that extends {@link Logger}.
* @param name The logger name.
* @param factories The factories that should be used to create the
* wrapped logger.
* @return A new LazyLoggerAccessor.
*/
public static LazyLoggerAccessor makeAccessor(String name,
LazyLoggerFactories<? extends Logger> factories, Module module) {
return new LazyLoggerAccessor(name, factories, module);
}
}
An implementation of Logger
that redirects all calls to a wrapped instance of Logger
. /**
* An implementation of {@link Logger} that redirects all calls to a wrapped
* instance of {@code Logger}.
*/
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();
}
}
// Do not expose this outside of this package.
private static volatile LoggerFinder provider;
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
// doPrivileged every time.
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
// in the bootstrap sequence...
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);
}
}
Gets a logger from the LoggerFinder. Creates the actual concrete
logger.
Params: - name – name of the logger
- module – module on behalf of which the logger is created
Returns: The logger returned by the LoggerFinder.
/**
* Gets a logger from the LoggerFinder. Creates the actual concrete
* logger.
* @param name name of the logger
* @param module module on behalf of which the logger is created
* @return The logger returned by the LoggerFinder.
*/
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);
}
}
Returns a (possibly lazy) Logger for the caller.
Params: - name – the logger name
- module – The module on behalf of which the logger is created. If the module is not loaded from the Boot ClassLoader, the LoggerFinder is accessed and the logger returned by
LoggerFinder.getLogger(String, Module)
is returned to the caller directly. Otherwise, the logger returned by getLazyLogger(String, Module)
is returned to the caller.
Returns: a (possibly lazy) Logger instance.
/**
* Returns a (possibly lazy) Logger for the caller.
*
* @param name the logger name
* @param module The module on behalf of which the logger is created.
* If the module is not loaded from the Boot ClassLoader,
* the LoggerFinder is accessed and the logger returned
* by {@link LoggerFinder#getLogger(java.lang.String, java.lang.Module)}
* is returned to the caller directly.
* Otherwise, the logger returned by
* {@link #getLazyLogger(java.lang.String, java.lang.Module)}
* is returned to the caller.
*
* @return a (possibly lazy) Logger instance.
*/
public static final Logger getLogger(String name, Module module) {
if (DefaultLoggerFinder.isSystem(module)) {
return getLazyLogger(name, module);
} else {
return getLoggerFromFinder(name, module);
}
}
Returns a (possibly lazy) Logger suitable for system classes. Whether the returned logger is lazy or not depend on the result returned by BootstrapLogger.useLazyLoggers()
. Params: - name – the logger name
- module – the module on behalf of which the logger is created.
Returns: a (possibly lazy) Logger instance.
/**
* Returns a (possibly lazy) Logger suitable for system classes.
* Whether the returned logger is lazy or not depend on the result
* returned by {@link BootstrapLogger#useLazyLoggers()}.
*
* @param name the logger name
* @param module the module on behalf of which the logger is created.
* @return a (possibly lazy) Logger instance.
*/
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
// configuration, we're not going to delay the creation of loggers...
final boolean useLazyLogger = BootstrapLogger.useLazyLoggers();
if (useLazyLogger) {
return new JdkLazyLogger(name, module);
} else {
// Directly invoke the LoggerFinder.
return getLoggerFromFinder(name, module);
}
}
}