| /* |
| * 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. |
| */ |
| 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. |
| * @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. |
| */ |
| 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. |
| * @return The name of the logger that is / will be lazily created. |
| */ |
| public String getLoggerName(); |
| |
| /** |
| * Returns the wrapped logger object. |
| * @return the wrapped logger object. |
| */ |
| public Logger wrapped(); |
| |
| /** |
| * 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. |
| */ |
| 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. |
| * @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. |
| * @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. |
| * @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 {@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. |
| * @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. |
| * @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 {@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. |
| * @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. |
| * |
| * @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 {@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); |
| } |
| } |
| |
| } |