< 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<String>} 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 >