--- old/jdk/src/java.base/share/classes/java/lang/System.java 2015-11-20 17:44:01.000000000 +0100 +++ new/jdk/src/java.base/share/classes/java/lang/System.java 2015-11-20 17:44:01.000000000 +0100 @@ -30,13 +30,14 @@ 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 java.util.Objects; +import java.util.ResourceBundle; +import java.util.function.Supplier; import sun.nio.ch.Interruptible; import sun.reflect.CallerSensitive; import sun.reflect.Reflection; @@ -45,6 +46,9 @@ import jdk.internal.HotSpotIntrinsicCandidate; import jdk.internal.misc.JavaLangAccess;; import jdk.internal.misc.SharedSecrets;; +import jdk.internal.logger.LoggerFinderLoader; +import jdk.internal.logger.LazyLoggers; +import jdk.internal.logger.LocalizedLoggerWrapper; /** * The System class contains several useful class fields @@ -944,6 +948,648 @@ } /** + * {@code System.Logger} instances log messages that will be + * routed to the underlying logging framework the {@link System.LoggerFinder + * LoggerFinder} uses. + *

+ * {@code System.Logger} instances are typically 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)}. + * + * @see java.lang.System#getLogger(java.lang.String) + * @see java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle) + * @see java.lang.System.LoggerFinder + * + * @since 9 + * + */ + public interface Logger { + + /** + * System {@linkplain Logger loggers} levels. + *

+ * A level has a {@linkplain #getName() name} and {@linkplain + * #getSeverity() severity}. + * Level values are {@link #ALL}, {@link #TRACE}, {@link #DEBUG}, + * {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #OFF}, + * by order of increasing severity. + *
+ * {@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}. + *

+ * Severity values and Mapping to {@code java.util.logging.Level}. + *

+ * {@linkplain System.Logger.Level System logger levels} are mapped to + * {@linkplain java.util.logging.Level java.util.logging levels} + * of corresponding severity. + *
The mapping is as follows: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
System.Logger Severity Level Mapping
System.Logger 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}{@link Logger.Level#OFF OFF}
java.util.logging Levels{@link java.util.logging.Level#ALL ALL}{@link java.util.logging.Level#FINER FINER}{@link java.util.logging.Level#FINE FINE}{@link java.util.logging.Level#INFO INFO}{@link java.util.logging.Level#WARNING WARNING}{@link java.util.logging.Level#SEVERE SEVERE}{@link java.util.logging.Level#OFF OFF}
+ * + * @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), // typically 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), // typically 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), // typically 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), // typically 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), // typically 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), // typically 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); // typically 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 log message level. + * @return {@code true} if the given log 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 log 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}); can be {@code null}. + * + * @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. + *

+ * If the logger is currently enabled for the given log 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 log message level. + * @param msgSupplier a supplier function that produces a message. + * + * @throws NullPointerException if {@code level} is {@code null}, + * or {@code msgSupplier} is {@code null}. + */ + public default void log(Level level, Supplier msgSupplier) { + Objects.requireNonNull(msgSupplier); + if (isLoggable(Objects.requireNonNull(level))) { + log(level, (ResourceBundle) null, msgSupplier.get(), (Object[]) null); + } + } + + /** + * Logs a message produced from the given object. + *

+ * If the logger is currently enabled for the given log 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 log message level. + * @param obj the object to log. + * + * @throws NullPointerException if {@code level} is {@code null}, or + * {@code obj} is {@code null}. + */ + public default void log(Level level, Object obj) { + Objects.requireNonNull(obj); + if (isLoggable(Objects.requireNonNull(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 log 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}); can be {@code null}. + * @param thrown a {@code Throwable} associated with the log message; + * can be {@code null}. + * + * @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. + *

+ * If the logger is currently enabled for the given log 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 log message level identifiers. + * @param msgSupplier a supplier function that produces a message. + * @param thrown a {@code Throwable} associated with log message; + * can be {@code null}. + * + * @throws NullPointerException if {@code level} is {@code null}, or + * {@code msgSupplier} is {@code null}. + */ + public default void log(Level level, Supplier msgSupplier, + Throwable thrown) { + Objects.requireNonNull(msgSupplier); + if (isLoggable(Objects.requireNonNull(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 log message level identifiers. + * @param format the string message format in {@link + * java.text.MessageFormat} 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}); can be {@code null}. + * @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. + *

+ * 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 log 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}); can be {@code null}. + * @param thrown a {@code Throwable} associated with the log message; + * can be {@code null}. + * + * @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. + *

+ * 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 log message level. + * @param bundle a resource bundle to localize {@code format}; can be + * {@code null}. + * @param format the string message format in {@link + * java.text.MessageFormat} format, (or a key in the message + * catalog if {@code bundle} is not {@code null}); can be {@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 is responsible for creating, managing, + * and configuring loggers to the underlying framework it uses. + *

+ * A logger finder is a concrete implementation of this class that has a + * zero-argument constructor and implements the abstract methods defined + * by this class. + * The loggers returned from a logger finder are capable of routing log + * messages to the logging backend this provider supports. + * A given invocation of the Java Runtime maintains a single + * system-wide LoggerFinder instance that is loaded as follows: + *

+ *

+ * An application can replace the logging backend + * even when the java.logging module is present, by simply providing + * and declaring an implementation of the {@link LoggerFinder} service. + *

+ * Default Implementation + *

+ * The system default {@code LoggerFinder} implementation uses + * {@code java.util.logging} as the backend framework when the + * {@code java.logging} module is present. + * It returns a {@linkplain System.Logger logger} instance + * that will route log messages to a {@link java.util.logging.Logger + * java.util.logging.Logger}. Otherwise, if {@code java.logging} is not + * present, the default implementation will return a simple logger + * instance that will route log messages of {@code INFO} level and above to + * the console ({@code System.err}). + *

+ * Logging Configuration + *

+ * {@linkplain 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. + *

For the default {@code LoggerFinder} implementation + * using {@code java.util.logging} as its backend, refer to + * {@link java.util.logging java.util.logging} for logging configuration. + * For the default {@code LoggerFinder} implementation returning simple loggers + * when the {@code java.logging} module is absent, the configuration + * is implementation dependent. + *

+ * 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. + *

+ * A system class that needs to log messages will typically obtain + * a {@link System.Logger} instance to route messages to the logging + * framework selected by the application. + *

+ * Libraries and classes that only need loggers to produce log messages + * should not attempt to configure loggers by themselves, as that + * would make them dependent from a specific implementation of the + * {@code LoggerFinder} service. + *

+ * In addition, when a security manager is present, loggers provided to + * system classes should not be directly configurable through the logging + * backend without requiring permissions. + *
+ * It is the responsibility of the provider of + * the concrete {@code LoggerFinder} implementation to ensure that + * these loggers are not configured by untrusted code without proper + * permission checks, as configuration performed on such loggers usually + * affects all applications in the same Java Runtime. + *

+ * Message Levels and Mapping to backend levels + *

+ * A logger finder is responsible for mapping from a {@code + * System.Logger.Level} to a level supported by the logging backend it uses. + *
The default LoggerFinder using {@code java.util.logging} as the backend + * maps {@code System.Logger} levels to + * {@linkplain java.util.logging.Level java.util.logging} levels + * of corresponding severity - as described in {@link Logger.Level + * Logger.Level}. + * + * @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. + */ + static final RuntimePermission LOGGERFINDER_PERMISSION = + new RuntimePermission("loggerFinder"); + + /** + * Creates a new instance of {@code LoggerFinder}. + * + * @implNote It is recommended that a {@code LoggerFinder} service + * implementation does not perform any heavy initialization in its + * constructor, in order to avoid possible risks of deadlock or class + * loading cycles during the instantiation of the service provider. + * + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow 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} + * for the given {@code caller}. + * + * @param name the name of the logger. + * @param caller the class for which the logger is being requested; + * can be {@code null}. + * + * @return a {@link Logger logger} suitable for the given caller's + * use. + * @throws NullPointerException if {@code name} is {@code null} or + * {@code caller} is {@code null}. + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow the + * {@code RuntimePermission("loggerFinder")}. + */ + public abstract Logger getLogger(String name, /* Module */ Class caller); + + /** + * Returns a localizable instance of {@link Logger Logger} + * for the given {@code 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 instance. + * 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. + *

+ * An implementation of {@code LoggerFinder} may override this method, + * for example, when 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; can be {@code null}. + * @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 NullPointerException if {@code name} is {@code null} or + * {@code caller} is {@code null}. + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow the + * {@code RuntimePermission("loggerFinder")}. + */ + public Logger getLocalizedLogger(String name, ResourceBundle bundle, + /* Module */ Class caller) { + return new LocalizedLoggerWrapper<>(getLogger(name, caller), bundle); + } + + /** + * Returns the {@code LoggerFinder} instance. There is one + * single system-wide {@code LoggerFinder} instance in + * the Java Runtime. See the class specification of how the + * {@link LoggerFinder LoggerFinder} implementation is located and + * loaded. + + * @return the {@link LoggerFinder LoggerFinder} instance. + * @throws SecurityException if a security manager is present and its + * {@code checkPermission} method doesn't allow the + * {@code RuntimePermission("loggerFinder")}. + */ + public static LoggerFinder getLoggerFinder() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(LOGGERFINDER_PERMISSION); + } + return accessProvider(); + } + + + private static volatile LoggerFinder service; + static LoggerFinder accessProvider() { + // We do not need to synchronize: LoggerFinderLoader will + // always return the same instance, so if we don't have it, + // just fetch it again. + if (service == null) { + PrivilegedAction pa = + () -> LoggerFinderLoader.getLoggerFinder(); + service = AccessController.doPrivileged(pa, null, + LOGGERFINDER_PERMISSION); + } + return service; + } + + } + + + /** + * Returns an instance of {@link Logger Logger} 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 + * This method may defer calling the {@link + * LoggerFinder#getLogger(java.lang.String, java.lang.Class) + * LoggerFinder.getLogger} method to create an actual logger supplied by + * the logging backend, for instance, to allow loggers to be obtained during + * the system initialization time. + * + * @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) { + Objects.requireNonNull(name); + final Class caller = Reflection.getCallerClass(); + return LazyLoggers.getLogger(name, caller); + } + + /** + * Returns a localizable instance of {@link Logger + * Logger} for the caller's 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 + * This method is intended to be used after the system is fully initialized. + * 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. + * System classes which may be loaded early in the boot sequence and + * need to log localized messages should 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} is {@code null} or + * {@code bundle} is {@code null}. + */ + @CallerSensitive + public static Logger getLogger(String name, ResourceBundle bundle) { + final ResourceBundle rb = Objects.requireNonNull(bundle); + Objects.requireNonNull(name); + final Class caller = Reflection.getCallerClass(); + final SecurityManager sm = System.getSecurityManager(); + // 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) + () -> LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller), + null, + LoggerFinder.LOGGERFINDER_PERMISSION); + } + return LoggerFinder.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.