/*
* Copyright (c) 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.internal;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ResourceBundle;
import java.util.function.Supplier;
import java.lang.System.LoggerFinder;
import java.lang.System.Logger;
import java.util.Objects;
import java.util.logging.LogManager;
import jdk.internal.logger.DefaultLoggerFinder;
import java.util.logging.LoggingPermission;
import sun.util.logging.PlatformLogger;
import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
This LoggingProviderImpl
is the JDK internal implementation of the DefaultLoggerFinder
which is used by the default implementation of the Logger
when no LoggerFinder
is found and java.util.logging
is present. When java.util.logging
is present, the LoggingProviderImpl
is installed as an internal service provider, making it possible to use java.util.logging
as the backend for loggers returned by the default LoggerFinder implementation. This implementation of DefaultLoggerFinder
returns instances of Logger
which delegate to a wrapped instance of
java.util.logging.Logger
.
Loggers returned by this class can therefore be configured by accessing their wrapped implementation through the regular java.util.logging
APIs - such as java.util.logging.LogManager
and java.util.logging.Logger
.
See Also: API Note: Programmers are not expected to call this class directly. Instead they should rely on the static methods defined by java.lang.System
. To replace this default java.util.logging
backend, an application is expected to install its own LoggerFinder
.
/**
* This {@code LoggingProviderImpl} is the JDK internal implementation of the
* {@link jdk.internal.logger.DefaultLoggerFinder} which is used by
* the default implementation of the {@link Logger}
* when no {@link LoggerFinder} is found
* and {@code java.util.logging} is present.
* When {@code java.util.logging} is present, the {@code LoggingProviderImpl}
* is {@linkplain java.util.ServiceLoader#loadInstalled(Class) installed} as
* an internal service provider, making it possible to use {@code java.util.logging}
* as the backend for loggers returned by the default LoggerFinder implementation.
* <p>
* This implementation of {@link DefaultLoggerFinder} returns instances of
* {@link java.lang.System.Logger} which
* delegate to a wrapped instance of {@link java.util.logging.Logger
* java.util.logging.Logger}.
* <br>
* Loggers returned by this class can therefore be configured by accessing
* their wrapped implementation through the regular {@code java.util.logging}
* APIs - such as {@link java.util.logging.LogManager java.util.logging.LogManager}
* and {@link java.util.logging.Logger java.util.logging.Logger}.
*
* @apiNote Programmers are not expected to call this class directly.
* Instead they should rely on the static methods defined by
* {@link java.lang.System java.lang.System}.
* <p>
* To replace this default
* {@code java.util.logging} backend, an application is expected to install
* its own {@link java.lang.System.LoggerFinder}.
*
* @see java.lang.System.Logger
* @see java.lang.System.LoggerFinder
* @see sun.util.logging.PlatformLogger.Bridge
* @see java.lang.System
* @see jdk.internal.logger
* @see jdk.internal.logger
*
*/
public final class LoggingProviderImpl extends DefaultLoggerFinder {
static final RuntimePermission LOGGERFINDER_PERMISSION =
new RuntimePermission("loggerFinder");
private static final LoggingPermission LOGGING_CONTROL_PERMISSION =
new LoggingPermission("control", null);
Creates a new instance of LoggingProviderImpl.
Throws: - SecurityException – if the calling code does not have the
RuntimePermission("loggerFinder")
.
/**
* Creates a new instance of LoggingProviderImpl.
* @throws SecurityException if the calling code does not have the
* {@code RuntimePermission("loggerFinder")}.
*/
public LoggingProviderImpl() {
}
A logger that delegates to a java.util.logging.Logger delegate.
/**
* A logger that delegates to a java.util.logging.Logger delegate.
*/
static final class JULWrapper extends LoggerConfiguration
implements System.Logger, PlatformLogger.Bridge,
PlatformLogger.ConfigurableBridge {
private static final java.util.logging.Level[] spi2JulLevelMapping = {
java.util.logging.Level.ALL, // mapped from ALL
java.util.logging.Level.FINER, // mapped from TRACE
java.util.logging.Level.FINE, // mapped from DEBUG
java.util.logging.Level.INFO, // mapped from INFO
java.util.logging.Level.WARNING, // mapped from WARNING
java.util.logging.Level.SEVERE, // mapped from ERROR
java.util.logging.Level.OFF // mapped from OFF
};
private static final java.util.logging.Level[] platform2JulLevelMapping = {
java.util.logging.Level.ALL, // mapped from ALL
java.util.logging.Level.FINEST, // mapped from FINEST
java.util.logging.Level.FINER, // mapped from FINER
java.util.logging.Level.FINE, // mapped from FINE
java.util.logging.Level.CONFIG, // mapped from CONFIG
java.util.logging.Level.INFO, // mapped from INFO
java.util.logging.Level.WARNING, // mapped from WARNING
java.util.logging.Level.SEVERE, // mapped from SEVERE
java.util.logging.Level.OFF // mapped from OFF
};
private final java.util.logging.Logger julLogger;
private JULWrapper(java.util.logging.Logger logger) {
this.julLogger = logger;
}
@Override
public String getName() {
return julLogger.getName();
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable throwable) {
julLogger.log(toJUL(level), msg, throwable);
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) {
julLogger.log(toJUL(level), format, params);
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
julLogger.log(toJUL(level), msg);
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, Supplier<String> msgSuppier) {
julLogger.log(toJUL(level), msgSuppier);
}
@Override
public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, Supplier<String> msgSuppier) {
julLogger.log(toJUL(level), thrown, msgSuppier);
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Throwable throwable) {
julLogger.logrb(toJUL(level), bundle, key, throwable);
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Object... params) {
julLogger.logrb(toJUL(level), bundle, key, params);
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg) {
julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg);
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
Supplier<String> msgSupplier) {
julLogger.logp(toJUL(level), sourceClass, sourceMethod, msgSupplier);
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
String msg, Object... params) {
julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, params);
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
String msg, Throwable thrown) {
julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, thrown);
}
@Override
public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
Throwable thrown, Supplier<String> msgSupplier) {
julLogger.logp(toJUL(level), sourceClass, sourceMethod,
thrown, msgSupplier);
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
ResourceBundle bundle, String key, Object... params) {
julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
bundle, key, params);
}
@Override
public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
ResourceBundle bundle, String key, Throwable thrown) {
julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
bundle, key, thrown);
}
@Override
public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
return julLogger.isLoggable(toJUL(level));
}
// -----------------------------------------------------------------
// Generic methods taking a Level as parameter
// -----------------------------------------------------------------
@Override
public boolean isLoggable(Level level) {
return julLogger.isLoggable(toJUL(level));
}
@Override
public void log(Level level, String msg) {
julLogger.log(toJUL(level), msg);
}
@Override
public void log(Level level,
Supplier<String> msgSupplier) {
// We need to check for null here to satisfy the contract
// of System.Logger - because the underlying implementation
// of julLogger will check for it only if the level is
// loggable
Objects.requireNonNull(msgSupplier);
julLogger.log(toJUL(level), msgSupplier);
}
@Override
public void log(Level level, Object obj) {
// We need to check for null here to satisfy the contract
// of System.Logger - because the underlying implementation
// of julLogger will check for it only if the level is
// loggable
Objects.requireNonNull(obj);
julLogger.log(toJUL(level), () -> obj.toString());
}
@Override
public void log(Level level,
String msg, Throwable thrown) {
julLogger.log(toJUL(level), msg, thrown);
}
@Override
public void log(Level level, Supplier<String> msgSupplier,
Throwable thrown) {
// We need to check for null here to satisfy the contract
// of System.Logger - because the underlying implementation
// of julLogger will check for it only if the level is
// loggable
Objects.requireNonNull(msgSupplier);
julLogger.log(toJUL(level), thrown, msgSupplier);
}
@Override
public void log(Level level,
String format, Object... params) {
julLogger.log(toJUL(level), format, params);
}
@Override
public void log(Level level, ResourceBundle bundle,
String key, Throwable thrown) {
julLogger.logrb(toJUL(level), bundle, key, thrown);
}
@Override
public void log(Level level, ResourceBundle bundle,
String format, Object... params) {
julLogger.logrb(toJUL(level), bundle, format, params);
}
static java.util.logging.Level toJUL(Level level) {
if (level == null) return null;
assert level.ordinal() < spi2JulLevelMapping.length;
return spi2JulLevelMapping[level.ordinal()];
}
// ---------------------------------------------------------
// Methods from PlatformLogger.Bridge
// ---------------------------------------------------------
@Override
public boolean isEnabled() {
return julLogger.getLevel() != java.util.logging.Level.OFF;
}
@Override
public PlatformLogger.Level getPlatformLevel() {
final java.util.logging.Level javaLevel = julLogger.getLevel();
if (javaLevel == null) return null;
try {
return PlatformLogger.Level.valueOf(javaLevel.getName());
} catch (IllegalArgumentException e) {
return PlatformLogger.Level.valueOf(javaLevel.intValue());
}
}
@Override
public void setPlatformLevel(PlatformLogger.Level level) {
// null is allowed here
julLogger.setLevel(toJUL(level));
}
@Override
public LoggerConfiguration getLoggerConfiguration() {
return this;
}
static java.util.logging.Level toJUL(PlatformLogger.Level level) {
// The caller will throw if null is invalid in its context.
// There's at least one case where a null level is valid.
if (level == null) return null;
assert level.ordinal() < platform2JulLevelMapping.length;
return platform2JulLevelMapping[level.ordinal()];
}
@Override
public boolean equals(Object obj) {
return (obj instanceof JULWrapper)
&& obj.getClass() == this.getClass()
&& ((JULWrapper)obj).julLogger == this.julLogger;
}
@Override
public int hashCode() {
return julLogger.hashCode();
}
// A JULWrapper is just a stateless thin shell over a JUL logger - so
// for a given JUL logger, we could always return the same wrapper.
//
// This is an optimization which may - or may not - be worth the
// trouble: if many classes use the same logger, and if each class
// keeps a reference to that logger, then caching the wrapper will
// be worthwhile. Otherwise, if each logger is only referred once,
// then the cache will eat up more memory than would be necessary...
//
// Here is an example of how we could implement JULWrapper.of(...)
// if we wanted to create at most one wrapper instance for each logger
// instance:
//
// static final WeakHashMap<JULWrapper, WeakReference<JULWrapper>>
// wrappers = new WeakHashMap<>();
//
// static JULWrapper of(java.util.logging.Logger logger) {
//
// // First access without synchronizing
// final JULWrapper candidate = new JULWrapper(logger);
// WeakReference<JULWrapper> ref = wrappers.get(candidate);
// JULWrapper found = ref.get();
//
// // OK - we found it - lets return it.
// if (found != null) return found;
//
// // Not found. Need to synchronize.
// synchronized (wrappers) {
// ref = wrappers.get(candidate);
// found = ref.get();
// if (found == null) {
// wrappers.put(candidate, new WeakReference<>(candidate));
// found = candidate;
// }
// }
// assert found != null;
// return found;
// }
//
// But given that it may end up eating more memory in the nominal case
// (where each class that does logging has its own logger with the
// class name as logger name and stashes that logger away in a static
// field, thus making the cache redundant - as only one wrapper will
// ever be created anyway) - then we will simply return a new wrapper
// for each invocation of JULWrapper.of(...) - which may
// still prove more efficient in terms of memory consumption...
//
static JULWrapper of(java.util.logging.Logger logger) {
return new JULWrapper(logger);
}
}
Creates a java.util.logging.Logger for the given module.
Params: - name – the logger name.
- module – the module for which the logger should be created.
Returns: a Logger suitable for use in the given module.
/**
* Creates a java.util.logging.Logger for the given module.
* @param name the logger name.
* @param module the module for which the logger should be created.
* @return a Logger suitable for use in the given module.
*/
private static java.util.logging.Logger demandJULLoggerFor(final String name,
Module module) {
final LogManager manager = LogManager.getLogManager();
final SecurityManager sm = System.getSecurityManager();
if (sm == null) {
return logManagerAccess.demandLoggerFor(manager, name, module);
} else {
final PrivilegedAction<java.util.logging.Logger> pa =
() -> logManagerAccess.demandLoggerFor(manager, name, module);
return AccessController.doPrivileged(pa, null, LOGGING_CONTROL_PERMISSION);
}
}
{@inheritDoc}
Throws: - SecurityException – if the calling code doesn't have the
RuntimePermission("loggerFinder")
.
API Note: The logger returned by this method can be configured through its
corresponding java.util.logging.Logger backend. Returns: {@inheritDoc}
/**
* {@inheritDoc}
*
* @apiNote The logger returned by this method can be configured through
* its {@linkplain java.util.logging.LogManager#getLogger(String)
* corresponding java.util.logging.Logger backend}.
*
* @return {@inheritDoc}
* @throws SecurityException if the calling code doesn't have the
* {@code RuntimePermission("loggerFinder")}.
*/
@Override
protected Logger demandLoggerFor(String name, Module module) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGERFINDER_PERMISSION);
}
return JULWrapper.of(demandJULLoggerFor(name,module));
}
public static interface LogManagerAccess {
java.util.logging.Logger demandLoggerFor(LogManager manager,
String name, Module module);
}
// Hook for tests
public static LogManagerAccess getLogManagerAccess() {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGING_CONTROL_PERMISSION);
}
// Triggers initialization of accessJulLogger if not set.
if (logManagerAccess == null) LogManager.getLogManager();
return logManagerAccess;
}
private static volatile LogManagerAccess logManagerAccess;
public static void setLogManagerAccess(LogManagerAccess accesLoggers) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(LOGGING_CONTROL_PERMISSION);
}
logManagerAccess = accesLoggers;
}
}