提交 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 @@ ...@@ -24,6 +24,10 @@
*/ */
package java.util.logging; package java.util.logging;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
/** /**
...@@ -59,7 +63,6 @@ import java.util.ResourceBundle; ...@@ -59,7 +63,6 @@ import java.util.ResourceBundle;
*/ */
public class Level implements java.io.Serializable { 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"; private static String defaultBundle = "sun.util.logging.resources.logging";
/** /**
...@@ -77,6 +80,9 @@ public class Level implements java.io.Serializable { ...@@ -77,6 +80,9 @@ public class Level implements java.io.Serializable {
*/ */
private final String resourceBundleName; private final String resourceBundleName;
// localized level name
private String localizedLevelName;
/** /**
* OFF is a special level that can be used to turn off logging. * OFF is a special level that can be used to turn off logging.
* This level is initialized to <CODE>Integer.MAX_VALUE</CODE>. * This level is initialized to <CODE>Integer.MAX_VALUE</CODE>.
...@@ -202,9 +208,8 @@ public class Level implements java.io.Serializable { ...@@ -202,9 +208,8 @@ public class Level implements java.io.Serializable {
this.name = name; this.name = name;
this.value = value; this.value = value;
this.resourceBundleName = resourceBundleName; this.resourceBundleName = resourceBundleName;
synchronized (Level.class) { this.localizedLevelName = resourceBundleName == null ? name : null;
known.add(this); KnownLevel.add(this);
}
} }
/** /**
...@@ -236,12 +241,76 @@ public class Level implements java.io.Serializable { ...@@ -236,12 +241,76 @@ public class Level implements java.io.Serializable {
* @return localized name * @return localized name
*/ */
public String getLocalizedName() { 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 { try {
ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName); ResourceBundle rb = ResourceBundle.getBundle(resourceBundleName);
return rb.getString(name); localizedLevelName = rb.getString(name);
} catch (Exception ex) { } 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 { ...@@ -268,21 +337,15 @@ public class Level implements java.io.Serializable {
// Serialization magic to prevent "doppelgangers". // Serialization magic to prevent "doppelgangers".
// This is a performance optimization. // This is a performance optimization.
private Object readResolve() { private Object readResolve() {
synchronized (Level.class) { KnownLevel o = KnownLevel.matches(this);
for (int i = 0; i < known.size(); i++) { if (o != null) {
Level other = known.get(i); return o.levelObject;
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;
} }
// 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 { ...@@ -296,6 +359,7 @@ public class Level implements java.io.Serializable {
* <li> "SEVERE" * <li> "SEVERE"
* <li> "1000" * <li> "1000"
* </ul> * </ul>
*
* @param name string to be parsed * @param name string to be parsed
* @throws NullPointerException if the name is null * @throws NullPointerException if the name is null
* @throws IllegalArgumentException if the value is not valid. * @throws IllegalArgumentException if the value is not valid.
...@@ -315,12 +379,12 @@ public class Level implements java.io.Serializable { ...@@ -315,12 +379,12 @@ public class Level implements java.io.Serializable {
// Check that name is not null. // Check that name is not null.
name.length(); name.length();
KnownLevel level;
// Look for a known Level with the given non-localized name. // Look for a known Level with the given non-localized name.
for (int i = 0; i < known.size(); i++) { level = KnownLevel.findByName(name);
Level l = known.get(i); if (level != null) {
if (name.equals(l.name)) { return level.levelObject;
return l;
}
} }
// Now, check if the given name is an integer. If so, // Now, check if the given name is an integer. If so,
...@@ -328,14 +392,13 @@ public class Level implements java.io.Serializable { ...@@ -328,14 +392,13 @@ public class Level implements java.io.Serializable {
// if necessary create one. // if necessary create one.
try { try {
int x = Integer.parseInt(name); int x = Integer.parseInt(name);
for (int i = 0; i < known.size(); i++) { level = KnownLevel.findByValue(x);
Level l = known.get(i); if (level == null) {
if (l.value == x) { // add new Level
return l; Level levelObject = new Level(name, x);
} level = KnownLevel.findByValue(x);
} }
// Create a new Level. return level.levelObject;
return new Level(name, x);
} catch (NumberFormatException ex) { } catch (NumberFormatException ex) {
// Not an integer. // Not an integer.
// Drop through. // Drop through.
...@@ -344,11 +407,9 @@ public class Level implements java.io.Serializable { ...@@ -344,11 +407,9 @@ public class Level implements java.io.Serializable {
// Finally, look for a known level with the given localized name, // Finally, look for a known level with the given localized name,
// in the current default locale. // in the current default locale.
// This is relatively expensive, but not excessively so. // This is relatively expensive, but not excessively so.
for (int i = 0; i < known.size(); i++) { level = KnownLevel.findByLocalizedName(name);
Level l = known.get(i); if (level != null) {
if (name.equals(l.getLocalizedName())) { return level.levelObject;
return l;
}
} }
// OK, we've tried everything and failed // OK, we've tried everything and failed
...@@ -375,4 +436,124 @@ public class Level implements java.io.Serializable { ...@@ -375,4 +436,124 @@ public class Level implements java.io.Serializable {
public int hashCode() { public int hashCode() {
return this.value; 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;
}
}
} }
...@@ -34,6 +34,8 @@ import java.lang.ref.WeakReference; ...@@ -34,6 +34,8 @@ import java.lang.ref.WeakReference;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.net.URL; import java.net.URL;
import sun.misc.JavaAWTAccess;
import sun.misc.SharedSecrets;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
/** /**
...@@ -157,10 +159,9 @@ public class LogManager { ...@@ -157,10 +159,9 @@ public class LogManager {
// count to allow for cases where the same listener is registered many times. // count to allow for cases where the same listener is registered many times.
private final Map<PropertyChangeListener,Integer> listenerMap = new HashMap<>(); private final Map<PropertyChangeListener,Integer> listenerMap = new HashMap<>();
// Table of named Loggers that maps names to Loggers. // LoggerContext for system loggers and user loggers
private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>(); private final LoggerContext systemContext = new SystemLoggerContext();
// Tree of named Loggers private final LoggerContext userContext = new UserLoggerContext();
private LogNode root = new LogNode(null);
private Logger rootLogger; private Logger rootLogger;
// Have we done the primordial reading of the configuration file? // Have we done the primordial reading of the configuration file?
...@@ -198,12 +199,13 @@ public class LogManager { ...@@ -198,12 +199,13 @@ public class LogManager {
// Create and retain Logger for the root of the namespace. // Create and retain Logger for the root of the namespace.
manager.rootLogger = manager.new RootLogger(); manager.rootLogger = manager.new RootLogger();
manager.addLogger(manager.rootLogger); manager.systemContext.addLogger(manager.rootLogger);
manager.userContext.addLogger(manager.rootLogger);
// Adding the global Logger. Doing so in the Logger.<clinit> // Adding the global Logger. Doing so in the Logger.<clinit>
// would deadlock with the LogManager.<clinit>. // would deadlock with the LogManager.<clinit>.
Logger.getGlobal().setLogManager(manager); Logger.getGlobal().setLogManager(manager);
manager.addLogger(Logger.getGlobal()); manager.systemContext.addLogger(Logger.getGlobal());
// We don't call readConfiguration() here, as we may be running // We don't call readConfiguration() here, as we may be running
// very early in the JVM startup sequence. Instead readConfiguration // very early in the JVM startup sequence. Instead readConfiguration
...@@ -281,14 +283,14 @@ public class LogManager { ...@@ -281,14 +283,14 @@ public class LogManager {
return; return;
} }
readPrimordialConfiguration = true; readPrimordialConfiguration = true;
try { try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Object run() throws Exception { public Void run() throws Exception {
readConfiguration(); readConfiguration();
// Platform loggers begin to delegate to java.util.logging.Logger // Platform loggers begin to delegate to java.util.logging.Logger
sun.util.logging.PlatformLogger.redirectPlatformLoggers(); sun.util.logging.PlatformLogger.redirectPlatformLoggers();
return null; return null;
} }
}); });
...@@ -358,62 +360,290 @@ public class LogManager { ...@@ -358,62 +360,290 @@ public class LogManager {
} }
} }
// Package-level method. // Returns the LoggerContext for the user code (i.e. application or AppContext).
// Find or create a specified logger instance. If a logger has // Loggers are isolated from each AppContext.
// already been created with the given name it is returned. LoggerContext getUserContext() {
// Otherwise a new logger instance is created and registered LoggerContext context = null;
// in the LogManager global namespace.
// This method will always return a non-null Logger object.
// Synchronization is not required here. All synchronization for
// adding a new Logger object is handled by addLogger().
Logger demandLogger(String name) {
Logger result = getLogger(name);
if (result == null) {
// only allocate the new logger once
Logger newLogger = new Logger(name, null);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
return newLogger;
}
// We didn't add the new Logger that we created above SecurityManager sm = System.getSecurityManager();
// because another thread added a Logger with the same JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
// name after our null check above and before our call if (sm != null && javaAwtAccess != null) {
// to addLogger(). We have to refetch the Logger because synchronized (javaAwtAccess) {
// addLogger() returns a boolean instead of the Logger // AppContext.getAppContext() returns the system AppContext if called
// reference itself. However, if the thread that created // from a system thread but Logger.getLogger might be called from
// the other Logger is not holding a strong reference to // an applet code. Instead, find the AppContext of the applet code
// the other Logger, then it is possible for the other // from the execution stack.
// Logger to be GC'ed after we saw it in addLogger() and Object ecx = javaAwtAccess.getExecutionContext();
// before we can refetch it. If it has been GC'ed then if (ecx == null) {
// we'll just loop around and try again. // fall back to AppContext.getAppContext()
result = getLogger(name); ecx = javaAwtAccess.getContext();
} while (result == null); }
context = (LoggerContext)javaAwtAccess.get(ecx, LoggerContext.class);
if (context == null) {
if (javaAwtAccess.isMainAppContext()) {
context = userContext;
} else {
context = new UserLoggerContext();
context.addLogger(manager.rootLogger);
}
javaAwtAccess.put(ecx, LoggerContext.class, context);
}
}
} else {
context = userContext;
} }
return result; return context;
} }
// If logger.getUseParentHandlers() returns 'true' and any of the logger's LoggerContext getSystemContext() {
// parents have levels or handlers defined, make sure they are instantiated. return systemContext;
private void processParentHandlers(Logger logger, String name) { }
int ix = 1;
for (;;) { private List<LoggerContext> contexts() {
int ix2 = name.indexOf(".", ix); List<LoggerContext> cxs = new ArrayList<>();
if (ix2 < 0) { cxs.add(systemContext);
break; cxs.add(getUserContext());
return cxs;
}
static class LoggerContext {
// Table of named Loggers that maps names to Loggers.
private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
// Tree of named Loggers
private final LogNode root;
private LoggerContext() {
this.root = new LogNode(null, this);
}
synchronized Logger findLogger(String name) {
LoggerWeakRef ref = namedLoggers.get(name);
if (ref == null) {
return null;
}
Logger logger = ref.get();
if (logger == null) {
// Hashtable holds stale weak reference
// to a logger which has been GC-ed.
removeLogger(name);
}
return logger;
}
synchronized boolean addLogger(Logger logger) {
final String name = logger.getName();
if (name == null) {
throw new NullPointerException();
}
// cleanup some Loggers that have been GC'ed
manager.drainLoggerRefQueueBounded();
LoggerWeakRef ref = namedLoggers.get(name);
if (ref != null) {
if (ref.get() == null) {
// It's possible that the Logger was GC'ed after the
// drainLoggerRefQueueBounded() call above so allow
// a new one to be registered.
removeLogger(name);
} else {
// We already have a registered logger with the given name.
return false;
}
}
// We're adding a new logger.
// Note that we are creating a weak reference here.
ref = manager.new LoggerWeakRef(logger);
namedLoggers.put(name, ref);
// Apply any initial level defined for the new logger.
Level level = manager.getLevelProperty(name + ".level", null);
if (level != null) {
doSetLevel(logger, level);
}
// Do we have a per logger handler too?
// Note: this will add a 200ms penalty
manager.loadLoggerHandlers(logger, name, name + ".handlers");
processParentHandlers(logger, name);
// Find the new node and its parent.
LogNode node = getNode(name);
node.loggerRef = ref;
Logger parent = null;
LogNode nodep = node.parent;
while (nodep != null) {
LoggerWeakRef nodeRef = nodep.loggerRef;
if (nodeRef != null) {
parent = nodeRef.get();
if (parent != null) {
break;
}
}
nodep = nodep.parent;
}
if (parent != null) {
doSetParent(logger, parent);
}
// Walk over the children and tell them we are their new parent.
node.walkAndSetParent(logger);
// new LogNode is ready so tell the LoggerWeakRef about it
ref.setNode(node);
return true;
}
void removeLogger(String name) {
namedLoggers.remove(name);
}
synchronized Enumeration<String> getLoggerNames() {
return namedLoggers.keys();
}
Logger demandLogger(String name) {
return demandLogger(name, null);
}
// Find or create a specified logger instance. If a logger has
// already been created with the given name it is returned.
// Otherwise a new logger instance is created and registered
// in the LogManager global namespace.
// This method will always return a non-null Logger object.
// Synchronization is not required here. All synchronization for
// adding a new Logger object is handled by addLogger().
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
// only allocate the new logger once
Logger newLogger = new Logger(name, resourceBundleName);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
return newLogger;
}
// We didn't add the new Logger that we created above
// because another thread added a Logger with the same
// name after our null check above and before our call
// to addLogger(). We have to refetch the Logger because
// addLogger() returns a boolean instead of the Logger
// reference itself. However, if the thread that created
// the other Logger is not holding a strong reference to
// the other Logger, then it is possible for the other
// Logger to be GC'ed after we saw it in addLogger() and
// before we can refetch it. If it has been GC'ed then
// we'll just loop around and try again.
result = findLogger(name);
} while (result == null);
}
return result;
}
// If logger.getUseParentHandlers() returns 'true' and any of the logger's
// parents have levels or handlers defined, make sure they are instantiated.
private void processParentHandlers(Logger logger, String name) {
int ix = 1;
for (;;) {
int ix2 = name.indexOf(".", ix);
if (ix2 < 0) {
break;
}
String pname = name.substring(0, ix2);
if (manager.getProperty(pname + ".level") != null ||
manager.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
demandLogger(pname);
}
ix = ix2+1;
}
}
// Gets a node in our tree of logger nodes.
// If necessary, create it.
LogNode getNode(String name) {
if (name == null || name.equals("")) {
return root;
}
LogNode node = root;
while (name.length() > 0) {
int ix = name.indexOf(".");
String head;
if (ix > 0) {
head = name.substring(0, ix);
name = name.substring(ix + 1);
} else {
head = name;
name = "";
}
if (node.children == null) {
node.children = new HashMap<>();
}
LogNode child = node.children.get(head);
if (child == null) {
child = new LogNode(node, this);
node.children.put(head, child);
}
node = child;
} }
String pname = name.substring(0,ix2); return node;
}
}
static class SystemLoggerContext extends LoggerContext {
// Default resource bundle for all system loggers
Logger demandLogger(String name) {
// default to use the system logger's resource bundle
return super.demandLogger(name, Logger.SYSTEM_LOGGER_RB_NAME);
}
}
static class UserLoggerContext extends LoggerContext {
/**
* Returns a Logger of the given name if there is one registered
* in this context. Otherwise, it will return the one registered
* in the system context if there is one. The returned Logger
* instance may be initialized with a different resourceBundleName.
* If no such logger exists, a new Logger instance will be created
* and registered in this context.
*/
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
// use the system logger if exists; or allocate a new logger.
// The system logger is added to the app logger context so that
// any child logger created in the app logger context can have
// a system logger as its parent if already exist.
Logger logger = manager.systemContext.findLogger(name);
Logger newLogger =
logger != null ? logger : new Logger(name, resourceBundleName);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
// created above so return it without refetching.
return newLogger;
}
if (getProperty(pname+".level") != null || // We didn't add the new Logger that we created above
getProperty(pname+".handlers") != null) { // because another thread added a Logger with the same
// This pname has a level/handlers definition. // name after our null check above and before our call
// Make sure it exists. // to addLogger(). We have to refetch the Logger because
demandLogger(pname); // addLogger() returns a boolean instead of the Logger
// reference itself. However, if the thread that created
// the other Logger is not holding a strong reference to
// the other Logger, then it is possible for the other
// Logger to be GC'ed after we saw it in addLogger() and
// before we can refetch it. If it has been GC'ed then
// we'll just loop around and try again.
result = findLogger(name);
} while (result == null);
} }
ix = ix2+1; return result;
} }
} }
...@@ -438,16 +668,17 @@ public class LogManager { ...@@ -438,16 +668,17 @@ public class LogManager {
try { try {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word); Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
Handler hdl = (Handler) clz.newInstance(); Handler hdl = (Handler) clz.newInstance();
try { // Check if there is a property defining the
// Check if there is a property defining the // this handler's level.
// this handler's level. String levs = getProperty(word + ".level");
String levs = getProperty(word + ".level"); if (levs != null) {
if (levs != null) { Level l = Level.findLevel(levs);
hdl.setLevel(Level.parse(levs)); if (l != null) {
hdl.setLevel(l);
} else {
// Probably a bad level. Drop through.
System.err.println("Can't set level for " + word);
} }
} catch (Exception ex) {
System.err.println("Can't set level for " + word);
// Probably a bad level. Drop through.
} }
// Add this Handler to the logger // Add this Handler to the logger
logger.addHandler(hdl); logger.addHandler(hdl);
...@@ -503,7 +734,7 @@ public class LogManager { ...@@ -503,7 +734,7 @@ public class LogManager {
if (node != null) { if (node != null) {
// if we have a LogNode, then we were a named Logger // if we have a LogNode, then we were a named Logger
// so clear namedLoggers weak ref to us // so clear namedLoggers weak ref to us
manager.namedLoggers.remove(name); node.context.removeLogger(name);
name = null; // clear our ref to the Logger's name name = null; // clear our ref to the Logger's name
node.loggerRef = null; // clear LogNode's weak ref to us node.loggerRef = null; // clear LogNode's weak ref to us
...@@ -592,70 +823,15 @@ public class LogManager { ...@@ -592,70 +823,15 @@ public class LogManager {
* false if a logger of that name already exists. * false if a logger of that name already exists.
* @exception NullPointerException if the logger name is null. * @exception NullPointerException if the logger name is null.
*/ */
public synchronized boolean addLogger(Logger logger) { public boolean addLogger(Logger logger) {
final String name = logger.getName(); final String name = logger.getName();
if (name == null) { if (name == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
if (systemContext.findLogger(name) != null) {
// cleanup some Loggers that have been GC'ed return false;
drainLoggerRefQueueBounded();
LoggerWeakRef ref = namedLoggers.get(name);
if (ref != null) {
if (ref.get() == null) {
// It's possible that the Logger was GC'ed after the
// drainLoggerRefQueueBounded() call above so allow
// a new one to be registered.
namedLoggers.remove(name);
} else {
// We already have a registered logger with the given name.
return false;
}
}
// We're adding a new logger.
// Note that we are creating a weak reference here.
ref = new LoggerWeakRef(logger);
namedLoggers.put(name, ref);
// Apply any initial level defined for the new logger.
Level level = getLevelProperty(name+".level", null);
if (level != null) {
doSetLevel(logger, level);
}
// Do we have a per logger handler too?
// Note: this will add a 200ms penalty
loadLoggerHandlers(logger, name, name+".handlers");
processParentHandlers(logger, name);
// Find the new node and its parent.
LogNode node = findNode(name);
node.loggerRef = ref;
Logger parent = null;
LogNode nodep = node.parent;
while (nodep != null) {
LoggerWeakRef nodeRef = nodep.loggerRef;
if (nodeRef != null) {
parent = nodeRef.get();
if (parent != null) {
break;
}
}
nodep = nodep.parent;
}
if (parent != null) {
doSetParent(logger, parent);
} }
// Walk over the children and tell them we are their new parent. return getUserContext().addLogger(logger);
node.walkAndSetParent(logger);
// new LogNode is ready so tell the LoggerWeakRef about it
ref.setNode(node);
return true;
} }
...@@ -697,36 +873,6 @@ public class LogManager { ...@@ -697,36 +873,6 @@ public class LogManager {
}}); }});
} }
// Find a node in our tree of logger nodes.
// If necessary, create it.
private LogNode findNode(String name) {
if (name == null || name.equals("")) {
return root;
}
LogNode node = root;
while (name.length() > 0) {
int ix = name.indexOf(".");
String head;
if (ix > 0) {
head = name.substring(0,ix);
name = name.substring(ix+1);
} else {
head = name;
name = "";
}
if (node.children == null) {
node.children = new HashMap<>();
}
LogNode child = node.children.get(head);
if (child == null) {
child = new LogNode(node);
node.children.put(head, child);
}
node = child;
}
return node;
}
/** /**
* Method to find a named logger. * Method to find a named logger.
* <p> * <p>
...@@ -742,18 +888,16 @@ public class LogManager { ...@@ -742,18 +888,16 @@ public class LogManager {
* @param name name of the logger * @param name name of the logger
* @return matching logger or null if none is found * @return matching logger or null if none is found
*/ */
public synchronized Logger getLogger(String name) { public Logger getLogger(String name) {
LoggerWeakRef ref = namedLoggers.get(name); // return the first logger added
if (ref == null) { //
return null; // once a system logger is added in the system context, no one can
} // adds a logger with the same name in the global context
Logger logger = ref.get(); // (see LogManager.addLogger). So if there is a logger in the global
if (logger == null) { // context with the same name as one in the system context, it must be
// Hashtable holds stale weak reference // added before the system logger was created.
// to a logger which has been GC-ed. Logger logger = getUserContext().findLogger(name);
namedLoggers.remove(name); return logger != null ? logger : systemContext.findLogger(name);
}
return logger;
} }
/** /**
...@@ -772,8 +916,11 @@ public class LogManager { ...@@ -772,8 +916,11 @@ public class LogManager {
* <p> * <p>
* @return enumeration of logger name strings * @return enumeration of logger name strings
*/ */
public synchronized Enumeration<String> getLoggerNames() { public Enumeration<String> getLoggerNames() {
return namedLoggers.keys(); // only return unique names
Set<String> names = new HashSet<>(Collections.list(systemContext.getLoggerNames()));
names.addAll(Collections.list(getUserContext().getLoggerNames()));
return Collections.enumeration(names);
} }
/** /**
...@@ -858,20 +1005,20 @@ public class LogManager { ...@@ -858,20 +1005,20 @@ public class LogManager {
// the global handlers, if they haven't been initialized yet. // the global handlers, if they haven't been initialized yet.
initializedGlobalHandlers = true; initializedGlobalHandlers = true;
} }
Enumeration<String> enum_ = getLoggerNames(); for (LoggerContext cx : contexts()) {
while (enum_.hasMoreElements()) { Enumeration<String> enum_ = cx.getLoggerNames();
String name = enum_.nextElement(); while (enum_.hasMoreElements()) {
resetLogger(name); String name = enum_.nextElement();
Logger logger = cx.findLogger(name);
if (logger != null) {
resetLogger(logger);
}
}
} }
} }
// Private method to reset an individual target logger. // Private method to reset an individual target logger.
private void resetLogger(String name) { private void resetLogger(Logger logger) {
Logger logger = getLogger(name);
if (logger == null) {
return;
}
// Close all the Logger's handlers. // Close all the Logger's handlers.
Handler[] targets = logger.getHandlers(); Handler[] targets = logger.getHandlers();
for (int i = 0; i < targets.length; i++) { for (int i = 0; i < targets.length; i++) {
...@@ -883,6 +1030,7 @@ public class LogManager { ...@@ -883,6 +1030,7 @@ public class LogManager {
// Problems closing a handler? Keep going... // Problems closing a handler? Keep going...
} }
} }
String name = logger.getName();
if (name != null && name.equals("")) { if (name != null && name.equals("")) {
// This is the root logger. // This is the root logger.
logger.setLevel(defaultLevel); logger.setLevel(defaultLevel);
...@@ -1046,11 +1194,8 @@ public class LogManager { ...@@ -1046,11 +1194,8 @@ public class LogManager {
if (val == null) { if (val == null) {
return defaultValue; return defaultValue;
} }
try { Level l = Level.findLevel(val.trim());
return Level.parse(val.trim()); return l != null ? l : defaultValue;
} catch (Exception ex) {
return defaultValue;
}
} }
// Package private method to get a filter property. // Package private method to get a filter property.
...@@ -1140,9 +1285,11 @@ public class LogManager { ...@@ -1140,9 +1285,11 @@ public class LogManager {
HashMap<String,LogNode> children; HashMap<String,LogNode> children;
LoggerWeakRef loggerRef; LoggerWeakRef loggerRef;
LogNode parent; LogNode parent;
final LoggerContext context;
LogNode(LogNode parent) { LogNode(LogNode parent, LoggerContext context) {
this.parent = parent; this.parent = parent;
this.context = context;
} }
// Recursive method to walk the tree below a node and set // Recursive method to walk the tree below a node and set
...@@ -1215,11 +1362,13 @@ public class LogManager { ...@@ -1215,11 +1362,13 @@ public class LogManager {
System.err.println("Bad level value for property: " + key); System.err.println("Bad level value for property: " + key);
continue; continue;
} }
Logger l = getLogger(name); for (LoggerContext cx : contexts()) {
if (l == null) { Logger l = cx.findLogger(name);
continue; if (l == null) {
continue;
}
l.setLevel(level);
} }
l.setLevel(level);
} }
} }
......
...@@ -30,6 +30,7 @@ import java.util.*; ...@@ -30,6 +30,7 @@ import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.security.*; import java.security.*;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.logging.LogManager.LoggerContext;
/** /**
* A Logger object is used to log messages for a specific * A Logger object is used to log messages for a specific
...@@ -286,6 +287,26 @@ public class Logger { ...@@ -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 * Find or create a logger for a named subsystem. If a logger has
* already been created with the given name it is returned. Otherwise * already been created with the given name it is returned. Otherwise
...@@ -327,8 +348,8 @@ public class Logger { ...@@ -327,8 +348,8 @@ public class Logger {
// would throw an IllegalArgumentException in the second call // would throw an IllegalArgumentException in the second call
// because the wrapper would result in an attempt to replace // because the wrapper would result in an attempt to replace
// the existing "resourceBundleForFoo" with null. // the existing "resourceBundleForFoo" with null.
LogManager manager = LogManager.getLogManager(); LoggerContext context = getLoggerContext();
return manager.demandLogger(name); return context.demandLogger(name);
} }
/** /**
...@@ -375,8 +396,8 @@ public class Logger { ...@@ -375,8 +396,8 @@ public class Logger {
// Synchronization is not required here. All synchronization for // Synchronization is not required here. All synchronization for
// adding a new Logger object is handled by LogManager.addLogger(). // adding a new Logger object is handled by LogManager.addLogger().
public static Logger getLogger(String name, String resourceBundleName) { public static Logger getLogger(String name, String resourceBundleName) {
LogManager manager = LogManager.getLogManager(); LoggerContext context = getLoggerContext();
Logger result = manager.demandLogger(name); Logger result = context.demandLogger(name, resourceBundleName);
// MissingResourceException or IllegalArgumentException can be // MissingResourceException or IllegalArgumentException can be
// thrown by setupResourceInfo(). // thrown by setupResourceInfo().
...@@ -384,6 +405,18 @@ public class Logger { ...@@ -384,6 +405,18 @@ public class Logger {
return result; 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 * Create an anonymous Logger. The newly created Logger is not
...@@ -536,7 +569,7 @@ public class Logger { ...@@ -536,7 +569,7 @@ public class Logger {
private void doLog(LogRecord lr) { private void doLog(LogRecord lr) {
lr.setLoggerName(name); lr.setLoggerName(name);
String ebname = getEffectiveResourceBundleName(); String ebname = getEffectiveResourceBundleName();
if (ebname != null) { if (ebname != null && !ebname.equals(SYSTEM_LOGGER_RB_NAME)) {
lr.setResourceBundleName(ebname); lr.setResourceBundleName(ebname);
lr.setResourceBundle(findResourceBundle(ebname)); lr.setResourceBundle(findResourceBundle(ebname));
} }
...@@ -1285,6 +1318,22 @@ public class Logger { ...@@ -1285,6 +1318,22 @@ public class Logger {
// May also return null if we can't find the resource bundle and // May also return null if we can't find the resource bundle and
// there is no suitable previous cached value. // 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) { private synchronized ResourceBundle findResourceBundle(String name) {
// Return a null bundle for a null name. // Return a null bundle for a null name.
if (name == null) { if (name == null) {
...@@ -1299,6 +1348,13 @@ public class Logger { ...@@ -1299,6 +1348,13 @@ public class Logger {
return catalog; 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 thread's context ClassLoader. If there isn't one,
// use the SystemClassloader. // use the SystemClassloader.
ClassLoader cl = Thread.currentThread().getContextClassLoader(); ClassLoader cl = Thread.currentThread().getContextClassLoader();
...@@ -1315,7 +1371,6 @@ public class Logger { ...@@ -1315,7 +1371,6 @@ public class Logger {
// ClassLoader. Drop through. // ClassLoader. Drop through.
} }
// Fall back to searching up the call stack and trying each // Fall back to searching up the call stack and trying each
// calling ClassLoader. // calling ClassLoader.
for (int ix = 0; ; ix++) { for (int ix = 0; ; ix++) {
......
...@@ -34,7 +34,7 @@ import java.util.ArrayList; ...@@ -34,7 +34,7 @@ import java.util.ArrayList;
* *
* The <tt>LoggingMXBean</tt> interface provides a standard * The <tt>LoggingMXBean</tt> interface provides a standard
* method for management access to the individual * method for management access to the individual
* java.util.Logger objects available at runtime. * {@code Logger} objects available at runtime.
* *
* @author Ron Mann * @author Ron Mann
* @author Mandy Chung * @author Mandy Chung
...@@ -75,7 +75,7 @@ class Logging implements LoggingMXBean { ...@@ -75,7 +75,7 @@ class Logging implements LoggingMXBean {
if (level == null) { if (level == null) {
return EMPTY_STRING; return EMPTY_STRING;
} else { } else {
return level.getName(); return level.getLevelName();
} }
} }
...@@ -85,7 +85,6 @@ class Logging implements LoggingMXBean { ...@@ -85,7 +85,6 @@ class Logging implements LoggingMXBean {
} }
Logger logger = logManager.getLogger(loggerName); Logger logger = logManager.getLogger(loggerName);
if (logger == null) { if (logger == null) {
throw new IllegalArgumentException("Logger " + loggerName + throw new IllegalArgumentException("Logger " + loggerName +
"does not exist"); "does not exist");
...@@ -94,7 +93,10 @@ class Logging implements LoggingMXBean { ...@@ -94,7 +93,10 @@ class Logging implements LoggingMXBean {
Level level = null; Level level = null;
if (levelName != null) { if (levelName != null) {
// parse will throw IAE if logLevel is invalid // 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); logger.setLevel(level);
......
...@@ -37,7 +37,8 @@ class LoggingProxyImpl implements LoggingProxy { ...@@ -37,7 +37,8 @@ class LoggingProxyImpl implements LoggingProxy {
@Override @Override
public Object getLogger(String name) { public Object getLogger(String name) {
return Logger.getLogger(name); // always create a platform logger with the resource bundle name
return Logger.getPlatformLogger(name);
} }
@Override @Override
...@@ -92,12 +93,16 @@ class LoggingProxyImpl implements LoggingProxy { ...@@ -92,12 +93,16 @@ class LoggingProxyImpl implements LoggingProxy {
@Override @Override
public Object parseLevel(String levelName) { 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 @Override
public String getLevelName(Object level) { public String getLevelName(Object level) {
return ((Level) level).getName(); return ((Level) level).getLevelName();
} }
@Override @Override
......
...@@ -162,7 +162,7 @@ public class SimpleFormatter extends Formatter { ...@@ -162,7 +162,7 @@ public class SimpleFormatter extends Formatter {
dat, dat,
source, source,
record.getLoggerName(), record.getLoggerName(),
record.getLevel().getLocalizedName(), record.getLevel().getLocalizedLevelName(),
message, message,
throwable); throwable);
} }
......
...@@ -327,21 +327,27 @@ public final class AppContext { ...@@ -327,21 +327,27 @@ public final class AppContext {
// Before we return the main "system" AppContext, check to // Before we return the main "system" AppContext, check to
// see if there's an AWTSecurityManager installed. If so, // see if there's an AWTSecurityManager installed. If so,
// allow it to choose the AppContext to return. // allow it to choose the AppContext to return.
SecurityManager securityManager = System.getSecurityManager(); AppContext secAppContext = getExecutionAppContext();
if ((securityManager != null) && if (secAppContext != null) {
(securityManager instanceof AWTSecurityManager)) appContext = secAppContext; // Return what we're told
{
AWTSecurityManager awtSecMgr = (AWTSecurityManager)securityManager;
AppContext secAppContext = awtSecMgr.getAppContext();
if (secAppContext != null) {
appContext = secAppContext; // Return what we're told
}
} }
} }
return appContext; 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. * Returns the main ("system") AppContext.
* *
...@@ -806,6 +812,21 @@ public final class AppContext { ...@@ -806,6 +812,21 @@ public final class AppContext {
public boolean isMainAppContext() { public boolean isMainAppContext() {
return (numAppContexts.get() == 1); 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 @@ ...@@ -26,6 +26,14 @@
package sun.misc; package sun.misc;
public interface JavaAWTAccess { 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 Object get(Object key);
public void put(Object key, Object value); public void put(Object key, Object value);
public void remove(Object key); public void remove(Object key);
......
...@@ -148,6 +148,9 @@ keystore.type=jks ...@@ -148,6 +148,9 @@ keystore.type=jks
package.access=sun.,\ package.access=sun.,\
com.sun.xml.internal.,\ com.sun.xml.internal.,\
com.sun.imageio.,\ 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.xerces.internal.utils.,\
com.sun.org.apache.xalan.internal.utils.,\ com.sun.org.apache.xalan.internal.utils.,\
com.sun.org.glassfish.external.,\ com.sun.org.glassfish.external.,\
...@@ -166,6 +169,9 @@ package.access=sun.,\ ...@@ -166,6 +169,9 @@ package.access=sun.,\
package.definition=sun.,\ package.definition=sun.,\
com.sun.xml.internal.,\ com.sun.xml.internal.,\
com.sun.imageio.,\ 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.xerces.internal.utils.,\
com.sun.org.apache.xalan.internal.utils.,\ com.sun.org.apache.xalan.internal.utils.,\
com.sun.org.glassfish.external.,\ com.sun.org.glassfish.external.,\
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册