/* * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.util.logging; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.ResourceBundle; import java.util.function.Supplier; import jdk.internal.logger.LazyLoggers; import jdk.internal.logger.LoggerWrapper; /** * Platform logger provides an API for the JRE components to log * messages. This enables the runtime components to eliminate the * static dependency of the logging facility and also defers the * java.util.logging initialization until it is enabled. * In addition, the PlatformLogger API can be used if the logging * module does not exist. * * If the logging facility is not enabled, the platform loggers * will output log messages per the default logging configuration * (see below). In this implementation, it does not log the * the stack frame information issuing the log message. * * When the logging facility is enabled (at startup or runtime), * the backend logger will be created for each platform * logger and all log messages will be forwarded to the Logger * to handle. * * The PlatformLogger uses an underlying PlatformLogger.Bridge instance * obtained by calling {@link PlatformLogger.Bridge#convert PlatformLogger.Bridge.convert(} * {@link jdk.internal.logger.LazyLoggers#getLazyLogger(java.lang.String, java.lang.Class) * jdk.internal.logger.LazyLoggers#getLazyLogger(name, PlatformLogger.class))}. * * Logging facility is "enabled" when one of the following * conditions is met: * 1) ServiceLoader.load({@link java.lang.System.LoggerFinder LoggerFinder.class}, * ClassLoader.getSystemClassLoader()).iterator().hasNext(). * 2) ServiceLoader.loadInstalled({@link jdk.internal.logger.DefaultLoggerFinder}).iterator().hasNext(), * and 2.1) a system property "java.util.logging.config.class" or * "java.util.logging.config.file" is set * or 2.2) java.util.logging.LogManager or java.util.logging.Logger * is referenced that will trigger the logging initialization. * * Default logging configuration: * * No LoggerFinder service implementation declared * global logging level = INFO * handlers = java.util.logging.ConsoleHandler * java.util.logging.ConsoleHandler.level = INFO * java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter * * Limitation: * {@code <JAVA_HOME>/conf/logging.properties} is the system-wide logging * configuration defined in the specification and read in the * default case to configure any java.util.logging.Logger instances. * Platform loggers will not detect if {@code <JAVA_HOME>/conf/logging.properties} * is modified. In other words, unless the java.util.logging API * is used at runtime or the logging system properties is set, * the platform loggers will use the default setting described above. * The platform loggers are designed for JDK developers use and * this limitation can be workaround with setting * -Djava.util.logging.config.file system property. * <br> * Calling PlatformLogger.setLevel will not work when there is a custom * LoggerFinder installed - and as a consequence {@link #setLevel setLevel} * is now deprecated. * * @since 1.7 */ public class PlatformLogger { /** * PlatformLogger logging levels. */ public static enum Level { // The name and value must match that of {@code java.util.logging.Level}s. // Declare in ascending order of the given value for binary search. ALL(System.Logger.Level.ALL), FINEST(System.Logger.Level.TRACE), FINER(System.Logger.Level.TRACE), FINE(System.Logger.Level.DEBUG), CONFIG(System.Logger.Level.DEBUG), INFO(System.Logger.Level.INFO), WARNING(System.Logger.Level.WARNING), SEVERE(System.Logger.Level.ERROR), OFF(System.Logger.Level.OFF); final System.Logger.Level systemLevel; Level(System.Logger.Level systemLevel) { this.systemLevel = systemLevel; } // The integer values must match that of {@code java.util.logging.Level} // objects. private static final int SEVERITY_OFF = Integer.MAX_VALUE; private static final int SEVERITY_SEVERE = 1000; private static final int SEVERITY_WARNING = 900; private static final int SEVERITY_INFO = 800; private static final int SEVERITY_CONFIG = 700; private static final int SEVERITY_FINE = 500; private static final int SEVERITY_FINER = 400; private static final int SEVERITY_FINEST = 300; private static final int SEVERITY_ALL = Integer.MIN_VALUE; // ascending order for binary search matching the list of enum constants private static final int[] LEVEL_VALUES = new int[] { SEVERITY_ALL, SEVERITY_FINEST, SEVERITY_FINER, SEVERITY_FINE, SEVERITY_CONFIG, SEVERITY_INFO, SEVERITY_WARNING, SEVERITY_SEVERE, SEVERITY_OFF }; public System.Logger.Level systemLevel() { return systemLevel; } public int intValue() { return LEVEL_VALUES[this.ordinal()]; } /** * Maps a severity value to an effective logger level. * @param level The severity of the messages that should be * logged with a logger set to the returned level. * @return The effective logger level, which is the nearest Level value * whose severity is greater or equal to the given level. * For level > SEVERE (OFF excluded), return SEVERE. */ public static Level valueOf(int level) { switch (level) { // ordering per the highest occurrences in the jdk source // finest, fine, finer, info first case SEVERITY_FINEST : return Level.FINEST; case SEVERITY_FINE : return Level.FINE; case SEVERITY_FINER : return Level.FINER; case SEVERITY_INFO : return Level.INFO; case SEVERITY_WARNING : return Level.WARNING; case SEVERITY_CONFIG : return Level.CONFIG; case SEVERITY_SEVERE : return Level.SEVERE; case SEVERITY_OFF : return Level.OFF; case SEVERITY_ALL : return Level.ALL; } // return the nearest Level value >= the given level, // for level > SEVERE, return SEVERE and exclude OFF int i = Arrays.binarySearch(LEVEL_VALUES, 0, LEVEL_VALUES.length-2, level); return values()[i >= 0 ? i : (-i-1)]; } } /** * * The PlatformLogger.Bridge interface is implemented by the System.Logger * objects returned by our default JUL provider - so that JRE classes using * PlatformLogger see no difference when JUL is the actual backend. * * PlatformLogger is now only a thin adaptation layer over the same * loggers than returned by java.lang.System.getLogger(String name). * * The recommendation for JRE classes going forward is to use * java.lang.System.getLogger(String name), which will * use Lazy Loggers when possible and necessary. * */ public static interface Bridge { /** * Gets the name for this platform logger. * @return the name of the platform logger. */ public String getName(); /** * Returns true if a message of the given level would actually * be logged by this logger. * @param level the level * @return whether a message of that level would be logged */ public boolean isLoggable(Level level); public boolean isEnabled(); public void log(Level level, String msg); public void log(Level level, String msg, Throwable thrown); public void log(Level level, String msg, Object... params); public void log(Level level, Supplier<String> msgSupplier); public void log(Level level, Throwable thrown, Supplier<String> msgSupplier); public void logp(Level level, String sourceClass, String sourceMethod, String msg); public void logp(Level level, String sourceClass, String sourceMethod, Supplier<String> msgSupplier); public void logp(Level level, String sourceClass, String sourceMethod, String msg, Object... params); public void logp(Level level, String sourceClass, String sourceMethod, String msg, Throwable thrown); public void logp(Level level, String sourceClass, String sourceMethod, Throwable thrown, Supplier<String> msgSupplier); public void logrb(Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Object... params); public void logrb(Level level, String sourceClass, String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown); public void logrb(Level level, ResourceBundle bundle, String msg, Object... params); public void logrb(Level level, ResourceBundle bundle, String msg, Throwable thrown); public static Bridge convert(System.Logger logger) { if (logger instanceof PlatformLogger.Bridge) { return (Bridge) logger; } else { return new LoggerWrapper<>(logger); } } } /** * The {@code PlatformLogger.ConfigurableBridge} interface is used to * implement the deprecated {@link PlatformLogger#setLevel} method. * * PlatformLogger is now only a thin adaptation layer over the same * loggers than returned by java.lang.System.getLogger(String name). * * The recommendation for JRE classes going forward is to use * java.lang.System.getLogger(String name), which will * use Lazy Loggers when possible and necessary. * */ public static interface ConfigurableBridge { public abstract class LoggerConfiguration { public abstract Level getPlatformLevel(); public abstract void setPlatformLevel(Level level); } public default LoggerConfiguration getLoggerConfiguration() { return null; } public static LoggerConfiguration getLoggerConfiguration(PlatformLogger.Bridge logger) { if (logger instanceof PlatformLogger.ConfigurableBridge) { return ((ConfigurableBridge) logger).getLoggerConfiguration(); } else { return null; } } } // Table of known loggers. Maps names to PlatformLoggers. private static final Map<String,WeakReference<PlatformLogger>> loggers = new HashMap<>(); /** * Returns a PlatformLogger of a given name. * @param name the name of the logger * @return a PlatformLogger */ public static synchronized PlatformLogger getLogger(String name) { PlatformLogger log = null; WeakReference<PlatformLogger> ref = loggers.get(name); if (ref != null) { log = ref.get(); } if (log == null) { log = new PlatformLogger(PlatformLogger.Bridge.convert( // We pass PlatformLogger.class.getModule() (java.base) // rather than the actual module of the caller // because we want PlatformLoggers to be system loggers: we // won't need to resolve any resource bundles anyway. // Note: Many unit tests depend on the fact that // PlatformLogger.getLoggerFromFinder is not caller // sensitive, and this strategy ensure that the tests // still pass. LazyLoggers.getLazyLogger(name, PlatformLogger.class.getModule()))); loggers.put(name, new WeakReference<>(log)); } return log; } // The system loggerProxy returned by LazyLoggers // This may be a lazy logger - see jdk.internal.logger.LazyLoggers, // or may be a Logger instance (or a wrapper thereof). // private final PlatformLogger.Bridge loggerProxy; private PlatformLogger(PlatformLogger.Bridge loggerProxy) { this.loggerProxy = loggerProxy; } /** * A convenience method to test if the logger is turned off. * (i.e. its level is OFF). * @return whether the logger is turned off. */ public boolean isEnabled() { return loggerProxy.isEnabled(); } /** * Gets the name for this platform logger. * @return the name of the platform logger. */ public String getName() { return loggerProxy.getName(); } /** * Returns true if a message of the given level would actually * be logged by this logger. * @param level the level * @return whether a message of that level would be logged */ public boolean isLoggable(Level level) { if (level == null) { throw new NullPointerException(); } return loggerProxy.isLoggable(level); } /** * Get the log level that has been specified for this PlatformLogger. * The result may be null, which means that this logger's * effective level will be inherited from its parent. * * @return this PlatformLogger's level */ public Level level() { final ConfigurableBridge.LoggerConfiguration spi = PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy); return spi == null ? null : spi.getPlatformLevel(); } /** * Set the log level specifying which message levels will be * logged by this logger. Message levels lower than this * value will be discarded. The level value {@link Level#OFF} * can be used to turn off logging. * <p> * If the new level is null, it means that this node should * inherit its level from its nearest ancestor with a specific * (non-null) level value. * * @param newLevel the new value for the log level (may be null) * @deprecated Platform Loggers should not be configured programmatically. * This method will not work if a custom {@link * java.lang.System.LoggerFinder} is installed. */ @Deprecated public void setLevel(Level newLevel) { final ConfigurableBridge.LoggerConfiguration spi = PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);; if (spi != null) { spi.setPlatformLevel(newLevel); } } /** * Logs a SEVERE message. * @param msg the message */ public void severe(String msg) { loggerProxy.log(Level.SEVERE, msg, (Object[])null); } public void severe(String msg, Throwable t) { loggerProxy.log(Level.SEVERE, msg, t); } public void severe(String msg, Object... params) { loggerProxy.log(Level.SEVERE, msg, params); } /** * Logs a WARNING message. * @param msg the message */ public void warning(String msg) { loggerProxy.log(Level.WARNING, msg, (Object[])null); } public void warning(String msg, Throwable t) { loggerProxy.log(Level.WARNING, msg, t); } public void warning(String msg, Object... params) { loggerProxy.log(Level.WARNING, msg, params); } /** * Logs an INFO message. * @param msg the message */ public void info(String msg) { loggerProxy.log(Level.INFO, msg, (Object[])null); } public void info(String msg, Throwable t) { loggerProxy.log(Level.INFO, msg, t); } public void info(String msg, Object... params) { loggerProxy.log(Level.INFO, msg, params); } /** * Logs a CONFIG message. * @param msg the message */ public void config(String msg) { loggerProxy.log(Level.CONFIG, msg, (Object[])null); } public void config(String msg, Throwable t) { loggerProxy.log(Level.CONFIG, msg, t); } public void config(String msg, Object... params) { loggerProxy.log(Level.CONFIG, msg, params); } /** * Logs a FINE message. * @param msg the message */ public void fine(String msg) { loggerProxy.log(Level.FINE, msg, (Object[])null); } public void fine(String msg, Throwable t) { loggerProxy.log(Level.FINE, msg, t); } public void fine(String msg, Object... params) { loggerProxy.log(Level.FINE, msg, params); } /** * Logs a FINER message. * @param msg the message */ public void finer(String msg) { loggerProxy.log(Level.FINER, msg, (Object[])null); } public void finer(String msg, Throwable t) { loggerProxy.log(Level.FINER, msg, t); } public void finer(String msg, Object... params) { loggerProxy.log(Level.FINER, msg, params); } /** * Logs a FINEST message. * @param msg the message */ public void finest(String msg) { loggerProxy.log(Level.FINEST, msg, (Object[])null); } public void finest(String msg, Throwable t) { loggerProxy.log(Level.FINEST, msg, t); } public void finest(String msg, Object... params) { loggerProxy.log(Level.FINEST, msg, params); } // ------------------------------------ // Maps used for Level conversion // ------------------------------------ // This map is indexed by java.util.spi.Logger.Level.ordinal() and returns // a PlatformLogger.Level // // ALL, TRACE, DEBUG, INFO, WARNING, ERROR, OFF private static final Level[] spi2platformLevelMapping = { Level.ALL, // mapped from ALL Level.FINER, // mapped from TRACE Level.FINE, // mapped from DEBUG Level.INFO, // mapped from INFO Level.WARNING, // mapped from WARNING Level.SEVERE, // mapped from ERROR Level.OFF // mapped from OFF }; public static Level toPlatformLevel(java.lang.System.Logger.Level level) { if (level == null) return null; assert level.ordinal() < spi2platformLevelMapping.length; return spi2platformLevelMapping[level.ordinal()]; } }