/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/ package org.apache.juli;
/** * Per classloader LogManager implementation. For light debugging, set the system property * <code>org.apache.juli.ClassLoaderLogManager.debug=true</code>. Short configuration information will be sent to * <code>System.err</code>.
*/ publicclass ClassLoaderLogManager extends LogManager {
public ClassLoaderLogManager() { super(); try {
Runtime.getRuntime().addShutdownHook(new Cleaner());
} catch (IllegalStateException ise) { // We are probably already being shutdown. Ignore this error.
}
}
/** * Map containing the classloader information, keyed per classloader. A weak hashmap is used to ensure no * classloader reference is leaked from application redeployment.
*/ protectedfinal Map<ClassLoader, ClassLoaderLogInfo> classLoaderLoggers = new WeakHashMap<>(); // Guarded by this
/** * This prefix is used to allow using prefixes for the properties names of handlers and their subcomponents.
*/ protectedfinal ThreadLocal<String> prefix = new ThreadLocal<>();
/** * Determines if the shutdown hook is used to perform any necessary clean-up such as flushing buffered handlers on * JVM shutdown. Defaults to <code>true</code> but may be set to false if another component ensures that * {@link #shutdown()} is called.
*/ protectedvolatileboolean useShutdownHook = true;
// --------------------------------------------------------- Public Methods
/** * Add the specified logger to the classloader local configuration. * * @param logger The logger to be added
*/
@Override publicsynchronizedboolean addLogger(final Logger logger) {
final String loggerName = logger.getName();
ClassLoader classLoader = getClassLoader();
ClassLoaderLogInfo info = getClassLoaderInfo(classLoader); if (info.loggers.containsKey(loggerName)) { returnfalse;
}
info.loggers.put(loggerName, logger);
// Apply initial level for new logger final String levelString = getProperty(loggerName + ".level"); if (levelString != null) { try {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
logger.setLevel(Level.parse(levelString.trim())); returnnull;
});
} catch (IllegalArgumentException e) { // Leave level set to null
}
}
// Always instantiate parent loggers so that // we can control log categories even during runtime int dotIndex = loggerName.lastIndexOf('.'); if (dotIndex >= 0) { final String parentName = loggerName.substring(0, dotIndex);
Logger.getLogger(parentName);
}
// Set parent logger
Logger parentLogger = node.findParentLogger(); if (parentLogger != null) {
doSetParentLogger(logger, parentLogger);
}
// Tell children we are their new parent
node.setParentLogger(logger);
// Add associated handlers, if any are defined using the .handlers property. // In this case, handlers of the parent logger(s) will not be used
String handlers = getProperty(loggerName + ".handlers"); if (handlers != null) {
logger.setUseParentHandlers(false);
StringTokenizer tok = new StringTokenizer(handlers, ","); while (tok.hasMoreTokens()) {
String handlerName = (tok.nextToken().trim());
Handler handler = null;
ClassLoader current = classLoader; while (current != null) {
info = classLoaderLoggers.get(current); if (info != null) {
handler = info.handlers.get(handlerName); if (handler != null) { break;
}
}
current = current.getParent();
} if (handler != null) {
logger.addHandler(handler);
}
}
}
// Parse useParentHandlers to set if the logger should delegate to its parent. // Unlike java.util.logging, the default is to not delegate if a list of handlers // has been specified for the logger.
String useParentHandlersString = getProperty(loggerName + ".useParentHandlers"); if (Boolean.parseBoolean(useParentHandlersString)) {
logger.setUseParentHandlers(true);
}
returntrue;
}
/** * Get the logger associated with the specified name inside the classloader local configuration. If this returns * null, and the call originated for Logger.getLogger, a new logger with the specified name will be instantiated and * added using addLogger. * * @param name The name of the logger to retrieve
*/
@Override publicsynchronized Logger getLogger(final String name) {
ClassLoader classLoader = getClassLoader(); return getClassLoaderInfo(classLoader).loggers.get(name);
}
/** * Get an enumeration of the logger names currently defined in the classloader local configuration.
*/
@Override publicsynchronized Enumeration<String> getLoggerNames() {
ClassLoader classLoader = getClassLoader(); return Collections.enumeration(getClassLoaderInfo(classLoader).loggers.keySet());
}
/** * Get the value of the specified property in the classloader local configuration. * * @param name The property name
*/
@Override public String getProperty(String name) {
String prefix = this.prefix.get();
String result = null;
// If a prefix is defined look for a prefixed property first if (prefix != null) {
result = findProperty(prefix + name);
}
// If there is no prefix or no property match with the prefix try just // the name if (result == null) {
result = findProperty(name);
}
// Simple property replacement (mostly for folder names) if (result != null) {
result = replace(result);
} return result;
}
privatesynchronized String findProperty(String name) {
ClassLoader classLoader = getClassLoader();
ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
String result = info.props.getProperty(name); // If the property was not found, and the current classloader had no // configuration (property list is empty), look for the parent classloader // properties. if ((result == null) && (info.props.isEmpty())) { if (classLoader != null) {
ClassLoader current = classLoader.getParent(); while (current != null) {
info = classLoaderLoggers.get(current); if (info != null) {
result = info.props.getProperty(name); if ((result != null) || (!info.props.isEmpty())) { break;
}
}
current = current.getParent();
}
} if (result == null) {
result = super.getProperty(name);
}
} return result;
}
@Override publicsynchronizedvoid reset() throws SecurityException { Threadthread = Thread.currentThread(); if (thread.getClass().getName().startsWith("java.util.logging.LogManager$")) { // Ignore the call from java.util.logging.LogManager.Cleaner, // because we have our own shutdown hook return;
}
ClassLoader classLoader = getClassLoader();
ClassLoaderLogInfo clLogInfo = getClassLoaderInfo(classLoader);
resetLoggers(clLogInfo); // Do not call super.reset(). It should be a NO-OP as all loggers should // have been registered via this manager. Very rarely a // ConcurrentModificationException has been seen in the unit tests when // calling super.reset() and that exception could cause the stop of a // web application to fail.
}
/** * Shuts down the logging system.
*/ publicsynchronizedvoid shutdown() { // The JVM is being shutdown. Make sure all loggers for all class // loaders are shutdown for (ClassLoaderLogInfo clLogInfo : classLoaderLoggers.values()) {
resetLoggers(clLogInfo);
}
}
// -------------------------------------------------------- Private Methods privatevoid resetLoggers(ClassLoaderLogInfo clLogInfo) { // This differs from LogManager#resetLogger() in that we close not all // handlers of all loggers, but only those that are present in our // ClassLoaderLogInfo#handlers list. That is because our #addLogger(..) // method can use handlers from the parent class loaders, and closing // handlers that the current class loader does not own would be not // good. for (Logger logger : clLogInfo.loggers.values()) {
Handler[] handlers = logger.getHandlers(); for (Handler handler : handlers) {
logger.removeHandler(handler);
}
} for (Handler handler : clLogInfo.handlers.values()) { try {
handler.close();
} catch (Exception e) { // Ignore
}
}
clLogInfo.handlers.clear();
}
/** * Retrieve the configuration associated with the specified classloader. If it does not exist, it will be created. * If no class loader is specified, the class loader used to load this class is used. * * @param classLoader The class loader for which we will retrieve or build the configuration * * @return the log configuration
*/ protectedsynchronized ClassLoaderLogInfo getClassLoaderInfo(ClassLoader classLoader) {
if (classLoader == null) {
classLoader = this.getClass().getClassLoader();
}
ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader); if (info == null) { final ClassLoader classLoaderParam = classLoader;
AccessController.doPrivileged((PrivilegedAction<Void>) () -> { try {
readConfiguration(classLoaderParam);
} catch (IOException e) { // Ignore
} returnnull;
});
info = classLoaderLoggers.get(classLoader);
} return info;
}
/** * Read configuration for the specified classloader. * * @param classLoader The classloader * * @throws IOException Error reading configuration
*/ protectedsynchronizedvoid readConfiguration(ClassLoader classLoader) throws IOException {
InputStream is = null; // Special case for URL classloaders which are used in containers: // only look in the local repositories to avoid redefining loggers 20 times try { if (classLoader instanceof WebappProperties) { if (((WebappProperties) classLoader).hasLoggingConfig()) {
is = classLoader.getResourceAsStream("logging.properties");
}
} elseif (classLoader instanceof URLClassLoader) {
URL logConfig = ((URLClassLoader) classLoader).findResource("logging.properties");
if (null != logConfig) { if (Boolean.getBoolean(DEBUG_PROPERTY)) {
System.err.println(getClass().getName() + ".readConfiguration(): " + "Found logging.properties at " + logConfig);
}
is = classLoader.getResourceAsStream("logging.properties");
} else { if (Boolean.getBoolean(DEBUG_PROPERTY)) {
System.err.println(
getClass().getName() + ".readConfiguration(): " + "Found no logging.properties");
}
}
}
} catch (AccessControlException ace) { // No permission to configure logging in context // Log and carry on
ClassLoaderLogInfo info = classLoaderLoggers.get(ClassLoader.getSystemClassLoader()); if (info != null) {
Logger log = info.loggers.get(""); if (log != null) {
Permission perm = ace.getPermission(); if (perm instanceof FilePermission && perm.getActions().equals("read")) {
log.warning("Reading " + perm.getName() + " is not permitted. See \"per context logging\" in the default catalina.policy file.");
} else {
log.warning( "Reading logging.properties is not permitted in some context. See \"per context logging\" in the default catalina.policy file.");
log.warning("Original error was: " + ace.getMessage());
}
}
}
} if ((is == null) && (classLoader == ClassLoader.getSystemClassLoader())) {
String configFileStr = System.getProperty("java.util.logging.config.file"); if (configFileStr != null) { try {
is = new FileInputStream(replace(configFileStr));
} catch (IOException e) {
System.err.println("Configuration error");
e.printStackTrace();
}
} // Try the default JVM configuration if (is == null) {
File defaultFile = new File(new File(System.getProperty("java.home"), "conf"), "logging.properties"); try {
is = new FileInputStream(defaultFile);
} catch (IOException e) {
System.err.println("Configuration error");
e.printStackTrace();
}
}
}
Logger localRootLogger = new RootLogger(); if (is == null) { // Retrieve the root logger of the parent classloader instead
ClassLoader current = classLoader.getParent();
ClassLoaderLogInfo info = null; while (current != null && info == null) {
info = getClassLoaderInfo(current);
current = current.getParent();
} if (info != null) {
localRootLogger.setParent(info.rootNode.logger);
}
}
ClassLoaderLogInfo info = new ClassLoaderLogInfo(new LogNode(null, localRootLogger));
classLoaderLoggers.put(classLoader, info);
if (is != null) {
readConfiguration(is, classLoader);
}
if (localRootLogger.getParent() == null && localRootLogger.getLevel() == null) {
localRootLogger.setLevel(Level.INFO);
} try { // Use a ThreadLocal to work around // https://bugs.openjdk.java.net/browse/JDK-8195096
addingLocalRootLogger.set(Boolean.TRUE);
addLogger(localRootLogger);
} finally {
addingLocalRootLogger.set(Boolean.FALSE);
}
}
/** * Load specified configuration. * * @param is InputStream to the properties file * @param classLoader for which the configuration will be loaded * * @throws IOException If something wrong happens during loading
*/ protectedsynchronizedvoid readConfiguration(InputStream is, ClassLoader classLoader) throws IOException {
ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader);
/** * Obtain the class loader to use to lookup loggers, obtain configuration etc. The search order is: * <ol> * <li>Thread.currentThread().getContextClassLoader()</li> * <li>The class laoder of this class</li> * </ol> * * @return The class loader to use to lookup loggers, obtain configuration etc.
*/ static ClassLoader getClassLoader() {
ClassLoader result = Thread.currentThread().getContextClassLoader(); if (result == null) {
result = ClassLoaderLogManager.class.getClassLoader();
} return result;
}
// ---------------------------------------------------- LogNode Inner Class
// -------------------------------------------- ClassLoaderInfo Inner Class
protectedstaticfinalclass ClassLoaderLogInfo { final LogNode rootNode; final Map<String, Logger> loggers = new ConcurrentHashMap<>(); final Map<String, Handler> handlers = new HashMap<>(); final Properties props = new Properties();
// ------------------------------------------------- RootLogger Inner Class
/** * This class is needed to instantiate the root of each per classloader hierarchy.
*/ protectedstaticclass RootLogger extends Logger { public RootLogger() { super("", null);
}
}
}
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.