< prev index next >

jdk/src/java.base/share/classes/java/lang/System.java

Print this page

        

*** 28,52 **** import java.lang.reflect.Executable; import java.lang.annotation.Annotation; import java.security.AccessControlContext; import java.util.Properties; import java.util.PropertyPermission; - import java.util.StringTokenizer; import java.util.Map; import java.security.AccessController; import java.security.PrivilegedAction; - import java.security.AllPermission; import java.nio.channels.Channel; import java.nio.channels.spi.SelectorProvider; import sun.nio.ch.Interruptible; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import sun.security.util.SecurityConstants; import sun.reflect.annotation.AnnotationType; import jdk.internal.HotSpotIntrinsicCandidate; import jdk.internal.misc.JavaLangAccess;; import jdk.internal.misc.SharedSecrets;; /** * The <code>System</code> class contains several useful class fields * and methods. It cannot be instantiated. * --- 28,61 ---- import java.lang.reflect.Executable; import java.lang.annotation.Annotation; import java.security.AccessControlContext; import java.util.Properties; import java.util.PropertyPermission; import java.util.Map; import java.security.AccessController; import java.security.PrivilegedAction; import java.nio.channels.Channel; import java.nio.channels.spi.SelectorProvider; + import java.util.Objects; + import java.util.ResourceBundle; + import java.security.Permission; + import java.util.Arrays; + import java.util.ServiceConfigurationError; + import java.util.ServiceLoader; + import java.util.function.Supplier; + import sun.util.logger.JdkLoggerProvider; import sun.nio.ch.Interruptible; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; import sun.security.util.SecurityConstants; import sun.reflect.annotation.AnnotationType; import jdk.internal.HotSpotIntrinsicCandidate; import jdk.internal.misc.JavaLangAccess;; import jdk.internal.misc.SharedSecrets;; + import sun.util.logger.BootstrapLogger; + import sun.util.logger.LazyLoggers; + import sun.util.logger.LocalizedLoggerWrapper; /** * The <code>System</code> class contains several useful class fields * and methods. It cannot be instantiated. *
*** 942,951 **** --- 951,1776 ---- return ProcessEnvironment.getenv(); } /** + * The minimum set of methods that a logger returned by the + * {@link LoggerFinder} service should implement. + * Instances of loggers implementing this interface are obtained from + * the {@link java.lang.System System} class, by calling + * {@link java.lang.System#getLogger(java.lang.String) System.getLogger(loggerName)} + * or {@link java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle) + * System.getLogger(loggerName, bundle)}. + * <p> + * Unless + * + * @see java.lang.System + * @see java.lang.System.LoggerFinder + * + * @since 9 + * + */ + public interface Logger { + + /** + * Message levels for the {@link Logger loggers} + * returned by the {@link LoggerFinder} service. + * <p> + * A level has a {@linkplain #getName() name} and {@linkplain + * #getSeverity() severity}. + * Standard level values are {@link #ALL}, {@link #TRACE}, {@link #DEBUG}, + * {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #OFF}, + * by order of increasing severity. + * <br> + * {@link #ALL} and {@link #OFF} + * are simple markers with severities mapped respectively to + * {@link java.lang.Integer#MIN_VALUE Integer.MIN_VALUE} and + * {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}. + * <br> + * For convenience the severity values are mapped to corresponding + * {@link java.util.logging.Level java.util.logging.Level} severity + * values. + * + * @since 9 + * + * @see java.lang.System.LoggerFinder + * @see java.lang.System.Logger + */ + public enum Level { + + // for convenience, we're reusing java.util.logging.Level int values + // the mapping logic in sun.util.logging.PlatformLogger depends + // on this. + /** + * A marker to indicate that all levels are enabled. + * This level {@linkplain #getSeverity() severity} is + * {@link Integer#MIN_VALUE}. + */ + ALL(Integer.MIN_VALUE), // usually mapped to/from j.u.l.Level.ALL + /** + * {@code TRACE} level: usually used to log diagnostic information. + * This level {@linkplain #getSeverity() severity} is + * {@code 400}. + */ + TRACE(400), // usually mapped to/from j.u.l.Level.FINER + /** + * {@code DEBUG} level: usually used to log debug information traces. + * This level {@linkplain #getSeverity() severity} is + * {@code 500}. + */ + DEBUG(500), // usually mapped to/from j.u.l.Level.FINEST/FINE/CONFIG + /** + * {@code INFO} level: usually used to log information messages. + * This level {@linkplain #getSeverity() severity} is + * {@code 800}. + */ + INFO(800), // usually mapped to/from j.u.l.Level.INFO + /** + * {@code WARNING} level: usually used to log warning messages. + * This level {@linkplain #getSeverity() severity} is + * {@code 900}. + */ + WARNING(900), // usually mapped to/from j.u.l.Level.WARNING + /** + * {@code ERROR} level: usually used to log error messages. + * This level {@linkplain #getSeverity() severity} is + * {@code 1000}. + */ + ERROR(1000), // usually mapped to/from j.u.l.Level.SEVERE + /** + * A marker to indicate that all levels are disabled. + * This level {@linkplain #getSeverity() severity} is + * {@link Integer#MAX_VALUE}. + */ + OFF(Integer.MAX_VALUE); // usually mapped to/from j.u.l.Level.OFF + + private final int severity; + + private Level(int severity) { + this.severity = severity; + } + + /** + * Returns the name of this level. + * @return this level {@linkplain #name()}. + */ + public final String getName() { + return name(); + } + + /** + * Returns the severity of this level. + * A higher severity means a more severe condition. + * @return this level severity. + */ + public final int getSeverity() { + return severity; + } + } + + /** + * Returns the name of this logger. + * + * @return the logger name. + */ + public String getName(); + + /** + * Checks if a message of the given level would be logged by + * this logger. + * + * @param level the message level. + * @return {@code true} if the given message level is currently being logged. + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public boolean isLoggable(Level level); + + /** + * Logs a message. + * + * @implSpec The default implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msg, (Object[])null);} + * + * @param level the message level. + * @param msg the string message (or a key in the message catalog, if + * this logger is a {@link + * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class) + * localized logger}). + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public default void log(Level level, String msg) { + log(level, (ResourceBundle) null, msg, (Object[]) null); + } + + /** + * Logs a lazily supplied message. + * <p> + * If the logger is currently enabled for the given message level + * then a message is logged that is the result produced by the + * given supplier function. Otherwise, the supplier is not operated on. + * + * @implSpec When logging is enabled for the given level, the default + * implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), (Object[])null);} + * + * @param level the message level. + * @param msgSupplier a supplier function that produces a message. + * + * @throws NullPointerException if {@code level} is {@code null}, or if the level + * is loggable and {@code msgSupplier} is {@code null}. + */ + public default void log(Level level, Supplier<String> msgSupplier) { + if (isLoggable(level)) { + log(level, (ResourceBundle) null, msgSupplier.get(), (Object[]) null); + } + } + + /** + * Logs a message produced from the given object. + * <p> + * If the logger is currently enabled for the given message level then + * a message is logged that, by default, is the result produced from + * calling toString on the given object. + * Otherwise, the object is not operated on. + * + * @implSpec When logging is enabled for the given level, the default + * implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, obj.toString(), (Object[])null);} + * + * @param level the message level. + * @param obj the object to log. + * + * @throws NullPointerException if {@code level} is {@code null}, or if the level + * is loggable and {@code obj} is {@code null}. + */ + public default void log(Level level, Object obj) { + if (isLoggable(level)) { + this.log(level, (ResourceBundle) null, obj.toString(), (Object[]) null); + } + } + + /** + * Logs a message associated with a given throwable. + * + * @implSpec The default implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msg, thrown);} + * + * @param level the message level. + * @param msg the string message (or a key in the message catalog, if + * this logger is a {@link + * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class) + * localized logger}). + * @param thrown a {@code Throwable} associated with the log message. + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public default void log(Level level, String msg, Throwable thrown) { + this.log(level, null, msg, thrown); + } + + /** + * Logs a lazily supplied message associated with a given throwable. + * <p> + * If the logger is currently enabled for the given message level + * then a message is logged that is the result produced by the + * given supplier function. Otherwise, the supplier is not operated on. + * + * @implSpec When logging is enabled for the given level, the default + * implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), thrown);} + * + * @param level one of the message level identifiers. + * @param msgSupplier a supplier function that produces a message. + * @param thrown a {@code Throwable} associated with log message. + * + * @throws NullPointerException if {@code level} is {@code null}, or if the level + * is loggable and {@code msgSupplier} is {@code null}. + */ + public default void log(Level level, Supplier<String> msgSupplier, + Throwable thrown) { + if (isLoggable(level)) { + this.log(level, null, msgSupplier.get(), thrown); + } + } + + /** + * Logs a message with an optional list of parameters. + * + * @implSpec The default implementation for this method calls + * {@code this.log(level, (ResourceBundle)null, format, params);} + * + * @param level one of the message level identifiers. + * @param format the string message format (or a key in the message + * catalog, if this logger is a {@link + * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class) + * localized logger}). + * @param params an optional list of parameters to the message (may be + * none). + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public default void log(Level level, String format, Object... params) { + this.log(level, null, format, params); + } + + /** + * Logs a localized message associated with a given throwable. + * <p> + * If the given resource bundle is non-{@code null}, the {@code msg} + * string is localized using the given resource bundle. + * Otherwise the {@code msg} string is not localized. + * + * @param level the message level. + * @param bundle a resource bundle to localize {@code msg}, can be + * {@code null}. + * @param msg the string message (or a key in the message catalog, + * if {@code bundle} is not {@code null}). + * @param thrown a {@code Throwable} associated with the log message. + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public void log(Level level, ResourceBundle bundle, String msg, + Throwable thrown); + + /** + * Logs a message with resource bundle and an optional list of + * parameters. + * <p> + * If the given resource bundle is non-{@code null}, the {@code format} + * string is localized using the given resource bundle. + * Otherwise the {@code format} string is not localized. + * + * @param level the message level. + * @param bundle a resource bundle to localize {@code format}, can be + * {@code null}. + * @param format the string message format (or a key in the message + * catalog if {@code bundle} is not {@code null}). + * @param params an optional list of parameters to the message (may be + * none). + * + * @throws NullPointerException if {@code level} is {@code null}. + */ + public void log(Level level, ResourceBundle bundle, String format, + Object... params); + + + } + + /** + * The {@code LoggerFinder} service makes it possible to provide an + * alternate implementation for System.Loggers used in the JDK. + * <p> + * <b>Working with loggers obtained from the {@code LoggerFinder}</b> + * <p> + * The {@code LoggerFinder} class makes it possible to replace + * the logging backend through which logging events originating from + * platform classes will be routed. The backend of the default + * {@code LoggerFinder} implementation is still + * {@code java.util.logging}, but it can now be replaced by providing and + * declaring an alternate implementation of the {@code LoggerFinder} + * service. + * <p> + * The JDK uses the {@link java.util.ServiceLoader} + * facility to locate and load a concrete implementation of the + * {@code LoggerFinder} service, from the {@linkplain + * java.lang.ClassLoader#getSystemClassLoader() System ClassLoader}. + * If no custom implementation is found the JDK will use its own default + * implementation. If more than one implementation is found, a + * {@link java.util.ServiceConfigurationError} will be thrown. + * <p> + * Only one instance of the {@code LoggerFinder} implementation is created. + * That instance is responsible for creating, managing, and configuring + * loggers as appropriate to the underlying framework it is using. + * <p> + * In the JDK, a platform class that needs to log messages will obtain + * a logger that will route messages to a {@link Logger Logger} instance + * obtained from one of the factory methods provided by the + * {@code LoggerFinder} implementation. + * <p> + * {@link Logger Logger} instances obtained from the {@code + * LoggerFinder} factory methods are not directly configurable by + * the application. Configuration is the responsibility of the underlying + * logging backend, and usually requires using APIs specific to that + * backend. + * <p> + * System loggers (loggers obtained on behalf of platform classes) are + * usually kept in a global logger tree which is separated from the + * application logger tree. It is the responsibility of the provider of + * the concrete {@code LoggerFinder} implementation to ensure that + * system loggers can not be configured by an application without proper + * permission checks, as configuration performed on system loggers usually + * affects all applications in the same Java Runtime. + * <p> + * <b>Default Implementation</b> + * <p> + * The JDK default implementation of the {@code LoggerFinder} service + * will attempt to bind to the {@linkplain java.util.logging java.logging} + * module. + * When the {@linkplain java.util.logging java.logging} module is present, + * the default implementation will return {@link Logger} instances wrapping + * instances of {@link java.util.logging.Logger java.util.logging.Logger}. + * In that case, configuration may be performed by direct access to the + * {@code java.util.logging} APIs, using {@link java.util.logging.Logger + * java.util.logging.Logger} and {@link java.util.logging.LogManager} to + * access and configure the backend loggers. + * <br> + * If the {@link java.util.logging java.logging} module is not linked + * with the application, and no service implementation for the + * {@code LoggerFinder} has been found, then the default implementation + * will return a simple logger that prints out all log messages of + * {@link java.lang.System.Logger.Level#INFO INFO} + * level and above to the console ({@code System.err}). + * <br> + * These simple loggers are not configurable. + * <p> + * Note that if an implementation of the {@code LoggerFinder} service + * interface has been found by the {@link java.util.ServiceLoader}, then + * the JDK default implementation of {@code LoggerFinder} is not used and + * configuration can only be performed through APIs provided by the backend + * framework which the custom implementation of {@code LoggerFinder} has + * plugged in. + * <br> + * Libraries and classes that only need loggers to produce log messages + * should therefore not attempt to configure loggers by themselves, as that + * would make them dependant from a specific implementation of the + * {@code LoggerFinder} service. + * <p> + * Note also that the {@linkplain java.util.logging java.logging} module + * does not directly provide an implementation of the {@code LoggerFinder} + * service. The JDK default concrete implementation of {@code LoggerFinder} + * use instead a JDK private API to bind to {@code java.util.logging}. + * This makes it possible for an application to replace the logging backend + * <i>even when the java.logging module is present</i>, by simply providing + * and declaring an implementation of the {@link LoggerFinder} service. + * <p> + * <b>Message Levels and Mapping to {@code java.util.logging}</b> + * <p> + * The {@link Logger.Level Logger.Level} enum defines + * a set of standard levels: {@link Logger.Level#ALL ALL}, + * {@link Logger.Level#TRACE TRACE}, + * {@link Logger.Level#DEBUG DEBUG}, + * {@link Logger.Level#INFO INFO}, + * {@link Logger.Level#WARNING WARNING}, + * {@link Logger.Level#ERROR ERROR}, + * and {@link Logger.Level#OFF OFF}, by order of increased severity. + * <br> + * {@link Logger.Level#ALL Level.ALL} and {@link Logger.Level#OFF Level.OFF} + * are simple markers with severities mapped respectively to + * {@link java.lang.Integer#MIN_VALUE Integer.MIN_VALUE} and + * {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}. + * <p> + * When the logging backend supports custom levels, how to map System.Logger + * levels to backend levels is the responsibility of the backend. + * <p> + * When {@link java.util.logging} is the backend, System.Logger levels are + * mapped to {@linkplain java.util.logging.Level java.util.logging levels} + * as follows: + * <br><br> + * <table border="1"> + * <caption>System.Logger Level Mapping</caption> + * <tr><td><b>System.Logger Levels</b></td> + * <td>{@link Logger.Level#ALL ALL}</td> + * <td>{@link Logger.Level#TRACE TRACE}</td> + * <td>{@link Logger.Level#DEBUG DEBUG}</td> + * <td>{@link Logger.Level#INFO INFO}</td> + * <td>{@link Logger.Level#WARNING WARNING}</td> + * <td>{@link Logger.Level#ERROR ERROR}</td> + * <td>{@link Logger.Level#OFF OFF}</td> + * </tr> + * <tr><td><b>java.util.logging Backend</b></td> + * <td>{@link java.util.logging.Level#ALL ALL}</td> + * <td>{@link java.util.logging.Level#FINER FINER}</td> + * <td>{@link java.util.logging.Level#FINE FINE}</td> + * <td>{@link java.util.logging.Level#INFO INFO}</td> + * <td>{@link java.util.logging.Level#WARNING WARNING}</td> + * <td>{@link java.util.logging.Level#SEVERE SEVERE}</td> + * <td>{@link java.util.logging.Level#OFF OFF}</td> + * </tr> + * </table> + * <p> + * <b>Migrating From {@code java.util.logging}</b> + * <p> + * Usually - an application that uses a logging framework will log messages + * through a logger facade defined (or supported) by that framework. + * Applications that wish to use an external framework should log + * through the facade associated with that framework. + * <p> + * <b>Changing the {@code LoggerFinder} Implementation</b> + * <p> + * An application wishing to change the implementation of the + * {@code LoggerFinder} is expected to provide a concrete implementation + * of the {@code LoggerFinder} abstract class accessible from the + * {@linkplain java.lang.ClassLoader#getSystemClassLoader() + * System ClassLoader} and registered as a service in such a way that it + * can be located and loaded by the {@link java.util.ServiceLoader}. + * Note that because the loaded instance of {@code LoggerFinder} is + * global, it will be only looked for in the {@linkplain + * java.lang.ClassLoader#getSystemClassLoader() System ClassLoader} (and its + * parents). + * <p> + * + * @see java.lang.System + * @see java.lang.System.Logger + * + * @since 9 + */ + public static abstract class LoggerFinder { + /** + * The {@code RuntimePermission("loggerFinder")} is + * necessary to subclass and instantiate the {@code LoggerFinder} class, + * as well as to obtain loggers from an instance of that class. + */ + public static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + /** + * Creates a new instance of {@code LoggerFinder}. + * + * Only one instance will be created. + * + * @implNote It is recommended that subclasses of {@code LoggerFinder} + * do not perform any heavy initialization in their constructor, in + * order to avoid possible risks of deadlock or class loading cycles + * during the instantiation of the provider. + * + * @throws SecurityException if the calling code does not have the + * {@code RuntimePermission("loggerFinder")}. + */ + protected LoggerFinder() { + this(checkPermission()); + } + + private LoggerFinder(Void unused) { + // nothing to do. + } + + private static Void checkPermission() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return null; + } + + /** + * Returns an instance of {@link Logger Logger} + * suitable for the given caller. + * + * @param name the name of the logger. + * @param caller the class for which the logger is being requested. + * + * @return a {@link Logger logger} suitable for the given caller's + * use. + * @throws SecurityException if the calling code does not have the + * {@code RuntimePermission("loggerFinder")}. + */ + public abstract Logger getLogger(String name, /* Module */ Class<?> caller); + + /** + * Returns a localizable instance of {@link Logger Logger} + * suitable for the given caller. + * The returned logger will use the provided resource bundle for + * message localization. + * + * @implSpec By default, this method calls {@link + * #getLogger(java.lang.String, java.lang.Class) + * this.getLogger(name, caller)} to obtain a logger, then wraps that + * logger in a {@link Logger} instance where all methods that do not + * take a {@link ResourceBundle} as parameter are redirected to one + * which does - passing the given {@code bundle} for + * localization. So for instance, a call to {@link + * Logger#log(Level, String) Logger.log(Level.INFO, msg)} + * will end up as a call to {@link + * Logger#log(Level, ResourceBundle, String, Object...) + * Logger.log(Level.INFO, bundle, msg, (Object[])null)} on the wrapped + * logger object. + * Note however that by default, string messages returned by {@link + * java.util.function.Supplier Supplier&lt;String&gt;} will not be + * localized, as it is assumed that such strings are messages which are + * already constructed, rather than keys in a resource bundle. + * <p> + * An implementation of {@code LoggerFinder} is not + * constrained to use the default implementation of this method. In + * particular, if the underlying logging backend provides its own + * mechanism for localizing log messages, then such a + * {@code LoggerFinder} would be free to return a logger + * that makes direct use of the mechanism provided by the backend. + * + * @param name the name of the logger. + * @param bundle a resource bundle. + * @param caller the class for which the logger is being requested. + * @return an instance of {@link Logger Logger} which will use the + * provided resource bundle for message localization. + * + * @throws SecurityException if the calling code does not have the + * {@code RuntimePermission("loggerFinder")}. + */ + public Logger getLocalizedLogger(String name, ResourceBundle bundle, + /* Module */ Class<?> caller) { + return new LocalizedLoggerWrapper<>(getLogger(name, caller), bundle); + } + + /** + * Returns the loaded {@link LoggerFinder LoggerFinder} instance. + * @return the loaded {@link LoggerFinder LoggerFinder} instance. + * @throws SecurityException if the calling code does not have the + * {@code RuntimePermission("loggerFinder")}. + */ + public static LoggerFinder getLoggerFinder() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return LoggerFinderLoader.spi(); + } + + /** + * Helper class used to load the {@link LoggerFinder}. + */ + final static class LoggerFinderLoader { + private static volatile LoggerFinder spi; + private static final Object lock = new int[0]; + static final Permission CLASSLOADER_PERMISSION = + SecurityConstants.GET_CLASSLOADER_PERMISSION; + static final Permission READ_PERMISSION = + new FilePermission("<<ALL FILES>>", + SecurityConstants.FILE_READ_ACTION); + + // This class is static and cannot be instantiated. + private LoggerFinderLoader() { + throw new InternalError("LoggerFinderLoader cannot be instantiated"); + } + + + /** + * The DefaultLoggerFinder is used when no {@link + * LoggerFinder} implementation can be found. + * <p> + * By default, when {@link java.util.logging} + * is present, the {@code DefaultLoggerFinder} returns loggers that + * wrap {@link java.util.logging.Logger} created by the internal + * {@link sun.util.logging.internal.JdkLoggingProvider}. + * Backend loggers are obtained by + * calling {@link + * java.util.logging.LogManager#demandLoggerFor(java.lang.String, java.lang.Class)}. + * Backend loggers can be configured by direct + * use of the {@link java.util.logging} APIs. + * <p> + * When {@link java.util.logging} + * is <b>not</b> present, the {@code DefaultLoggerFinder} returns + * {@linkplain sun.util.logger.SimpleConsoleLogger + * simple console loggers}. + * By default, only message of level {@code INFO} and above will be + * printed on the console. + * For compatibility reason system loggers can be configured by + * direct use of the internal {@code PlatformLogger} API. + * For application loggers however, there is no API that would allow + * the application to configure these loggers. + * <p> + * Therefore an application that needs to configure loggers should + * either link with {@link java.util.logging} or install its own + * {@link LoggerFinder} service. + */ + final static class DefaultLoggerFinder extends LoggerFinder { + + // Default JdkLoggerProvider used when no JdkLoggerProvider is + // declared as an installed service. + static final class DefaultJdkLoggerProvider extends JdkLoggerProvider { + } + + private static JdkLoggerProvider loadJdkLoggerProvider() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + + final ServiceLoader<JdkLoggerProvider> loader; + if (sm == null) { + loader = ServiceLoader.loadInstalled(JdkLoggerProvider.class); + } else { + // We use limited do privileged here - the minimum set of + // permissions required to 'see' the META-INF/services resources + // seems to be CLASSLOADER_PERMISSION and READ_PERMISSION. + // Note that do privileged is required because + // otherwise the SecurityManager will prevent the ServiceLoader + // from seeing the installed provider. + loader = AccessController.doPrivileged( + (PrivilegedAction<ServiceLoader<JdkLoggerProvider>>) + () -> ServiceLoader.loadInstalled(JdkLoggerProvider.class), + null, CLASSLOADER_PERMISSION, READ_PERMISSION); + } + JdkLoggerProvider result = null; + try { + if (loader.iterator().hasNext()) { + result = loader.iterator().next(); + } + } catch (Throwable x) { + // should report configuration error. + // use the default. + } + if (result == null) { + result = new DefaultJdkLoggerProvider(); + } + return result; + } + + final JdkLoggerProvider jdkLoggerProvider; + + private DefaultLoggerFinder() { + this(loadJdkLoggerProvider()); + } + + private DefaultLoggerFinder(JdkLoggerProvider jdkLoggerProvider) { + this.jdkLoggerProvider = jdkLoggerProvider; + } + + @Override + public Logger getLogger(String name, Class<?> caller) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return jdkLoggerProvider.getJdkLogger(name, caller); + } + + } + + // Return the loaded LoggerFinder, or load it if not already loaded. + static LoggerFinder spi() { + if (spi != null) return spi; + synchronized(lock) { + if (spi != null) return spi; + spi = AccessController.doPrivileged( + (PrivilegedAction<LoggerFinder>) LoggerFinderLoader::load, + null, LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION, READ_PERMISSION); + } + // Since the LoggerFinder is already loaded - we can stop using + // temporary loggers. + BootstrapLogger.redirectTemporaryLoggers(); + return spi; + } + + // Loads the LoggerFinder using ServiceLoader. If no LoggerFinder + // is found returns the default (possibly JUL based) implementation + private static LoggerFinder load() { + LoggerFinder result; + try { + ServiceLoader<LoggerFinder> loader = + ServiceLoader.load(LoggerFinder.class, + ClassLoader.getSystemClassLoader()); + final java.util.Iterator<LoggerFinder> iterator = + loader.iterator(); + if (iterator.hasNext()) { + result = iterator.next(); + if (iterator.hasNext()) { + throw new ServiceConfigurationError( + LoggerFinder.class.getName() + + ": several implementations found"); + } + } else { + result = getDefaultImplementation(); + } + } catch(Error | RuntimeException x) { + //TODO: Use an ErrorManager to log that + spi = getDefaultImplementation(); + throw x; + } + return result; + } + + private static LoggerFinder getDefaultImplementation() { + return new DefaultLoggerFinder(); + } + + } + } + + private static LoggerFinder accessProvider() { + return LoggerFinder.LoggerFinderLoader.spi(); + } + + /** + * Returns an instance of {@link Logger Logger} suitable for the caller's + * use. + * + * @implSpec + * Instances returned by this method route messages to loggers + * obtained by calling {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class) + * LoggerFinder.getLogger(name, caller)}. + * + * @apiNote + * Calling this method may delay the creation of the actual logger. + * A common reason for delaying the creation of the actual logger is, + * for instance, if the Java Runtime is not finished booting at the time + * the logger is requested. + * + * @param name the name of the logger. + * @return an instance of {@link Logger} that can be used by the calling + * class. + * @throws NullPointerException if {@code name} is {@code null}. + */ + @CallerSensitive + public static Logger getLogger(String name) { + final Class<?> caller = Reflection.getCallerClass(); + // should we use lazy loggers only for platform loggers - not for all + // system loggers? In other words, should we have a version of + // getSystemLogger that takes an additional 'allowLazyLogger' boolean + // parameter? + // This method will create LazyLogger only if the Caller is in the + // Boot ClassLoader + return LazyLoggers.getLogger(Objects.requireNonNull(name), caller); + } + + /** + * Returns a localizable instance of {@link Logger + * Logger} suitable for the caller use. + * The returned logger will use the provided resource bundle for message + * localization. + * + * @implSpec + * The returned logger will perform message localization as specified + * by {@link LoggerFinder#getLocalizedLogger(java.lang.String, + * java.util.ResourceBundle, java.lang.Class) + * LoggerFinder.getLocalizedLogger(name, bundle, caller}. + * + * @apiNote + * Calling this method may trigger the immediate loading and initialization + * of the {@link LoggerFinder} service, which may cause issues if the + * Java Runtime is not ready to initialize the concrete service + * implementation yet. + * Platform classes which may be loaded early in the boot sequence and + * need to log localized messages may prefer to create a logger using + * {@link #getLogger(java.lang.String)} and then use the log methods that + * take a resource bundle as parameter. + * + * @param name the name of the logger. + * @param bundle a resource bundle. + * @return an instance of {@link Logger} which will use the provided + * resource bundle for message localization. + * @throws NullPointerException if {@code name} or {@code bundle} is + * {@code null} + */ + @CallerSensitive + public static Logger getLogger(String name, ResourceBundle bundle) { + final Class<?> caller = Reflection.getCallerClass(); + final SecurityManager sm = System.getSecurityManager(); + final ResourceBundle rb = Objects.requireNonNull(bundle); + // We don't use LazyLoggers if a resource bundle is specified. + // Bootstrap sensitive classes in the JDK do not use resource bundles + // when logging. This could be revisited later, if it needs to. + if (sm != null) { + return AccessController.doPrivileged((PrivilegedAction<Logger>) + () -> accessProvider().getLocalizedLogger(name, rb, caller), + null, + LoggerFinder.LOGGERFINDER_PERMISSION); + } + return accessProvider().getLocalizedLogger(name, rb, caller); + } + + /** * Terminates the currently running Java Virtual Machine. The * argument serves as a status code; by convention, a nonzero status * code indicates abnormal termination. * <p> * This method calls the <code>exit</code> method in class
< prev index next >