blob: 07c6878c15115aac69a32468757e9e4b64c7365b [file] [log] [blame]
/*
* 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 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()];
}
}