diff options
author | Jason Tedor <jason@tedor.me> | 2016-10-07 13:05:30 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-07 13:05:30 -0500 |
commit | 535bf922807b6cb62cd5e5b6df9b1c31a15a4b1b (patch) | |
tree | c8da35d7acfb30e13b6a4a455a80aba8c8369442 /core | |
parent | 2fa1d0d5f914db28b796e85eddbe3a8652156032 (diff) |
Upgrade Log4j 2 to version 2.7
This commit upgrades the Log4j 2 dependency to version 2.7 and removes
some hacks that we had in place to work around bugs in Log4j 2 version
2.6.2.
Relates #20805
Diffstat (limited to 'core')
6 files changed, 11 insertions, 1084 deletions
diff --git a/core/build.gradle b/core/build.gradle index ea2e3e27ef..4f7bec337a 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -158,6 +158,10 @@ thirdPartyAudit.excludes = [ 'com.fasterxml.jackson.databind.ObjectMapper', // from log4j + 'com.beust.jcommander.IStringConverter', + 'com.beust.jcommander.JCommander', + 'com.conversantmedia.util.concurrent.DisruptorBlockingQueue', + 'com.conversantmedia.util.concurrent.SpinPolicy', 'com.fasterxml.jackson.annotation.JsonInclude$Include', 'com.fasterxml.jackson.databind.DeserializationContext', 'com.fasterxml.jackson.databind.JsonMappingException', @@ -176,6 +180,10 @@ thirdPartyAudit.excludes = [ 'com.fasterxml.jackson.dataformat.xml.JacksonXmlModule', 'com.fasterxml.jackson.dataformat.xml.XmlMapper', 'com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter', + 'com.fasterxml.jackson.databind.node.JsonNodeFactory', + 'com.fasterxml.jackson.databind.node.ObjectNode', + 'org.fusesource.jansi.Ansi', + 'org.fusesource.jansi.AnsiRenderer$Code', 'com.lmax.disruptor.BlockingWaitStrategy', 'com.lmax.disruptor.BusySpinWaitStrategy', 'com.lmax.disruptor.EventFactory', @@ -228,6 +236,8 @@ thirdPartyAudit.excludes = [ 'org.apache.kafka.clients.producer.Producer', 'org.apache.kafka.clients.producer.ProducerRecord', 'org.codehaus.stax2.XMLStreamWriter2', + 'org.jctools.queues.MessagePassingQueue$Consumer', + 'org.jctools.queues.MpscArrayQueue', 'org.osgi.framework.AdaptPermission', 'org.osgi.framework.AdminPermission', 'org.osgi.framework.Bundle', diff --git a/core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java b/core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java deleted file mode 100644 index 37ab0a1539..0000000000 --- a/core/src/main/java/org/apache/logging/log4j/core/impl/ThrowableProxy.java +++ /dev/null @@ -1,665 +0,0 @@ -/* - * 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.logging.log4j.core.impl; - -import java.io.Serializable; -import java.net.URL; -import java.security.CodeSource; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import org.apache.logging.log4j.core.util.Loader; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.ReflectionUtil; -import org.apache.logging.log4j.util.Strings; - -/** - * Wraps a Throwable to add packaging information about each stack trace element. - * - * <p> - * A proxy is used to represent a throwable that may not exist in a different class loader or JVM. When an application - * deserializes a ThrowableProxy, the throwable may not be set, but the throwable's information is preserved in other - * fields of the proxy like the message and stack trace. - * </p> - * - * <p> - * TODO: Move this class to org.apache.logging.log4j.core because it is used from LogEvent. - * </p> - * <p> - * TODO: Deserialize: Try to rebuild Throwable if the target exception is in this class loader? - * </p> - */ -public class ThrowableProxy implements Serializable { - - private static final String CAUSED_BY_LABEL = "Caused by: "; - private static final String SUPPRESSED_LABEL = "Suppressed: "; - private static final String WRAPPED_BY_LABEL = "Wrapped by: "; - - /** - * Cached StackTracePackageElement and ClassLoader. - * <p> - * Consider this class private. - * </p> - */ - static class CacheEntry { - private final ExtendedClassInfo element; - private final ClassLoader loader; - - public CacheEntry(final ExtendedClassInfo element, final ClassLoader loader) { - this.element = element; - this.loader = loader; - } - } - - private static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0]; - - private static final char EOL = '\n'; - - private static final long serialVersionUID = -2752771578252251910L; - - private final ThrowableProxy causeProxy; - - private int commonElementCount; - - private final ExtendedStackTraceElement[] extendedStackTrace; - - private final String localizedMessage; - - private final String message; - - private final String name; - - private final ThrowableProxy[] suppressedProxies; - - private final transient Throwable throwable; - - /** - * For JSON and XML IO via Jackson. - */ - @SuppressWarnings("unused") - private ThrowableProxy() { - this.throwable = null; - this.name = null; - this.extendedStackTrace = null; - this.causeProxy = null; - this.message = null; - this.localizedMessage = null; - this.suppressedProxies = EMPTY_THROWABLE_PROXY_ARRAY; - } - - /** - * Constructs the wrapper for the Throwable that includes packaging data. - * - * @param throwable - * The Throwable to wrap, must not be null. - */ - public ThrowableProxy(final Throwable throwable) { - this(throwable, null); - } - - /** - * Constructs the wrapper for the Throwable that includes packaging data. - * - * @param throwable - * The Throwable to wrap, must not be null. - * @param visited - * The set of visited suppressed exceptions. - */ - private ThrowableProxy(final Throwable throwable, final Set<Throwable> visited) { - this.throwable = throwable; - this.name = throwable.getClass().getName(); - this.message = throwable.getMessage(); - this.localizedMessage = throwable.getLocalizedMessage(); - final Map<String, CacheEntry> map = new HashMap<>(); - final Stack<Class<?>> stack = ReflectionUtil.getCurrentStackTrace(); - this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace()); - final Throwable throwableCause = throwable.getCause(); - final Set<Throwable> causeVisited = new HashSet<>(1); - this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause, visited, causeVisited); - this.suppressedProxies = this.toSuppressedProxies(throwable, visited); - } - - /** - * Constructs the wrapper for a Throwable that is referenced as the cause by another Throwable. - * - * @param parent - * The Throwable referencing this Throwable. - * @param stack - * The Class stack. - * @param map - * The cache containing the packaging data. - * @param cause - * The Throwable to wrap. - * @param suppressedVisited TODO - * @param causeVisited TODO - */ - private ThrowableProxy(final Throwable parent, final Stack<Class<?>> stack, final Map<String, CacheEntry> map, - final Throwable cause, final Set<Throwable> suppressedVisited, final Set<Throwable> causeVisited) { - causeVisited.add(cause); - this.throwable = cause; - this.name = cause.getClass().getName(); - this.message = this.throwable.getMessage(); - this.localizedMessage = this.throwable.getLocalizedMessage(); - this.extendedStackTrace = this.toExtendedStackTrace(stack, map, parent.getStackTrace(), cause.getStackTrace()); - final Throwable causeCause = cause.getCause(); - this.causeProxy = causeCause == null || causeVisited.contains(causeCause) ? null : new ThrowableProxy(parent, - stack, map, causeCause, suppressedVisited, causeVisited); - this.suppressedProxies = this.toSuppressedProxies(cause, suppressedVisited); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (this.getClass() != obj.getClass()) { - return false; - } - final ThrowableProxy other = (ThrowableProxy) obj; - if (this.causeProxy == null) { - if (other.causeProxy != null) { - return false; - } - } else if (!this.causeProxy.equals(other.causeProxy)) { - return false; - } - if (this.commonElementCount != other.commonElementCount) { - return false; - } - if (this.name == null) { - if (other.name != null) { - return false; - } - } else if (!this.name.equals(other.name)) { - return false; - } - if (!Arrays.equals(this.extendedStackTrace, other.extendedStackTrace)) { - return false; - } - if (!Arrays.equals(this.suppressedProxies, other.suppressedProxies)) { - return false; - } - return true; - } - - private void formatCause(final StringBuilder sb, final String prefix, final ThrowableProxy cause, final List<String> ignorePackages) { - formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, ignorePackages); - } - - private void formatThrowableProxy(final StringBuilder sb, final String prefix, final String causeLabel, - final ThrowableProxy throwableProxy, final List<String> ignorePackages) { - if (throwableProxy == null) { - return; - } - sb.append(prefix).append(causeLabel).append(throwableProxy).append(EOL); - this.formatElements(sb, prefix, throwableProxy.commonElementCount, - throwableProxy.getStackTrace(), throwableProxy.extendedStackTrace, ignorePackages); - this.formatSuppressed(sb, prefix + "\t", throwableProxy.suppressedProxies, ignorePackages); - this.formatCause(sb, prefix, throwableProxy.causeProxy, ignorePackages); - } - - private void formatSuppressed(final StringBuilder sb, final String prefix, final ThrowableProxy[] suppressedProxies, - final List<String> ignorePackages) { - if (suppressedProxies == null) { - return; - } - for (final ThrowableProxy suppressedProxy : suppressedProxies) { - final ThrowableProxy cause = suppressedProxy; - formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, cause, ignorePackages); - } - } - - private void formatElements(final StringBuilder sb, final String prefix, final int commonCount, - final StackTraceElement[] causedTrace, final ExtendedStackTraceElement[] extStackTrace, - final List<String> ignorePackages) { - if (ignorePackages == null || ignorePackages.isEmpty()) { - for (final ExtendedStackTraceElement element : extStackTrace) { - this.formatEntry(element, sb, prefix); - } - } else { - int count = 0; - for (int i = 0; i < extStackTrace.length; ++i) { - if (!this.ignoreElement(causedTrace[i], ignorePackages)) { - if (count > 0) { - appendSuppressedCount(sb, prefix, count); - count = 0; - } - this.formatEntry(extStackTrace[i], sb, prefix); - } else { - ++count; - } - } - if (count > 0) { - appendSuppressedCount(sb, prefix, count); - } - } - if (commonCount != 0) { - sb.append(prefix).append("\t... ").append(commonCount).append(" more").append(EOL); - } - } - - private void appendSuppressedCount(final StringBuilder sb, final String prefix, final int count) { - sb.append(prefix); - if (count == 1) { - sb.append("\t....").append(EOL); - } else { - sb.append("\t... suppressed ").append(count).append(" lines").append(EOL); - } - } - - private void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb, final String prefix) { - sb.append(prefix); - sb.append("\tat "); - sb.append(extStackTraceElement); - sb.append(EOL); - } - - /** - * Formats the specified Throwable. - * - * @param sb - * StringBuilder to contain the formatted Throwable. - * @param cause - * The Throwable to format. - */ - public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause) { - this.formatWrapper(sb, cause, null); - } - - /** - * Formats the specified Throwable. - * - * @param sb - * StringBuilder to contain the formatted Throwable. - * @param cause - * The Throwable to format. - * @param packages - * The List of packages to be suppressed from the trace. - */ - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> packages) { - final Throwable caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null; - if (caused != null) { - this.formatWrapper(sb, cause.causeProxy); - sb.append(WRAPPED_BY_LABEL); - } - sb.append(cause).append(EOL); - this.formatElements(sb, "", cause.commonElementCount, - cause.getThrowable().getStackTrace(), cause.extendedStackTrace, packages); - } - - public ThrowableProxy getCauseProxy() { - return this.causeProxy; - } - - /** - * Format the Throwable that is the cause of this Throwable. - * - * @return The formatted Throwable that caused this Throwable. - */ - public String getCauseStackTraceAsString() { - return this.getCauseStackTraceAsString(null); - } - - /** - * Format the Throwable that is the cause of this Throwable. - * - * @param packages - * The List of packages to be suppressed from the trace. - * @return The formatted Throwable that caused this Throwable. - */ - public String getCauseStackTraceAsString(final List<String> packages) { - final StringBuilder sb = new StringBuilder(); - if (this.causeProxy != null) { - this.formatWrapper(sb, this.causeProxy); - sb.append(WRAPPED_BY_LABEL); - } - sb.append(this.toString()); - sb.append(EOL); - this.formatElements(sb, "", 0, this.throwable.getStackTrace(), this.extendedStackTrace, packages); - return sb.toString(); - } - - /** - * Return the number of elements that are being omitted because they are common with the parent Throwable's stack - * trace. - * - * @return The number of elements omitted from the stack trace. - */ - public int getCommonElementCount() { - return this.commonElementCount; - } - - /** - * Gets the stack trace including packaging information. - * - * @return The stack trace including packaging information. - */ - public ExtendedStackTraceElement[] getExtendedStackTrace() { - return this.extendedStackTrace; - } - - /** - * Format the stack trace including packaging information. - * - * @return The formatted stack trace including packaging information. - */ - public String getExtendedStackTraceAsString() { - return this.getExtendedStackTraceAsString(null); - } - - /** - * Format the stack trace including packaging information. - * - * @param ignorePackages - * List of packages to be ignored in the trace. - * @return The formatted stack trace including packaging information. - */ - public String getExtendedStackTraceAsString(final List<String> ignorePackages) { - final StringBuilder sb = new StringBuilder(this.name); - final String msg = this.message; - if (msg != null) { - sb.append(": ").append(msg); - } - sb.append(EOL); - final StackTraceElement[] causedTrace = this.throwable != null ? this.throwable.getStackTrace() : null; - this.formatElements(sb, "", 0, causedTrace, this.extendedStackTrace, ignorePackages); - this.formatSuppressed(sb, "\t", this.suppressedProxies, ignorePackages); - this.formatCause(sb, "", this.causeProxy, ignorePackages); - return sb.toString(); - } - - public String getLocalizedMessage() { - return this.localizedMessage; - } - - public String getMessage() { - return this.message; - } - - /** - * Return the FQCN of the Throwable. - * - * @return The FQCN of the Throwable. - */ - public String getName() { - return this.name; - } - - public StackTraceElement[] getStackTrace() { - return this.throwable == null ? null : this.throwable.getStackTrace(); - } - - /** - * Gets proxies for suppressed exceptions. - * - * @return proxies for suppressed exceptions. - */ - public ThrowableProxy[] getSuppressedProxies() { - return this.suppressedProxies; - } - - /** - * Format the suppressed Throwables. - * - * @return The formatted suppressed Throwables. - */ - public String getSuppressedStackTrace() { - final ThrowableProxy[] suppressed = this.getSuppressedProxies(); - if (suppressed == null || suppressed.length == 0) { - return Strings.EMPTY; - } - final StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:").append(EOL); - for (final ThrowableProxy proxy : suppressed) { - sb.append(proxy.getExtendedStackTraceAsString()); - } - return sb.toString(); - } - - /** - * The throwable or null if this object is deserialized from XML or JSON. - * - * @return The throwable or null if this object is deserialized from XML or JSON. - */ - public Throwable getThrowable() { - return this.throwable; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (this.causeProxy == null ? 0 : this.causeProxy.hashCode()); - result = prime * result + this.commonElementCount; - result = prime * result + (this.extendedStackTrace == null ? 0 : Arrays.hashCode(this.extendedStackTrace)); - result = prime * result + (this.suppressedProxies == null ? 0 : Arrays.hashCode(this.suppressedProxies)); - result = prime * result + (this.name == null ? 0 : this.name.hashCode()); - return result; - } - - private boolean ignoreElement(final StackTraceElement element, final List<String> ignorePackages) { - final String className = element.getClassName(); - for (final String pkg : ignorePackages) { - if (className.startsWith(pkg)) { - return true; - } - } - return false; - } - - /** - * Loads classes not located via Reflection.getCallerClass. - * - * @param lastLoader - * The ClassLoader that loaded the Class that called this Class. - * @param className - * The name of the Class. - * @return The Class object for the Class or null if it could not be located. - */ - private Class<?> loadClass(final ClassLoader lastLoader, final String className) { - // XXX: this is overly complicated - Class<?> clazz; - if (lastLoader != null) { - try { - clazz = Loader.initializeClass(className, lastLoader); - if (clazz != null) { - return clazz; - } - } catch (final Throwable ignore) { - // Ignore exception. - } - } - try { - clazz = Loader.loadClass(className); - } catch (final ClassNotFoundException ignored) { - return initializeClass(className); - } catch (final NoClassDefFoundError ignored) { - return initializeClass(className); - } catch (final SecurityException ignored) { - return initializeClass(className); - } - return clazz; - } - - private Class<?> initializeClass(final String className) { - try { - return Loader.initializeClass(className, this.getClass().getClassLoader()); - } catch (final ClassNotFoundException ignore) { - return null; - } catch (final NoClassDefFoundError ignore) { - return null; - } catch (final SecurityException ignore) { - return null; - } - } - - /** - * Construct the CacheEntry from the Class's information. - * - * @param stackTraceElement - * The stack trace element - * @param callerClass - * The Class. - * @param exact - * True if the class was obtained via Reflection.getCallerClass. - * - * @return The CacheEntry. - */ - private CacheEntry toCacheEntry(final StackTraceElement stackTraceElement, final Class<?> callerClass, - final boolean exact) { - String location = "?"; - String version = "?"; - ClassLoader lastLoader = null; - if (callerClass != null) { - try { - final CodeSource source = callerClass.getProtectionDomain().getCodeSource(); - if (source != null) { - final URL locationURL = source.getLocation(); - if (locationURL != null) { - final String str = locationURL.toString().replace('\\', '/'); - int index = str.lastIndexOf("/"); - if (index >= 0 && index == str.length() - 1) { - index = str.lastIndexOf("/", index - 1); - location = str.substring(index + 1); - } else { - location = str.substring(index + 1); - } - } - } - } catch (final Exception ex) { - // Ignore the exception. - } - final Package pkg = callerClass.getPackage(); - if (pkg != null) { - final String ver = pkg.getImplementationVersion(); - if (ver != null) { - version = ver; - } - } - lastLoader = callerClass.getClassLoader(); - } - return new CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader); - } - - /** - * Resolve all the stack entries in this stack trace that are not common with the parent. - * - * @param stack - * The callers Class stack. - * @param map - * The cache of CacheEntry objects. - * @param rootTrace - * The first stack trace resolve or null. - * @param stackTrace - * The stack trace being resolved. - * @return The StackTracePackageElement array. - */ - ExtendedStackTraceElement[] toExtendedStackTrace(final Stack<Class<?>> stack, final Map<String, CacheEntry> map, - final StackTraceElement[] rootTrace, final StackTraceElement[] stackTrace) { - int stackLength; - if (rootTrace != null) { - int rootIndex = rootTrace.length - 1; - int stackIndex = stackTrace.length - 1; - while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) { - --rootIndex; - --stackIndex; - } - this.commonElementCount = stackTrace.length - 1 - stackIndex; - stackLength = stackIndex + 1; - } else { - this.commonElementCount = 0; - stackLength = stackTrace.length; - } - final ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength]; - Class<?> clazz = stack.isEmpty() ? null : stack.peek(); - ClassLoader lastLoader = null; - for (int i = stackLength - 1; i >= 0; --i) { - final StackTraceElement stackTraceElement = stackTrace[i]; - final String className = stackTraceElement.getClassName(); - // The stack returned from getCurrentStack may be missing entries for java.lang.reflect.Method.invoke() - // and its implementation. The Throwable might also contain stack entries that are no longer - // present as those methods have returned. - ExtendedClassInfo extClassInfo; - if (clazz != null && className.equals(clazz.getName())) { - final CacheEntry entry = this.toCacheEntry(stackTraceElement, clazz, true); - extClassInfo = entry.element; - lastLoader = entry.loader; - stack.pop(); - clazz = stack.isEmpty() ? null : stack.peek(); - } else { - final CacheEntry cacheEntry = map.get(className); - if (cacheEntry != null) { - final CacheEntry entry = cacheEntry; - extClassInfo = entry.element; - if (entry.loader != null) { - lastLoader = entry.loader; - } - } else { - final CacheEntry entry = this.toCacheEntry(stackTraceElement, - this.loadClass(lastLoader, className), false); - extClassInfo = entry.element; - map.put(stackTraceElement.toString(), entry); - if (entry.loader != null) { - lastLoader = entry.loader; - } - } - } - extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo); - } - return extStackTrace; - } - - @Override - public String toString() { - final String msg = this.message; - return msg != null ? this.name + ": " + msg : this.name; - } - - private ThrowableProxy[] toSuppressedProxies(final Throwable thrown, Set<Throwable> suppressedVisited) { - try { - final Throwable[] suppressed = thrown.getSuppressed(); - if (suppressed == null) { - return EMPTY_THROWABLE_PROXY_ARRAY; - } - final List<ThrowableProxy> proxies = new ArrayList<>(suppressed.length); - if (suppressedVisited == null) { - suppressedVisited = new HashSet<>(proxies.size()); - } - for (int i = 0; i < suppressed.length; i++) { - final Throwable candidate = suppressed[i]; - if (!suppressedVisited.contains(candidate)) { - suppressedVisited.add(candidate); - proxies.add(new ThrowableProxy(candidate, suppressedVisited)); - } - } - return proxies.toArray(new ThrowableProxy[proxies.size()]); - } catch (final Exception e) { - StatusLogger.getLogger().error(e); - } - return null; - } -} diff --git a/core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java b/core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java deleted file mode 100644 index 8edf959a4f..0000000000 --- a/core/src/main/java/org/apache/logging/log4j/core/jmx/Server.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * 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.logging.log4j.core.jmx; - -import java.lang.management.ManagementFactory; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import javax.management.InstanceAlreadyExistsException; -import javax.management.MBeanRegistrationException; -import javax.management.MBeanServer; -import javax.management.NotCompliantMBeanException; -import javax.management.ObjectName; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.appender.AsyncAppender; -import org.apache.logging.log4j.core.async.AsyncLoggerConfig; -import org.apache.logging.log4j.core.async.AsyncLoggerContext; -import org.apache.logging.log4j.core.async.DaemonThreadFactory; -import org.apache.logging.log4j.core.config.LoggerConfig; -import org.apache.logging.log4j.core.impl.Log4jContextFactory; -import org.apache.logging.log4j.core.selector.ContextSelector; -import org.apache.logging.log4j.core.util.Constants; -import org.apache.logging.log4j.spi.LoggerContextFactory; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.PropertiesUtil; -import org.elasticsearch.common.SuppressForbidden; - -/** - * Creates MBeans to instrument various classes in the log4j class hierarchy. - * <p> - * All instrumentation for Log4j 2 classes can be disabled by setting system property {@code -Dlog4j2.disable.jmx=true}. - * </p> - */ -@SuppressForbidden(reason = "copied class to hack around Log4j bug") -public final class Server { - - /** - * The domain part, or prefix ({@value}) of the {@code ObjectName} of all MBeans that instrument Log4J2 components. - */ - public static final String DOMAIN = "org.apache.logging.log4j2"; - private static final String PROPERTY_DISABLE_JMX = "log4j2.disable.jmx"; - private static final String PROPERTY_ASYNC_NOTIF = "log4j2.jmx.notify.async"; - private static final String THREAD_NAME_PREFIX = "log4j2.jmx.notif"; - private static final StatusLogger LOGGER = StatusLogger.getLogger(); - static final Executor executor = isJmxDisabled() ? null : createExecutor(); - - private Server() { - } - - /** - * Returns either a {@code null} Executor (causing JMX notifications to be sent from the caller thread) or a daemon - * background thread Executor, depending on the value of system property "log4j2.jmx.notify.async". If this - * property is not set, use a {@code null} Executor for web apps to avoid memory leaks and other issues when the - * web app is restarted. - * @see <a href="https://issues.apache.org/jira/browse/LOG4J2-938">LOG4J2-938</a> - */ - private static ExecutorService createExecutor() { - final boolean defaultAsync = !Constants.IS_WEB_APP; - final boolean async = PropertiesUtil.getProperties().getBooleanProperty(PROPERTY_ASYNC_NOTIF, defaultAsync); - return async ? Executors.newFixedThreadPool(1, new DaemonThreadFactory(THREAD_NAME_PREFIX)) : null; - } - - /** - * Either returns the specified name as is, or returns a quoted value containing the specified name with the special - * characters (comma, equals, colon, quote, asterisk, or question mark) preceded with a backslash. - * - * @param name the name to escape so it can be used as a value in an {@link ObjectName}. - * @return the escaped name - */ - public static String escape(final String name) { - final StringBuilder sb = new StringBuilder(name.length() * 2); - boolean needsQuotes = false; - for (int i = 0; i < name.length(); i++) { - final char c = name.charAt(i); - switch (c) { - case '\\': - case '*': - case '?': - case '\"': - // quote, star, question & backslash must be escaped - sb.append('\\'); - needsQuotes = true; // ... and can only appear in quoted value - break; - case ',': - case '=': - case ':': - // no need to escape these, but value must be quoted - needsQuotes = true; - break; - case '\r': - // drop \r characters: \\r gives "invalid escape sequence" - continue; - case '\n': - // replace \n characters with \\n sequence - sb.append("\\n"); - needsQuotes = true; - continue; - } - sb.append(c); - } - if (needsQuotes) { - sb.insert(0, '\"'); - sb.append('\"'); - } - return sb.toString(); - } - - private static boolean isJmxDisabled() { - return PropertiesUtil.getProperties().getBooleanProperty(PROPERTY_DISABLE_JMX); - } - - public static void reregisterMBeansAfterReconfigure() { - // avoid creating Platform MBean Server if JMX disabled - if (isJmxDisabled()) { - LOGGER.debug("JMX disabled for log4j2. Not registering MBeans."); - return; - } - final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - reregisterMBeansAfterReconfigure(mbs); - } - - public static void reregisterMBeansAfterReconfigure(final MBeanServer mbs) { - if (isJmxDisabled()) { - LOGGER.debug("JMX disabled for log4j2. Not registering MBeans."); - return; - } - - // now provide instrumentation for the newly configured - // LoggerConfigs and Appenders - try { - final ContextSelector selector = getContextSelector(); - if (selector == null) { - LOGGER.debug("Could not register MBeans: no ContextSelector found."); - return; - } - LOGGER.trace("Reregistering MBeans after reconfigure. Selector={}", selector); - final List<LoggerContext> contexts = selector.getLoggerContexts(); - int i = 0; - for (final LoggerContext ctx : contexts) { - LOGGER.trace("Reregistering context ({}/{}): '{}' {}", ++i, contexts.size(), ctx.getName(), ctx); - // first unregister the context and all nested loggers, - // appenders, statusLogger, contextSelector, ringbuffers... - unregisterLoggerContext(ctx.getName(), mbs); - - final LoggerContextAdmin mbean = new LoggerContextAdmin(ctx, executor); - register(mbs, mbean, mbean.getObjectName()); - - if (ctx instanceof AsyncLoggerContext) { - final RingBufferAdmin rbmbean = ((AsyncLoggerContext) ctx).createRingBufferAdmin(); - if (rbmbean.getBufferSize() > 0) { - // don't register if Disruptor not started (DefaultConfiguration: config not found) - register(mbs, rbmbean, rbmbean.getObjectName()); - } - } - - // register the status logger and the context selector - // repeatedly - // for each known context: if one context is unregistered, - // these MBeans should still be available for the other - // contexts. - registerStatusLogger(ctx.getName(), mbs, executor); - registerContextSelector(ctx.getName(), selector, mbs, executor); - - registerLoggerConfigs(ctx, mbs, executor); - registerAppenders(ctx, mbs, executor); - } - } catch (final Exception ex) { - LOGGER.error("Could not register mbeans", ex); - } - } - - /** - * Unregister all log4j MBeans from the platform MBean server. - */ - public static void unregisterMBeans() { - if (isJmxDisabled()) { - LOGGER.debug("JMX disabled for Log4j2. Not unregistering MBeans."); - return; - } - final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - unregisterMBeans(mbs); - } - - /** - * Unregister all log4j MBeans from the specified MBean server. - * - * @param mbs the MBean server to unregister from. - */ - public static void unregisterMBeans(final MBeanServer mbs) { - unregisterStatusLogger("*", mbs); - unregisterContextSelector("*", mbs); - unregisterContexts(mbs); - unregisterLoggerConfigs("*", mbs); - unregisterAsyncLoggerRingBufferAdmins("*", mbs); - unregisterAsyncLoggerConfigRingBufferAdmins("*", mbs); - unregisterAppenders("*", mbs); - unregisterAsyncAppenders("*", mbs); - } - - /** - * Returns the {@code ContextSelector} of the current {@code Log4jContextFactory}. - * - * @return the {@code ContextSelector} of the current {@code Log4jContextFactory} - */ - private static ContextSelector getContextSelector() { - final LoggerContextFactory factory = LogManager.getFactory(); - if (factory instanceof Log4jContextFactory) { - final ContextSelector selector = ((Log4jContextFactory) factory).getSelector(); - return selector; - } - return null; - } - - /** - * Unregisters all MBeans associated with the specified logger context (including MBeans for {@code LoggerConfig}s - * and {@code Appender}s from the platform MBean server. - * - * @param loggerContextName name of the logger context to unregister - */ - public static void unregisterLoggerContext(final String loggerContextName) { - if (isJmxDisabled()) { - LOGGER.debug("JMX disabled for Log4j2. Not unregistering MBeans."); - return; - } - final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - unregisterLoggerContext(loggerContextName, mbs); - } - - /** - * Unregisters all MBeans associated with the specified logger context (including MBeans for {@code LoggerConfig}s - * and {@code Appender}s from the platform MBean server. - * - * @param contextName name of the logger context to unregister - * @param mbs the MBean Server to unregister the instrumented objects from - */ - public static void unregisterLoggerContext(final String contextName, final MBeanServer mbs) { - final String pattern = LoggerContextAdminMBean.PATTERN; - final String search = String.format(pattern, escape(contextName), "*"); - unregisterAllMatching(search, mbs); // unregister context mbean - - // now unregister all MBeans associated with this logger context - unregisterStatusLogger(contextName, mbs); - unregisterContextSelector(contextName, mbs); - unregisterLoggerConfigs(contextName, mbs); - unregisterAppenders(contextName, mbs); - unregisterAsyncAppenders(contextName, mbs); - unregisterAsyncLoggerRingBufferAdmins(contextName, mbs); - unregisterAsyncLoggerConfigRingBufferAdmins(contextName, mbs); - } - - private static void registerStatusLogger(final String contextName, final MBeanServer mbs, final Executor executor) - throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { - - final StatusLoggerAdmin mbean = new StatusLoggerAdmin(contextName, executor); - register(mbs, mbean, mbean.getObjectName()); - } - - private static void registerContextSelector(final String contextName, final ContextSelector selector, - final MBeanServer mbs, final Executor executor) throws InstanceAlreadyExistsException, - MBeanRegistrationException, NotCompliantMBeanException { - - final ContextSelectorAdmin mbean = new ContextSelectorAdmin(contextName, selector); - register(mbs, mbean, mbean.getObjectName()); - } - - private static void unregisterStatusLogger(final String contextName, final MBeanServer mbs) { - final String pattern = StatusLoggerAdminMBean.PATTERN; - final String search = String.format(pattern, escape(contextName), "*"); - unregisterAllMatching(search, mbs); - } - - private static void unregisterContextSelector(final String contextName, final MBeanServer mbs) { - final String pattern = ContextSelectorAdminMBean.PATTERN; - final String search = String.format(pattern, escape(contextName), "*"); - unregisterAllMatching(search, mbs); - } - - private static void unregisterLoggerConfigs(final String contextName, final MBeanServer mbs) { - final String pattern = LoggerConfigAdminMBean.PATTERN; - final String search = String.format(pattern, escape(contextName), "*"); - unregisterAllMatching(search, mbs); - } - - private static void unregisterContexts(final MBeanServer mbs) { - final String pattern = LoggerContextAdminMBean.PATTERN; - final String search = String.format(pattern, "*"); - unregisterAllMatching(search, mbs); - } - - private static void unregisterAppenders(final String contextName, final MBeanServer mbs) { - final String pattern = AppenderAdminMBean.PATTERN; - final String search = String.format(pattern, escape(contextName), "*"); - unregisterAllMatching(search, mbs); - } - - private static void unregisterAsyncAppenders(final String contextName, final MBeanServer mbs) { - final String pattern = AsyncAppenderAdminMBean.PATTERN; - final String search = String.format(pattern, escape(contextName), "*"); - unregisterAllMatching(search, mbs); - } - - private static void unregisterAsyncLoggerRingBufferAdmins(final String contextName, final MBeanServer mbs) { - final String pattern1 = RingBufferAdminMBean.PATTERN_ASYNC_LOGGER; - final String search1 = String.format(pattern1, escape(contextName)); - unregisterAllMatching(search1, mbs); - } - - private static void unregisterAsyncLoggerConfigRingBufferAdmins(final String contextName, final MBeanServer mbs) { - final String pattern2 = RingBufferAdminMBean.PATTERN_ASYNC_LOGGER_CONFIG; - final String search2 = String.format(pattern2, escape(contextName), "*"); - unregisterAllMatching(search2, mbs); - } - - private static void unregisterAllMatching(final String search, final MBeanServer mbs) { - try { - final ObjectName pattern = new ObjectName(search); - final Set<ObjectName> found = mbs.queryNames(pattern, null); - if (found.isEmpty()) { - LOGGER.trace("Unregistering but no MBeans found matching '{}'", search); - } else { - LOGGER.trace("Unregistering {} MBeans: {}", found.size(), found); - } - for (final ObjectName objectName : found) { - mbs.unregisterMBean(objectName); - } - } catch (final Exception ex) { - LOGGER.error("Could not unregister MBeans for " + search, ex); - } - } - - private static void registerLoggerConfigs(final LoggerContext ctx, final MBeanServer mbs, final Executor executor) - throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { - - final Map<String, LoggerConfig> map = ctx.getConfiguration().getLoggers(); - for (final String name : map.keySet()) { - final LoggerConfig cfg = map.get(name); - final LoggerConfigAdmin mbean = new LoggerConfigAdmin(ctx, cfg); - register(mbs, mbean, mbean.getObjectName()); - - if (cfg instanceof AsyncLoggerConfig) { - final AsyncLoggerConfig async = (AsyncLoggerConfig) cfg; - final RingBufferAdmin rbmbean = async.createRingBufferAdmin(ctx.getName()); - register(mbs, rbmbean, rbmbean.getObjectName()); - } - } - } - - private static void registerAppenders(final LoggerContext ctx, final MBeanServer mbs, final Executor executor) - throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { - - final Map<String, Appender> map = ctx.getConfiguration().getAppenders(); - for (final String name : map.keySet()) { - final Appender appender = map.get(name); - - if (appender instanceof AsyncAppender) { - final AsyncAppender async = ((AsyncAppender) appender); - final AsyncAppenderAdmin mbean = new AsyncAppenderAdmin(ctx.getName(), async); - register(mbs, mbean, mbean.getObjectName()); - } else { - final AppenderAdmin mbean = new AppenderAdmin(ctx.getName(), appender); - register(mbs, mbean, mbean.getObjectName()); - } - } - } - - private static void register(final MBeanServer mbs, final Object mbean, final ObjectName objectName) - throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { - LOGGER.debug("Registering MBean {}", objectName); - mbs.registerMBean(mbean, objectName); - } -} diff --git a/core/src/main/java/org/elasticsearch/bootstrap/JarHell.java b/core/src/main/java/org/elasticsearch/bootstrap/JarHell.java index ebf9ab5f55..22ba936d90 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/JarHell.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/JarHell.java @@ -272,20 +272,6 @@ public class JarHell { "class: " + clazz + System.lineSeparator() + "exists multiple times in jar: " + jarpath + " !!!!!!!!!"); } else { - if (clazz.startsWith("org.apache.logging.log4j.core.impl.ThrowableProxy")) { - /* - * deliberate to hack around a bug in Log4j - * cf. https://github.com/elastic/elasticsearch/issues/20304 - * cf. https://issues.apache.org/jira/browse/LOG4J2-1560 - */ - return; - } else if (clazz.startsWith("org.apache.logging.log4j.core.jmx.Server")) { - /* - * deliberate to hack around a bug in Log4j - * cf. https://issues.apache.org/jira/browse/LOG4J2-1506 - */ - return; - } throw new IllegalStateException("jar hell!" + System.lineSeparator() + "class: " + clazz + System.lineSeparator() + "jar1: " + previous + System.lineSeparator() + diff --git a/core/src/main/java/org/elasticsearch/common/logging/LogConfigurator.java b/core/src/main/java/org/elasticsearch/common/logging/LogConfigurator.java index 22d08202f9..428e3ce796 100644 --- a/core/src/main/java/org/elasticsearch/common/logging/LogConfigurator.java +++ b/core/src/main/java/org/elasticsearch/common/logging/LogConfigurator.java @@ -99,7 +99,7 @@ public class LogConfigurator { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file.getFileName().toString().equals("log4j2.properties")) { - configurations.add((PropertiesConfiguration) factory.getConfiguration(file.toString(), file.toUri())); + configurations.add((PropertiesConfiguration) factory.getConfiguration(context, file.toString(), file.toUri())); } return FileVisitResult.CONTINUE; } diff --git a/core/src/test/java/org/elasticsearch/bootstrap/JarHellTests.java b/core/src/test/java/org/elasticsearch/bootstrap/JarHellTests.java index 55d5e65580..d38d346d6c 100644 --- a/core/src/test/java/org/elasticsearch/bootstrap/JarHellTests.java +++ b/core/src/test/java/org/elasticsearch/bootstrap/JarHellTests.java @@ -111,18 +111,6 @@ public class JarHellTests extends ESTestCase { } } - public void testLog4jThrowableProxyLeniency() throws Exception { - Path dir = createTempDir(); - URL[] jars = {makeJar(dir, "foo.jar", null, "org.apache.logging.log4j.core.impl.ThrowableProxy.class"), makeJar(dir, "bar.jar", null, "org.apache.logging.log4j.core.impl.ThrowableProxy.class")}; - JarHell.checkJarHell(jars); - } - - public void testLog4jServerLeniency() throws Exception { - Path dir = createTempDir(); - URL[] jars = {makeJar(dir, "foo.jar", null, "org.apache.logging.log4j.core.jmx.Server.class"), makeJar(dir, "bar.jar", null, "org.apache.logging.log4j.core.jmx.Server.class")}; - JarHell.checkJarHell(jars); - } - public void testWithinSingleJar() throws Exception { // the java api for zip file does not allow creating duplicate entries (good!) so // this bogus jar had to be constructed with ant |