提交 e0a8e955 编写于 作者: M mchung

6664509: Add logging context

6664528: Find log level matching its name or value given at construction time
Reviewed-by: alanb, ahgross, jgish, hawtin
上级 7bd29c94
......@@ -24,6 +24,10 @@
*/
package java.util.logging;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
/**
......@@ -59,7 +63,6 @@ import java.util.ResourceBundle;
*/
public class Level implements java.io.Serializable {
private static java.util.ArrayList<Level> known = new java.util.ArrayList<>();
private static String defaultBundle = "sun.util.logging.resources.logging";
/**
......@@ -77,6 +80,9 @@ public class Level implements java.io.Serializable {
*/
private final String resourceBundleName;
// localized level name
private String localizedLevelName;
/**
* OFF is a special level that can be used to turn off logging.
* This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.
......@@ -202,9 +208,8 @@ public class Level implements java.io.Serializable {
this.name = name;
this.value = value;
this.resourceBundleName = resourceBundleName;
synchronized (Level.class) {
known.add(this);
}
this.localizedLevelName = resourceBundleName == null ? name : null;
KnownLevel.add(this);
}
/**
......@@ -236,12 +241,76 @@ public class Level implements java.io.Serializable {
* @return localized name
*/
public String getLocalizedName() {
return getLocalizedLevelName();
}
// package-private getLevelName() is used by the implementation
// instead of getName() to avoid calling the subclass's version
final String getLevelName() {
return this.name;
}
final synchronized String getLocalizedLevelName() {
if (localizedLevelName != null) {
return localizedLevelName;
}
try {
ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName);
return rb.getString(name);
localizedLevelName = rb.getString(name);
} catch (Exception ex) {
return name;
localizedLevelName = name;
}
return localizedLevelName;
}
// Returns a mirrored Level object that matches the given name as
// specified in the Level.parse method. Returns null if not found.
//
// It returns the same Level object as the one returned by Level.parse
// method if the given name is a non-localized name or integer.
//
// If the name is a localized name, findLevel and parse method may
// return a different level value if there is a custom Level subclass
// that overrides Level.getLocalizedName() to return a different string
// than what's returned by the default implementation.
//
static Level findLevel(String name) {
if (name == null) {
throw new NullPointerException();
}
KnownLevel level;
// Look for a known Level with the given non-localized name.
level = KnownLevel.findByName(name);
if (level != null) {
return level.mirroredLevel;
}
// Now, check if the given name is an integer. If so,
// first look for a Level with the given value and then
// if necessary create one.
try {
int x = Integer.parseInt(name);
level = KnownLevel.findByValue(x);
if (level == null) {
// add new Level
Level levelObject = new Level(name, x);
level = KnownLevel.findByValue(x);
}
return level.mirroredLevel;
} catch (NumberFormatException ex) {
// Not an integer.
// Drop through.
}
level = KnownLevel.findByLocalizedLevelName(name);
if (level != null) {
return level.mirroredLevel;
}
return null;
}
/**
......@@ -268,21 +337,15 @@ public class Level implements java.io.Serializable {
// Serialization magic to prevent "doppelgangers".
// This is a performance optimization.
private Object readResolve() {
synchronized (Level.class) {
for (int i = 0; i < known.size(); i++) {
Level other = known.get(i);
if (this.name.equals(other.name) && this.value == other.value
&& (this.resourceBundleName == other.resourceBundleName ||
(this.resourceBundleName != null &&
this.resourceBundleName.equals(other.resourceBundleName)))) {
return other;
}
}
// Woops. Whoever sent us this object knows
// about a new log level. Add it to our list.
known.add(this);
return this;
KnownLevel o = KnownLevel.matches(this);
if (o != null) {
return o.levelObject;
}
// Woops. Whoever sent us this object knows
// about a new log level. Add it to our list.
Level level = new Level(this.name, this.value, this.resourceBundleName);
return level;
}
/**
......@@ -296,6 +359,7 @@ public class Level implements java.io.Serializable {
* <li> "SEVERE"
* <li> "1000"
* </ul>
*
* @param name string to be parsed
* @throws NullPointerException if the name is null
* @throws IllegalArgumentException if the value is not valid.
......@@ -315,12 +379,12 @@ public class Level implements java.io.Serializable {
// Check that name is not null.
name.length();
KnownLevel level;
// Look for a known Level with the given non-localized name.
for (int i = 0; i < known.size(); i++) {
Level l = known.get(i);
if (name.equals(l.name)) {
return l;
}
level = KnownLevel.findByName(name);
if (level != null) {
return level.levelObject;
}
// Now, check if the given name is an integer. If so,
......@@ -328,14 +392,13 @@ public class Level implements java.io.Serializable {
// if necessary create one.
try {
int x = Integer.parseInt(name);
for (int i = 0; i < known.size(); i++) {
Level l = known.get(i);
if (l.value == x) {
return l;
}
level = KnownLevel.findByValue(x);
if (level == null) {
// add new Level
Level levelObject = new Level(name, x);
level = KnownLevel.findByValue(x);
}
// Create a new Level.
return new Level(name, x);
return level.levelObject;
} catch (NumberFormatException ex) {
// Not an integer.
// Drop through.
......@@ -344,11 +407,9 @@ public class Level implements java.io.Serializable {
// Finally, look for a known level with the given localized name,
// in the current default locale.
// This is relatively expensive, but not excessively so.
for (int i = 0; i < known.size(); i++) {
Level l = known.get(i);
if (name.equals(l.getLocalizedName())) {
return l;
}
level = KnownLevel.findByLocalizedName(name);
if (level != null) {
return level.levelObject;
}
// OK, we've tried everything and failed
......@@ -375,4 +436,124 @@ public class Level implements java.io.Serializable {
public int hashCode() {
return this.value;
}
// KnownLevel class maintains the global list of all known levels.
// The API allows multiple custom Level instances of the same name/value
// be created. This class provides convenient methods to find a level
// by a given name, by a given value, or by a given localized name.
//
// KnownLevel wraps the following Level objects:
// 1. levelObject: standard Level object or custom Level object
// 2. mirroredLevel: Level object representing the level specified in the
// logging configuration.
//
// Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
// are non-final but the name and resource bundle name are parameters to
// the Level constructor. Use the mirroredLevel object instead of the
// levelObject to prevent the logging framework to execute foreign code
// implemented by untrusted Level subclass.
//
// Implementation Notes:
// If Level.getName, Level.getLocalizedName, Level.getResourceBundleName methods
// were final, the following KnownLevel implementation can be removed.
// Future API change should take this into consideration.
static final class KnownLevel {
private static Map<String, List<KnownLevel>> nameToLevels = new HashMap<>();
private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();
final Level levelObject; // instance of Level class or Level subclass
final Level mirroredLevel; // instance of Level class
KnownLevel(Level l) {
this.levelObject = l;
if (l.getClass() == Level.class) {
this.mirroredLevel = l;
} else {
this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName);
}
}
static synchronized void add(Level l) {
// the mirroredLevel object is always added to the list
// before the custom Level instance
KnownLevel o = new KnownLevel(l);
List<KnownLevel> list = nameToLevels.get(l.name);
if (list == null) {
list = new ArrayList<>();
nameToLevels.put(l.name, list);
}
list.add(o);
list = intToLevels.get(l.value);
if (list == null) {
list = new ArrayList<>();
intToLevels.put(l.value, list);
}
list.add(o);
}
// Returns a KnownLevel with the given non-localized name.
static synchronized KnownLevel findByName(String name) {
List<KnownLevel> list = nameToLevels.get(name);
if (list != null) {
return list.get(0);
}
return null;
}
// Returns a KnownLevel with the given value.
static synchronized KnownLevel findByValue(int value) {
List<KnownLevel> list = intToLevels.get(value);
if (list != null) {
return list.get(0);
}
return null;
}
// Returns a KnownLevel with the given localized name matching
// by calling the Level.getLocalizedLevelName() method (i.e. found
// from the resourceBundle associated with the Level object).
// This method does not call Level.getLocalizedName() that may
// be overridden in a subclass implementation
static synchronized KnownLevel findByLocalizedLevelName(String name) {
for (List<KnownLevel> levels : nameToLevels.values()) {
for (KnownLevel l : levels) {
String lname = l.levelObject.getLocalizedLevelName();
if (name.equals(lname)) {
return l;
}
}
}
return null;
}
// Returns a KnownLevel with the given localized name matching
// by calling the Level.getLocalizedName() method
static synchronized KnownLevel findByLocalizedName(String name) {
for (List<KnownLevel> levels : nameToLevels.values()) {
for (KnownLevel l : levels) {
String lname = l.levelObject.getLocalizedName();
if (name.equals(lname)) {
return l;
}
}
}
return null;
}
static synchronized KnownLevel matches(Level l) {
List<KnownLevel> list = nameToLevels.get(l.name);
if (list != null) {
for (KnownLevel level : list) {
Level other = level.mirroredLevel;
if (l.value == other.value &&
(l.resourceBundleName == other.resourceBundleName ||
(l.resourceBundleName != null &&
l.resourceBundleName.equals(other.resourceBundleName)))) {
return level;
}
}
}
return null;
}
}
}
......@@ -30,6 +30,7 @@ import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.security.*;
import java.lang.ref.WeakReference;
import java.util.logging.LogManager.LoggerContext;
/**
* A Logger object is used to log messages for a specific
......@@ -286,6 +287,26 @@ public class Logger {
}
}
// Until all JDK code converted to call sun.util.logging.PlatformLogger
// (see 7054233), we need to determine if Logger.getLogger is to add
// a system logger or user logger.
//
// As an interim solution, if the immediate caller whose caller loader is
// null, we assume it's a system logger and add it to the system context.
private static LoggerContext getLoggerContext() {
LogManager manager = LogManager.getLogManager();
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 0: Reflection 1: Logger.getLoggerContext 2: Logger.getLogger 3: caller
final int SKIP_FRAMES = 3;
Class<?> caller = sun.reflect.Reflection.getCallerClass(SKIP_FRAMES);
if (caller.getClassLoader() == null) {
return manager.getSystemContext();
}
}
return manager.getUserContext();
}
/**
* Find or create a logger for a named subsystem. If a logger has
* already been created with the given name it is returned. Otherwise
......@@ -327,8 +348,8 @@ public class Logger {
// would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null.
LogManager manager = LogManager.getLogManager();
return manager.demandLogger(name);
LoggerContext context = getLoggerContext();
return context.demandLogger(name);
}
/**
......@@ -375,8 +396,8 @@ public class Logger {
// Synchronization is not required here. All synchronization for
// adding a new Logger object is handled by LogManager.addLogger().
public static Logger getLogger(String name, String resourceBundleName) {
LogManager manager = LogManager.getLogManager();
Logger result = manager.demandLogger(name);
LoggerContext context = getLoggerContext();
Logger result = context.demandLogger(name, resourceBundleName);
// MissingResourceException or IllegalArgumentException can be
// thrown by setupResourceInfo().
......@@ -384,6 +405,18 @@ public class Logger {
return result;
}
// package-private
// Add a platform logger to the system context.
// i.e. caller of sun.util.logging.PlatformLogger.getLogger
static Logger getPlatformLogger(String name) {
LogManager manager = LogManager.getLogManager();
LoggerContext context = manager.getSystemContext();
// all loggers in the system context will default to
// the system logger's resource bundle
Logger result = context.demandLogger(name);
return result;
}
/**
* Create an anonymous Logger. The newly created Logger is not
......@@ -536,7 +569,7 @@ public class Logger {
private void doLog(LogRecord lr) {
lr.setLoggerName(name);
String ebname = getEffectiveResourceBundleName();
if (ebname != null) {
if (ebname != null && !ebname.equals(SYSTEM_LOGGER_RB_NAME)) {
lr.setResourceBundleName(ebname);
lr.setResourceBundle(findResourceBundle(ebname));
}
......@@ -1285,6 +1318,22 @@ public class Logger {
// May also return null if we can't find the resource bundle and
// there is no suitable previous cached value.
static final String SYSTEM_LOGGER_RB_NAME = "sun.util.logging.resources.logging";
private static ResourceBundle findSystemResourceBundle(final Locale locale) {
// the resource bundle is in a restricted package
return AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
public ResourceBundle run() {
try {
return ResourceBundle.getBundle(SYSTEM_LOGGER_RB_NAME,
locale);
} catch (MissingResourceException e) {
throw new InternalError(e.toString());
}
}
});
}
private synchronized ResourceBundle findResourceBundle(String name) {
// Return a null bundle for a null name.
if (name == null) {
......@@ -1299,6 +1348,13 @@ public class Logger {
return catalog;
}
if (name.equals(SYSTEM_LOGGER_RB_NAME)) {
catalog = findSystemResourceBundle(currentLocale);
catalogName = name;
catalogLocale = currentLocale;
return catalog;
}
// Use the thread's context ClassLoader. If there isn't one,
// use the SystemClassloader.
ClassLoader cl = Thread.currentThread().getContextClassLoader();
......@@ -1315,7 +1371,6 @@ public class Logger {
// ClassLoader. Drop through.
}
// Fall back to searching up the call stack and trying each
// calling ClassLoader.
for (int ix = 0; ; ix++) {
......
......@@ -34,7 +34,7 @@ import java.util.ArrayList;
*
* The <tt>LoggingMXBean</tt> interface provides a standard
* method for management access to the individual
* java.util.Logger objects available at runtime.
* {@code Logger} objects available at runtime.
*
* @author Ron Mann
* @author Mandy Chung
......@@ -75,7 +75,7 @@ class Logging implements LoggingMXBean {
if (level == null) {
return EMPTY_STRING;
} else {
return level.getName();
return level.getLevelName();
}
}
......@@ -85,7 +85,6 @@ class Logging implements LoggingMXBean {
}
Logger logger = logManager.getLogger(loggerName);
if (logger == null) {
throw new IllegalArgumentException("Logger " + loggerName +
"does not exist");
......@@ -94,7 +93,10 @@ class Logging implements LoggingMXBean {
Level level = null;
if (levelName != null) {
// parse will throw IAE if logLevel is invalid
level = Level.parse(levelName);
level = Level.findLevel(levelName);
if (level == null) {
throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
}
}
logger.setLevel(level);
......
......@@ -37,7 +37,8 @@ class LoggingProxyImpl implements LoggingProxy {
@Override
public Object getLogger(String name) {
return Logger.getLogger(name);
// always create a platform logger with the resource bundle name
return Logger.getPlatformLogger(name);
}
@Override
......@@ -92,12 +93,16 @@ class LoggingProxyImpl implements LoggingProxy {
@Override
public Object parseLevel(String levelName) {
return Level.parse(levelName);
Level level = Level.findLevel(levelName);
if (level == null) {
throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
}
return level;
}
@Override
public String getLevelName(Object level) {
return ((Level) level).getName();
return ((Level) level).getLevelName();
}
@Override
......
......@@ -162,7 +162,7 @@ public class SimpleFormatter extends Formatter {
dat,
source,
record.getLoggerName(),
record.getLevel().getLocalizedName(),
record.getLevel().getLocalizedLevelName(),
message,
throwable);
}
......
......@@ -327,21 +327,27 @@ public final class AppContext {
// Before we return the main "system" AppContext, check to
// see if there's an AWTSecurityManager installed. If so,
// allow it to choose the AppContext to return.
SecurityManager securityManager = System.getSecurityManager();
if ((securityManager != null) &&
(securityManager instanceof AWTSecurityManager))
{
AWTSecurityManager awtSecMgr = (AWTSecurityManager)securityManager;
AppContext secAppContext = awtSecMgr.getAppContext();
if (secAppContext != null) {
appContext = secAppContext; // Return what we're told
}
AppContext secAppContext = getExecutionAppContext();
if (secAppContext != null) {
appContext = secAppContext; // Return what we're told
}
}
return appContext;
}
private final static AppContext getExecutionAppContext() {
SecurityManager securityManager = System.getSecurityManager();
if ((securityManager != null) &&
(securityManager instanceof AWTSecurityManager))
{
AWTSecurityManager awtSecMgr = (AWTSecurityManager) securityManager;
AppContext secAppContext = awtSecMgr.getAppContext();
return secAppContext; // Return what we're told
}
return null;
}
/**
* Returns the main ("system") AppContext.
*
......@@ -806,6 +812,21 @@ public final class AppContext {
public boolean isMainAppContext() {
return (numAppContexts.get() == 1);
}
public Object getContext() {
return getAppContext();
}
public Object getExecutionContext() {
return getExecutionAppContext();
}
public Object get(Object context, Object key) {
return ((AppContext)context).get(key);
}
public void put(Object context, Object key, Object value) {
((AppContext)context).put(key, value);
}
public void remove(Object context, Object key) {
((AppContext)context).remove(key);
}
});
}
}
......
......@@ -26,6 +26,14 @@
package sun.misc;
public interface JavaAWTAccess {
public Object getContext();
public Object getExecutionContext();
public Object get(Object context, Object key);
public void put(Object context, Object key, Object value);
public void remove(Object context, Object key);
// convenience methods whose context is the object returned by getContext()
public Object get(Object key);
public void put(Object key, Object value);
public void remove(Object key);
......
......@@ -148,6 +148,9 @@ keystore.type=jks
package.access=sun.,\
com.sun.xml.internal.,\
com.sun.imageio.,\
com.sun.istack.internal.,\
com.sun.jmx.default.,\
com.sun.jmx.remote.util.,\
com.sun.org.apache.xerces.internal.utils.,\
com.sun.org.apache.xalan.internal.utils.,\
com.sun.org.glassfish.external.,\
......@@ -166,6 +169,9 @@ package.access=sun.,\
package.definition=sun.,\
com.sun.xml.internal.,\
com.sun.imageio.,\
com.sun.istack.internal.,\
com.sun.jmx.default.,\
com.sun.jmx.remote.util.,\
com.sun.org.apache.xerces.internal.utils.,\
com.sun.org.apache.xalan.internal.utils.,\
com.sun.org.glassfish.external.,\
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册