< prev index next >
src/java.logging/share/classes/java/util/logging/Logger.java
Print this page
*** 257,273 ****
new LoggerBundle(null, null);
private static final RuntimePermission GET_CLASS_LOADER_PERMISSION =
new RuntimePermission("getClassLoader");
private volatile LogManager manager;
private String name;
- private final CopyOnWriteArrayList<Handler> handlers =
- new CopyOnWriteArrayList<>();
private volatile LoggerBundle loggerBundle = NO_RESOURCE_BUNDLE;
- private volatile boolean useParentHandlers = true;
- private volatile Filter filter;
private boolean anonymous;
// Cache to speed up behavior of findResourceBundle:
private ResourceBundle catalog; // Cached resource bundle
private String catalogName; // name associated with catalog
--- 257,447 ----
new LoggerBundle(null, null);
private static final RuntimePermission GET_CLASS_LOADER_PERMISSION =
new RuntimePermission("getClassLoader");
+ // A value class that holds the logger configuration data.
+ // This configuration can be shared between an application logger
+ // and a system logger of the same name.
+ private static final class ConfigurationData {
+
+ // The delegate field is used to avoid races while
+ // merging configuration. This will ensure that any pending
+ // configuration action on an application logger will either
+ // be finished before the merge happens, or will be forwarded
+ // to the system logger configuration after the merge is completed.
+ // By default delegate=this.
+ private volatile ConfigurationData delegate;
+
+ volatile boolean useParentHandlers;
+ volatile Filter filter;
+ volatile Level levelObject;
+ volatile int levelValue; // current effective level value
+ final CopyOnWriteArrayList<Handler> handlers =
+ new CopyOnWriteArrayList<>();
+
+ ConfigurationData() {
+ delegate = this;
+ useParentHandlers = true;
+ levelValue = Level.INFO.intValue();
+ }
+
+ void setUseParentHandlers(boolean flag) {
+ useParentHandlers = flag;
+ if (delegate != this) {
+ // merge in progress - propagate value to system peer.
+ final ConfigurationData system = delegate;
+ synchronized (system) {
+ system.useParentHandlers = useParentHandlers;
+ }
+ }
+ }
+
+ void setFilter(Filter f) {
+ filter = f;
+ if (delegate != this) {
+ // merge in progress - propagate value to system peer.
+ final ConfigurationData system = delegate;
+ synchronized (system) {
+ system.filter = filter;
+ }
+ }
+ }
+
+ void setLevelObject(Level l) {
+ levelObject = l;
+ if (delegate != this) {
+ // merge in progress - propagate value to system peer.
+ final ConfigurationData system = delegate;
+ synchronized (system) {
+ system.levelObject = levelObject;
+ }
+ }
+ }
+
+ void setLevelValue(int v) {
+ levelValue = v;
+ if (delegate != this) {
+ // merge in progress - propagate value to system peer.
+ final ConfigurationData system = delegate;
+ synchronized (system) {
+ system.levelValue = levelValue;
+ }
+ }
+ }
+
+ void addHandler(Handler h) {
+ if (handlers.add(h)) {
+ if (delegate != this) {
+ // merge in progress - propagate value to system peer.
+ final ConfigurationData system = delegate;
+ synchronized (system) {
+ system.handlers.addIfAbsent(h);
+ }
+ }
+ }
+ }
+
+ void removeHandler(Handler h) {
+ if (handlers.remove(h)) {
+ if (delegate != this) {
+ // merge in progress - propagate value to system peer.
+ final ConfigurationData system = delegate;
+ synchronized (system) {
+ system.handlers.remove(h);
+ }
+ }
+ }
+ }
+
+ ConfigurationData merge(Logger systemPeer) {
+ if (!systemPeer.isSystemLogger) {
+ // should never come here
+ throw new InternalError("not a system logger");
+ }
+
+ ConfigurationData system = systemPeer.config;
+
+ if (system == this) {
+ // nothing to do
+ return system;
+ }
+
+ systemPeer.checkPermission();
+
+ synchronized (system) {
+ // synchronize before checking on delegate to counter
+ // race conditions where two threads might attempt to
+ // merge concurrently
+ if (delegate == system) {
+ // merge already performed;
+ return system;
+ }
+
+ // publish system as the temporary delegate configuration.
+ // This should take care of potential race conditions where
+ // an other thread might attempt to call e.g. setlevel on
+ // the application logger while merge is in progress.
+ // (see implementation of ConfigurationData::setLevel)
+ delegate = system;
+
+ // merge this config object data into the system config
+ system.useParentHandlers = useParentHandlers;
+ system.filter = filter;
+ system.levelObject = levelObject;
+ system.levelValue = levelValue;
+
+ // Prevent race condition in case two threads attempt to merge
+ // configuration and add handlers at the same time. We don't want
+ // to add the same handlers twice.
+ //
+ // Handlers are created and loaded by LogManager.addLogger. If we
+ // reach here, then it means that the application logger has
+ // been created first and added with LogManager.addLogger, and the
+ // system logger was created after - and no handler has been added
+ // to it by LogManager.addLogger. Therefore, system.handlers
+ // should be empty.
+ //
+ // A non empty cfg.handlers list indicates a race condition
+ // where two threads might attempt to merge the configuration
+ // or add handlers concurrently. Though of no consequence for
+ // the other data (level etc...) this would be an issue if we
+ // added the same handlers twice.
+ //
+ for (Handler h : handlers) {
+ if (!system.handlers.contains(h)) {
+ systemPeer.addHandler(h);
+ }
+ }
+ system.handlers.retainAll(handlers);
+ system.handlers.addAllAbsent(handlers);
+ }
+
+ // sanity: update effective level after merging
+ synchronized(treeLock) {
+ systemPeer.updateEffectiveLevel();
+ }
+
+ return system;
+ }
+
+ }
+
+ // The logger configuration data. Ideally, this should be final
+ // for system loggers, and replace-once for application loggers.
+ // When an application requests a logger by name, we do not know a-priori
+ // whether that corresponds to a system logger name or not.
+ // So if no system logger by that name already exists, we simply return an
+ // application logger.
+ // If a system class later requests a system logger of the same name, then
+ // the application logger and system logger configurations will be merged
+ // in a single instance of ConfigurationData that both loggers will share.
+ private volatile ConfigurationData config;
+
private volatile LogManager manager;
private String name;
private volatile LoggerBundle loggerBundle = NO_RESOURCE_BUNDLE;
private boolean anonymous;
// Cache to speed up behavior of findResourceBundle:
private ResourceBundle catalog; // Cached resource bundle
private String catalogName; // name associated with catalog
*** 278,289 ****
private static final Object treeLock = new Object();
// We keep weak references from parents to children, but strong
// references from children to parents.
private volatile Logger parent; // our nearest parent.
private ArrayList<LogManager.LoggerWeakRef> kids; // WeakReferences to loggers that have us as parent
- private volatile Level levelObject;
- private volatile int levelValue; // current effective level value
private WeakReference<Module> callerModuleRef;
private final boolean isSystemLogger;
/**
* GLOBAL_LOGGER_NAME is a name for the global logger.
--- 452,461 ----
*** 382,394 ****
Logger(String name, String resourceBundleName, Module caller,
LogManager manager, boolean isSystemLogger) {
this.manager = manager;
this.isSystemLogger = isSystemLogger;
! setupResourceInfo(resourceBundleName, caller);
this.name = name;
! levelValue = Level.INFO.intValue();
}
private void setCallerModuleRef(Module callerModule) {
if (callerModule != null) {
this.callerModuleRef = new WeakReference<>(callerModule);
--- 554,586 ----
Logger(String name, String resourceBundleName, Module caller,
LogManager manager, boolean isSystemLogger) {
this.manager = manager;
this.isSystemLogger = isSystemLogger;
! this.config = new ConfigurationData();
this.name = name;
! setupResourceInfo(resourceBundleName, caller);
! }
!
! // Called by LogManager when a system logger is created
! // after a user logger of the same name.
! // Ensure that both loggers will share the same
! // configuration.
! final void mergeWithSystemLogger(Logger system) {
! // sanity checks
! if (!system.isSystemLogger
! || anonymous
! || name == null
! || !name.equals(system.name)) {
! // should never come here
! throw new InternalError("invalid logger merge");
! }
! checkPermission();
! final ConfigurationData cfg = config;
! if (cfg != system.config) {
! config = cfg.merge(system);
! }
}
private void setCallerModuleRef(Module callerModule) {
if (callerModule != null) {
this.callerModuleRef = new WeakReference<>(callerModule);
*** 406,416 ****
// and Logger static initializers causing deadlocks.
private Logger(String name) {
// The manager field is not initialized here.
this.name = name;
this.isSystemLogger = true;
! levelValue = Level.INFO.intValue();
}
// It is called from LoggerContext.addLocalLogger() when the logger
// is actually added to a LogManager.
void setLogManager(LogManager manager) {
--- 598,608 ----
// and Logger static initializers causing deadlocks.
private Logger(String name) {
// The manager field is not initialized here.
this.name = name;
this.isSystemLogger = true;
! config = new ConfigurationData();
}
// It is called from LoggerContext.addLocalLogger() when the logger
// is actually added to a LogManager.
void setLogManager(LogManager manager) {
*** 449,459 ****
}
private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
LogManager manager = LogManager.getLogManager();
if (!SystemLoggerHelper.disableCallerCheck) {
! if (caller.getClassLoader() == null) {
return manager.demandSystemLogger(name, resourceBundleName, caller);
}
}
return manager.demandLogger(name, resourceBundleName, caller);
// ends up calling new Logger(name, resourceBundleName, caller)
--- 641,651 ----
}
private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
LogManager manager = LogManager.getLogManager();
if (!SystemLoggerHelper.disableCallerCheck) {
! if (isSystem(caller.getModule())) {
return manager.demandSystemLogger(name, resourceBundleName, caller);
}
}
return manager.demandLogger(name, resourceBundleName, caller);
// ends up calling new Logger(name, resourceBundleName, caller)
*** 738,757 ****
* this logger is not anonymous, and the caller
* does not have LoggingPermission("control").
*/
public void setFilter(Filter newFilter) throws SecurityException {
checkPermission();
! filter = newFilter;
}
/**
* Get the current filter for this Logger.
*
* @return a filter object (may be null)
*/
public Filter getFilter() {
! return filter;
}
/**
* Log a LogRecord.
* <p>
--- 930,949 ----
* this logger is not anonymous, and the caller
* does not have LoggingPermission("control").
*/
public void setFilter(Filter newFilter) throws SecurityException {
checkPermission();
! config.setFilter(newFilter);
}
/**
* Get the current filter for this Logger.
*
* @return a filter object (may be null)
*/
public Filter getFilter() {
! return config.filter;
}
/**
* Log a LogRecord.
* <p>
*** 763,773 ****
*/
public void log(LogRecord record) {
if (!isLoggable(record.getLevel())) {
return;
}
! Filter theFilter = filter;
if (theFilter != null && !theFilter.isLoggable(record)) {
return;
}
// Post the LogRecord to all our Handlers, and then to
--- 955,965 ----
*/
public void log(LogRecord record) {
if (!isLoggable(record.getLevel())) {
return;
}
! Filter theFilter = config.filter;
if (theFilter != null && !theFilter.isLoggable(record)) {
return;
}
// Post the LogRecord to all our Handlers, and then to
*** 782,792 ****
for (Handler handler : loggerHandlers) {
handler.publish(record);
}
final boolean useParentHdls = isSystemLogger
! ? logger.useParentHandlers
: logger.getUseParentHandlers();
if (!useParentHdls) {
break;
}
--- 974,984 ----
for (Handler handler : loggerHandlers) {
handler.publish(record);
}
final boolean useParentHdls = isSystemLogger
! ? logger.config.useParentHandlers
: logger.getUseParentHandlers();
if (!useParentHdls) {
break;
}
*** 1802,1829 ****
* does not have LoggingPermission("control").
*/
public void setLevel(Level newLevel) throws SecurityException {
checkPermission();
synchronized (treeLock) {
! levelObject = newLevel;
updateEffectiveLevel();
}
}
final boolean isLevelInitialized() {
! return levelObject != null;
}
/**
* Get the log Level that has been specified for this Logger.
* The result may be null, which means that this logger's
* effective level will be inherited from its parent.
*
* @return this Logger's level
*/
public Level getLevel() {
! return levelObject;
}
/**
* Check if a message of the given level would actually be logged
* by this logger. This check is based on the Loggers effective level,
--- 1994,2021 ----
* does not have LoggingPermission("control").
*/
public void setLevel(Level newLevel) throws SecurityException {
checkPermission();
synchronized (treeLock) {
! config.setLevelObject(newLevel);
updateEffectiveLevel();
}
}
final boolean isLevelInitialized() {
! return config.levelObject != null;
}
/**
* Get the log Level that has been specified for this Logger.
* The result may be null, which means that this logger's
* effective level will be inherited from its parent.
*
* @return this Logger's level
*/
public Level getLevel() {
! return config.levelObject;
}
/**
* Check if a message of the given level would actually be logged
* by this logger. This check is based on the Loggers effective level,
*** 1831,1840 ****
--- 2023,2033 ----
*
* @param level a message logging level
* @return true if the given message level is currently being logged.
*/
public boolean isLoggable(Level level) {
+ int levelValue = config.levelValue;
if (level.intValue() < levelValue || levelValue == offValue) {
return false;
}
return true;
}
*** 1860,1870 ****
* does not have LoggingPermission("control").
*/
public void addHandler(Handler handler) throws SecurityException {
Objects.requireNonNull(handler);
checkPermission();
! handlers.add(handler);
}
/**
* Remove a log Handler.
* <P>
--- 2053,2063 ----
* does not have LoggingPermission("control").
*/
public void addHandler(Handler handler) throws SecurityException {
Objects.requireNonNull(handler);
checkPermission();
! config.addHandler(handler);
}
/**
* Remove a log Handler.
* <P>
*** 1878,1888 ****
public void removeHandler(Handler handler) throws SecurityException {
checkPermission();
if (handler == null) {
return;
}
! handlers.remove(handler);
}
/**
* Get the Handlers associated with this logger.
*
--- 2071,2081 ----
public void removeHandler(Handler handler) throws SecurityException {
checkPermission();
if (handler == null) {
return;
}
! config.removeHandler(handler);
}
/**
* Get the Handlers associated with this logger.
*
*** 1893,1903 ****
}
// This method should ideally be marked final - but unfortunately
// it needs to be overridden by LogManager.RootLogger
Handler[] accessCheckedHandlers() {
! return handlers.toArray(emptyHandlers);
}
/**
* Specify whether or not this logger should send its output
* to its parent Logger. This means that any LogRecords will
--- 2086,2096 ----
}
// This method should ideally be marked final - but unfortunately
// it needs to be overridden by LogManager.RootLogger
Handler[] accessCheckedHandlers() {
! return config.handlers.toArray(emptyHandlers);
}
/**
* Specify whether or not this logger should send its output
* to its parent Logger. This means that any LogRecords will
*** 1910,1930 ****
* this logger is not anonymous, and the caller
* does not have LoggingPermission("control").
*/
public void setUseParentHandlers(boolean useParentHandlers) {
checkPermission();
! this.useParentHandlers = useParentHandlers;
}
/**
* Discover whether or not this logger is sending its output
* to its parent logger.
*
* @return true if output is to be sent to the logger's parent
*/
public boolean getUseParentHandlers() {
! return useParentHandlers;
}
/**
* Private utility method to map a resource bundle name to an
* actual resource bundle, using a simple one-entry cache.
--- 2103,2123 ----
* this logger is not anonymous, and the caller
* does not have LoggingPermission("control").
*/
public void setUseParentHandlers(boolean useParentHandlers) {
checkPermission();
! config.setUseParentHandlers(useParentHandlers);
}
/**
* Discover whether or not this logger is sending its output
* to its parent logger.
*
* @return true if output is to be sent to the logger's parent
*/
public boolean getUseParentHandlers() {
! return config.useParentHandlers;
}
/**
* Private utility method to map a resource bundle name to an
* actual resource bundle, using a simple one-entry cache.
*** 2254,2280 ****
private void updateEffectiveLevel() {
// assert Thread.holdsLock(treeLock);
// Figure out our current effective level.
int newLevelValue;
if (levelObject != null) {
newLevelValue = levelObject.intValue();
} else {
if (parent != null) {
! newLevelValue = parent.levelValue;
} else {
// This may happen during initialization.
newLevelValue = Level.INFO.intValue();
}
}
// If our effective value hasn't changed, we're done.
! if (levelValue == newLevelValue) {
return;
}
! levelValue = newLevelValue;
// System.err.println("effective level: \"" + getName() + "\" := " + level);
// Recursively update the level on each of our kids.
if (kids != null) {
--- 2447,2475 ----
private void updateEffectiveLevel() {
// assert Thread.holdsLock(treeLock);
// Figure out our current effective level.
int newLevelValue;
+ final ConfigurationData cfg = config;
+ final Level levelObject = cfg.levelObject;
if (levelObject != null) {
newLevelValue = levelObject.intValue();
} else {
if (parent != null) {
! newLevelValue = parent.config.levelValue;
} else {
// This may happen during initialization.
newLevelValue = Level.INFO.intValue();
}
}
// If our effective value hasn't changed, we're done.
! if (cfg.levelValue == newLevelValue) {
return;
}
! cfg.setLevelValue(newLevelValue);
// System.err.println("effective level: \"" + getName() + "\" := " + level);
// Recursively update the level on each of our kids.
if (kids != null) {
< prev index next >