blob: 432228a88d04a7182aef92ed70ba1e10e8f4555c [file] [log] [blame]
/*
* 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 {@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
* {@code RuntimePermission("loggerFinder")}.
*/
public LoggingProviderImpl() {
}
/**
* 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.
* @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}
*
* @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;
}
}