提交 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;
}
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.
known.add(this);
return this;
}
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;
}
}
}
......@@ -34,6 +34,8 @@ import java.lang.ref.WeakReference;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.net.URL;
import sun.misc.JavaAWTAccess;
import sun.misc.SharedSecrets;
import sun.security.action.GetPropertyAction;
/**
......@@ -157,10 +159,9 @@ public class LogManager {
// count to allow for cases where the same listener is registered many times.
private final Map<PropertyChangeListener,Integer> listenerMap = new HashMap<>();
// Table of named Loggers that maps names to Loggers.
private Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();
// Tree of named Loggers
private LogNode root = new LogNode(null);
// LoggerContext for system loggers and user loggers
private final LoggerContext systemContext = new SystemLoggerContext();
private final LoggerContext userContext = new UserLoggerContext();
private Logger rootLogger;
// Have we done the primordial reading of the configuration file?
......@@ -198,12 +199,13 @@ public class LogManager {
// Create and retain Logger for the root of the namespace.
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>
// would deadlock with the LogManager.<clinit>.
Logger.getGlobal().setLogManager(manager);
manager.addLogger(Logger.getGlobal());
manager.systemContext.addLogger(Logger.getGlobal());
// We don't call readConfiguration() here, as we may be running
// very early in the JVM startup sequence. Instead readConfiguration
......@@ -281,14 +283,14 @@ public class LogManager {
return;
}
readPrimordialConfiguration = true;
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws Exception {
readConfiguration();
// Platform loggers begin to delegate to java.util.logging.Logger
sun.util.logging.PlatformLogger.redirectPlatformLoggers();
return null;
}
});
......@@ -358,7 +360,152 @@ public class LogManager {
}
}
// Package-level method.
// Returns the LoggerContext for the user code (i.e. application or AppContext).
// Loggers are isolated from each AppContext.
LoggerContext getUserContext() {
LoggerContext context = null;
SecurityManager sm = System.getSecurityManager();
JavaAWTAccess javaAwtAccess = SharedSecrets.getJavaAWTAccess();
if (sm != null && javaAwtAccess != null) {
synchronized (javaAwtAccess) {
// AppContext.getAppContext() returns the system AppContext if called
// from a system thread but Logger.getLogger might be called from
// an applet code. Instead, find the AppContext of the applet code
// from the execution stack.
Object ecx = javaAwtAccess.getExecutionContext();
if (ecx == null) {
// fall back to AppContext.getAppContext()
ecx = javaAwtAccess.getContext();
}
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 context;
}
LoggerContext getSystemContext() {
return systemContext;
}
private List<LoggerContext> contexts() {
List<LoggerContext> cxs = new ArrayList<>();
cxs.add(systemContext);
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
......@@ -367,11 +514,11 @@ public class LogManager {
// 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);
Logger demandLogger(String name, String resourceBundleName) {
Logger result = findLogger(name);
if (result == null) {
// only allocate the new logger once
Logger newLogger = new Logger(name, null);
Logger newLogger = new Logger(name, resourceBundleName);
do {
if (addLogger(newLogger)) {
// We successfully added the new Logger that we
......@@ -390,7 +537,7 @@ public class LogManager {
// 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 = getLogger(name);
result = findLogger(name);
} while (result == null);
}
return result;
......@@ -405,10 +552,10 @@ public class LogManager {
if (ix2 < 0) {
break;
}
String pname = name.substring(0,ix2);
String pname = name.substring(0, ix2);
if (getProperty(pname+".level") != null ||
getProperty(pname+".handlers") != null) {
if (manager.getProperty(pname + ".level") != null ||
manager.getProperty(pname + ".handlers") != null) {
// This pname has a level/handlers definition.
// Make sure it exists.
demandLogger(pname);
......@@ -417,6 +564,89 @@ public class LogManager {
}
}
// 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;
}
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;
}
// 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;
}
}
// Add new per logger handlers.
// We need to raise privilege here. All our decisions will
// be made based on the logging configuration, which can
......@@ -438,16 +668,17 @@ public class LogManager {
try {
Class<?> clz = ClassLoader.getSystemClassLoader().loadClass(word);
Handler hdl = (Handler) clz.newInstance();
try {
// Check if there is a property defining the
// this handler's level.
String levs = getProperty(word + ".level");
if (levs != null) {
hdl.setLevel(Level.parse(levs));
}
} catch (Exception ex) {
System.err.println("Can't set level for " + word);
Level l = Level.findLevel(levs);
if (l != null) {
hdl.setLevel(l);
} else {
// Probably a bad level. Drop through.
System.err.println("Can't set level for " + word);
}
}
// Add this Handler to the logger
logger.addHandler(hdl);
......@@ -503,7 +734,7 @@ public class LogManager {
if (node != null) {
// if we have a LogNode, then we were a named Logger
// 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
node.loggerRef = null; // clear LogNode's weak ref to us
......@@ -592,70 +823,15 @@ public class LogManager {
* false if a logger of that name already exists.
* @exception NullPointerException if the logger name is null.
*/
public synchronized boolean addLogger(Logger logger) {
public boolean addLogger(Logger logger) {
final String name = logger.getName();
if (name == null) {
throw new NullPointerException();
}
// cleanup some Loggers that have been GC'ed
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.
if (systemContext.findLogger(name) != null) {
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.
node.walkAndSetParent(logger);
// new LogNode is ready so tell the LoggerWeakRef about it
ref.setNode(node);
return true;
return getUserContext().addLogger(logger);
}
......@@ -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.
* <p>
......@@ -742,18 +888,16 @@ public class LogManager {
* @param name name of the logger
* @return matching logger or null if none is found
*/
public synchronized Logger getLogger(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.
namedLoggers.remove(name);
}
return logger;
public Logger getLogger(String name) {
// return the first logger added
//
// once a system logger is added in the system context, no one can
// adds a logger with the same name in the global context
// (see LogManager.addLogger). So if there is a logger in the global
// context with the same name as one in the system context, it must be
// added before the system logger was created.
Logger logger = getUserContext().findLogger(name);
return logger != null ? logger : systemContext.findLogger(name);
}
/**
......@@ -772,8 +916,11 @@ public class LogManager {
* <p>
* @return enumeration of logger name strings
*/
public synchronized Enumeration<String> getLoggerNames() {
return namedLoggers.keys();
public Enumeration<String> getLoggerNames() {
// 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 {
// the global handlers, if they haven't been initialized yet.
initializedGlobalHandlers = true;
}
Enumeration<String> enum_ = getLoggerNames();
for (LoggerContext cx : contexts()) {
Enumeration<String> enum_ = cx.getLoggerNames();
while (enum_.hasMoreElements()) {
String name = enum_.nextElement();
resetLogger(name);
Logger logger = cx.findLogger(name);
if (logger != null) {
resetLogger(logger);
}
}
}
}
// Private method to reset an individual target logger.
private void resetLogger(String name) {
Logger logger = getLogger(name);
if (logger == null) {
return;
}
private void resetLogger(Logger logger) {
// Close all the Logger's handlers.
Handler[] targets = logger.getHandlers();
for (int i = 0; i < targets.length; i++) {
......@@ -883,6 +1030,7 @@ public class LogManager {
// Problems closing a handler? Keep going...
}
}
String name = logger.getName();
if (name != null && name.equals("")) {
// This is the root logger.
logger.setLevel(defaultLevel);
......@@ -1046,11 +1194,8 @@ public class LogManager {
if (val == null) {
return defaultValue;
}
try {
return Level.parse(val.trim());
} catch (Exception ex) {
return defaultValue;
}
Level l = Level.findLevel(val.trim());
return l != null ? l : defaultValue;
}
// Package private method to get a filter property.
......@@ -1140,9 +1285,11 @@ public class LogManager {
HashMap<String,LogNode> children;
LoggerWeakRef loggerRef;
LogNode parent;
final LoggerContext context;
LogNode(LogNode parent) {
LogNode(LogNode parent, LoggerContext context) {
this.parent = parent;
this.context = context;
}
// Recursive method to walk the tree below a node and set
......@@ -1215,13 +1362,15 @@ public class LogManager {
System.err.println("Bad level value for property: " + key);
continue;
}
Logger l = getLogger(name);
for (LoggerContext cx : contexts()) {
Logger l = cx.findLogger(name);
if (l == null) {
continue;
}
l.setLevel(level);
}
}
}
// Management Support
private static LoggingMXBean loggingMXBean = 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();
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.
先完成此消息的编辑!
想要评论请 注册