loggers = Hierarchy.getLoggersMap(ctx);
+ while ((name = getSubName(name)) != null) {
+ final Logger subLogger = loggers.get(name);
+ if (subLogger != null) {
+ final ResourceBundle rb = subLogger.bundle;
+ if (rb != null) {
+ return rb;
+ }
+ }
}
}
}
return null;
}
+ public void info(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, null);
+ }
+
+ public void info(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.INFO, message, t);
+ }
+
/**
- If assertion
parameter is {@code false}, then
- logs msg
as an {@link #error(Object) error} statement.
+ * Is the appender passed as parameter attached to this category?
+ *
+ * @param appender The Appender to add.
+ * @return true if the appender is attached.
+ */
+ @Override
+ public boolean isAttached(final Appender appender) {
+ return aai == null ? false : aai.isAttached(appender);
+ }
+
+ public boolean isDebugEnabled() {
+ return logger.isDebugEnabled();
+ }
- The assert
method has been renamed to
- assertLog
because assert
is a language
- reserved word in JDK 1.4.
+ private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
+ return logger.isEnabled(level);
+ }
- @param assertion The assertion.
- @param msg The message to print if assertion
is
- false.
+ public boolean isEnabledFor(final Priority level) {
+ return isEnabledFor(level.getVersion2Level());
+ }
- @since 1.2
- */
- public void assertLog(final boolean assertion, final String msg) {
- if (!assertion) {
- this.error(msg);
- }
+ public boolean isErrorEnabled() {
+ return logger.isErrorEnabled();
}
- public void l7dlog(final Priority priority, final String key, final Throwable t) {
- if (isEnabledFor(priority)) {
- final Message msg = new LocalizedMessage(bundle, key, null);
- forcedLog(FQCN, priority, msg, t);
- }
+ public boolean isFatalEnabled() {
+ return logger.isFatalEnabled();
+ }
+
+ public boolean isInfoEnabled() {
+ return logger.isInfoEnabled();
+ }
+
+ public boolean isWarnEnabled() {
+ return logger.isWarnEnabled();
}
public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
@@ -425,64 +524,139 @@ public void l7dlog(final Priority priority, final String key, final Object[] par
}
}
- public void log(final Priority priority, final Object message, final Throwable t) {
+ public void l7dlog(final Priority priority, final String key, final Throwable t) {
if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
+ final Message msg = new LocalizedMessage(bundle, key, null);
forcedLog(FQCN, priority, msg, t);
}
}
public void log(final Priority priority, final Object message) {
if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
- forcedLog(FQCN, priority, msg, null);
+ forcedLog(FQCN, priority, message, null);
}
}
- public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
+ public void log(final Priority priority, final Object message, final Throwable t) {
if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
- forcedLog(fqcn, priority, msg, t);
+ forcedLog(FQCN, priority, message, t);
}
}
- private void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level,
- final Object message, final Throwable throwable) {
- if (logger.isEnabled(level, null, message, throwable)) {
- logger.logMessage(FQCN, level, null, new ObjectMessage(message), throwable);
+ public void log(final String fqcn, final Priority priority, final Object message, final Throwable t) {
+ if (isEnabledFor(priority)) {
+ forcedLog(fqcn, priority, message, t);
}
}
- private static class PrivateAdapter extends AbstractLoggerAdapter {
+ void maybeLog(
+ final String fqcn,
+ final org.apache.logging.log4j.Level level,
+ final Object message,
+ final Throwable throwable) {
+ if (logger.isEnabled(level)) {
+ final Message msg = createMessage(message);
+ if (logger instanceof ExtendedLogger) {
+ ((ExtendedLogger) logger).logMessage(fqcn, level, null, msg, throwable);
+ } else {
+ logger.log(level, msg, throwable);
+ }
+ }
+ }
- @Override
- protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
- return new Logger((LoggerContext) context, name);
+ /**
+ * Removes all previously added appenders from this Category instance.
+ *
+ * This is useful when re-reading configuration information.
+ *
+ */
+ @Override
+ public void removeAllAppenders() {
+ if (aai != null) {
+ final Vector appenders = new Vector();
+ for (final Enumeration iter = aai.getAllAppenders(); iter != null && iter.hasMoreElements(); ) {
+ appenders.add(iter.nextElement());
+ }
+ aai.removeAllAppenders();
+ for (final Object appender : appenders) {
+ fireRemoveAppenderEvent((Appender) appender);
+ }
+ aai = null;
}
+ }
- @Override
- protected org.apache.logging.log4j.spi.LoggerContext getContext() {
- return PrivateManager.getContext();
+ /**
+ * Removes the appender passed as parameter form the list of appenders.
+ *
+ * @param appender The Appender to remove.
+ * @since 0.8.2
+ */
+ @Override
+ public void removeAppender(final Appender appender) {
+ if (appender == null || aai == null) {
+ return;
+ }
+ final boolean wasAttached = aai.isAttached(appender);
+ aai.removeAppender(appender);
+ if (wasAttached) {
+ fireRemoveAppenderEvent(appender);
}
}
/**
- * Private LogManager.
+ * Removes the appender with the name passed as parameter form the list of appenders.
+ *
+ * @param name The Appender to remove.
+ * @since 0.8.2
*/
- private static class PrivateManager extends org.apache.logging.log4j.LogManager {
- private static final String FQCN = Category.class.getName();
+ @Override
+ public void removeAppender(final String name) {
+ if (name == null || aai == null) {
+ return;
+ }
+ final Appender appender = aai.getAppender(name);
+ aai.removeAppender(name);
+ if (appender != null) {
+ fireRemoveAppenderEvent(appender);
+ }
+ }
- public static LoggerContext getContext() {
- return (LoggerContext) getContext(FQCN, false);
+ public void setAdditivity(final boolean additivity) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.setAdditivity(logger, additivity);
}
+ }
- public static org.apache.logging.log4j.Logger getLogger(final String name) {
- return getLogger(FQCN, name);
+ /**
+ * Only the Hiearchy class can set the hiearchy of a category. Default package access is MANDATORY here.
+ */
+ final void setHierarchy(final LoggerRepository repository) {
+ this.repository = repository;
+ }
+
+ public void setLevel(final Level level) {
+ setLevel(level != null ? level.getVersion2Level() : null);
+ }
+
+ private void setLevel(final org.apache.logging.log4j.Level level) {
+ if (LogManager.isLog4jCorePresent()) {
+ CategoryUtil.setLevel(logger, level);
}
}
- private boolean isEnabledFor(final org.apache.logging.log4j.Level level) {
- return logger.isEnabled(level, null, null);
+ public void setPriority(final Priority priority) {
+ setLevel(priority != null ? priority.getVersion2Level() : null);
+ }
+
+ public void setResourceBundle(final ResourceBundle bundle) {
+ this.bundle = bundle;
}
+ public void warn(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, null);
+ }
+
+ public void warn(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.WARN, message, t);
+ }
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java b/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java
new file mode 100644
index 00000000000..209ccf7be28
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/CategoryKey.java
@@ -0,0 +1,49 @@
+/*
+ * 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.log4j;
+
+/**
+ * CategoryKey is a wrapper for String that apparently accelerated hash table lookup in early JVM's.
+ */
+class CategoryKey {
+
+ String name;
+ int hashCache;
+
+ CategoryKey(final String name) {
+ this.name = name;
+ this.hashCache = name.hashCode();
+ }
+
+ @Override
+ public final int hashCode() {
+ return hashCache;
+ }
+
+ @Override
+ public final boolean equals(final Object rArg) {
+ if (this == rArg) {
+ return true;
+ }
+
+ if (rArg != null && CategoryKey.class == rArg.getClass()) {
+ return name.equals(((CategoryKey) rArg).name);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java
new file mode 100644
index 00000000000..59bfedff040
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/ConsoleAppender.java
@@ -0,0 +1,138 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Placeholder for Log4j 1.2 Console Appender.
+ */
+public class ConsoleAppender extends WriterAppender {
+
+ public static final String SYSTEM_OUT = "System.out";
+ public static final String SYSTEM_ERR = "System.err";
+
+ protected String target = SYSTEM_OUT;
+
+ /**
+ * Determines if the appender honors reassignments of System.out or System.err made after configuration.
+ */
+ private boolean follow;
+
+ /**
+ * Constructs a non-configured appender.
+ */
+ public ConsoleAppender() {}
+
+ /**
+ * Constructs a configured appender.
+ *
+ * @param layout layout, may not be null.
+ */
+ public ConsoleAppender(final Layout layout) {
+ this(layout, SYSTEM_OUT);
+ }
+
+ /**
+ * Constructs a configured appender.
+ *
+ * @param layout layout, may not be null.
+ * @param target target, either "System.err" or "System.out".
+ */
+ public ConsoleAppender(final Layout layout, final String target) {
+ setLayout(layout);
+ setTarget(target);
+ activateOptions();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void append(final LoggingEvent theEvent) {
+ // NOOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void close() {
+ // NOOP
+ }
+
+ /**
+ * Gets whether the appender honors reassignments of System.out or System.err made after configuration.
+ *
+ * @return true if appender will use value of System.out or System.err in force at the time when logging events are
+ * appended.
+ * @since 1.2.13
+ */
+ public boolean getFollow() {
+ return follow;
+ }
+
+ /**
+ * Gets the current value of the Target property. The default value of the option is "System.out".
+ *
+ * See also {@link #setTarget}.
+ */
+ public String getTarget() {
+ return target;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ /**
+ * Sets whether the appender honors reassignments of System.out or System.err made after configuration.
+ *
+ * @param follow if true, appender will use value of System.out or System.err in force at the time when logging events
+ * are appended.
+ * @since 1.2.13
+ */
+ public void setFollow(final boolean follow) {
+ this.follow = follow;
+ }
+
+ /**
+ * Sets the value of the Target option. Recognized values are "System.out" and "System.err". Any other value will
+ * be ignored.
+ */
+ public void setTarget(final String value) {
+ final String v = value.trim();
+
+ if (SYSTEM_OUT.equalsIgnoreCase(v)) {
+ target = SYSTEM_OUT;
+ } else if (SYSTEM_ERR.equalsIgnoreCase(v)) {
+ target = SYSTEM_ERR;
+ } else {
+ targetWarn(value);
+ }
+ }
+
+ void targetWarn(final String val) {
+ StatusLogger.getLogger().warn("[" + val + "] should be System.out or System.err.");
+ StatusLogger.getLogger().warn("Using previously set target, System.out by default.");
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
new file mode 100644
index 00000000000..ae57a544ae0
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
@@ -0,0 +1,29 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggerFactory;
+
+class DefaultCategoryFactory implements LoggerFactory {
+
+ DefaultCategoryFactory() {}
+
+ @Override
+ public Logger makeNewLoggerInstance(final String name) {
+ return new Logger(name);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
new file mode 100644
index 00000000000..79129edbb34
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
@@ -0,0 +1,87 @@
+/*
+ * 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.log4j;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.LineNumberReader;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import org.apache.log4j.spi.ThrowableRenderer;
+
+/**
+ * Default implementation of {@link ThrowableRenderer} using {@link Throwable#printStackTrace(PrintWriter)}.
+ *
+ * @since 1.2.16
+ */
+public final class DefaultThrowableRenderer implements ThrowableRenderer {
+
+ /**
+ * Render throwable using Throwable.printStackTrace.
+ *
+ * @param throwable throwable, may not be null.
+ * @return string representation.
+ */
+ @SuppressFBWarnings(
+ value = "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE",
+ justification = "The throwable is formatted into a log file, which should be private.")
+ public static String[] render(final Throwable throwable) {
+ final StringWriter sw = new StringWriter();
+ final PrintWriter pw = new PrintWriter(sw);
+ try {
+ throwable.printStackTrace(pw);
+ } catch (final RuntimeException ex) {
+ // ignore
+ }
+ pw.flush();
+ final LineNumberReader reader = new LineNumberReader(new StringReader(sw.toString()));
+ final ArrayList lines = new ArrayList<>();
+ try {
+ String line = reader.readLine();
+ while (line != null) {
+ lines.add(line);
+ line = reader.readLine();
+ }
+ } catch (final IOException ex) {
+ if (ex instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ lines.add(ex.toString());
+ }
+ final String[] tempRep = new String[lines.size()];
+ lines.toArray(tempRep);
+ return tempRep;
+ }
+
+ /**
+ * Construct new instance.
+ */
+ public DefaultThrowableRenderer() {
+ // empty
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] doRender(final Throwable throwable) {
+ return render(throwable);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java
new file mode 100644
index 00000000000..25b6edf77d2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/FileAppender.java
@@ -0,0 +1,315 @@
+/*
+ * 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.log4j;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.Writer;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorCode;
+
+/**
+ * FileAppender appends log events to a file.
+ *
+ * Support for java.io.Writer
and console appending has been deprecated and then removed. See the
+ * replacement solutions: {@link WriterAppender} and {@link ConsoleAppender}.
+ *
+ */
+public class FileAppender extends WriterAppender {
+
+ /**
+ * Controls file truncatation. The default value for this variable is true
, meaning that by default a
+ * FileAppender
will append to an existing file and not truncate it.
+ *
+ * This option is meaningful only if the FileAppender opens the file.
+ *
+ */
+ protected boolean fileAppend = true;
+
+ /**
+ * The name of the log file.
+ */
+ protected String fileName = null;
+
+ /**
+ * Do we do bufferedIO?
+ */
+ protected boolean bufferedIO = false;
+
+ /**
+ * Determines the size of IO buffer be. Default is 8K.
+ */
+ protected int bufferSize = 8 * 1024;
+
+ /**
+ * The default constructor does not do anything.
+ */
+ public FileAppender() {}
+
+ /**
+ * Constructs a FileAppender and open the file designated by filename
. The opened filename will become the
+ * output destination for this appender.
+ *
+ * The file will be appended to.
+ *
+ */
+ public FileAppender(final Layout layout, final String filename) throws IOException {
+ this(layout, filename, true);
+ }
+
+ /**
+ * Constructs a FileAppender and open the file designated by filename
. The opened filename will become the
+ * output destination for this appender.
+ *
+ * If the append
parameter is true, the file will be appended to. Otherwise, the file designated by
+ * filename
will be truncated before being opened.
+ *
+ */
+ public FileAppender(final Layout layout, final String filename, final boolean append) throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, false, bufferSize);
+ }
+
+ /**
+ * Constructs a FileAppender
and open the file designated by filename
. The opened filename
+ * will become the output destination for this appender.
+ *
+ * If the append
parameter is true, the file will be appended to. Otherwise, the file designated by
+ * filename
will be truncated before being opened.
+ *
+ *
+ * If the bufferedIO
parameter is true
, then buffered IO will be used to write to the output
+ * file.
+ *
+ */
+ public FileAppender(
+ final Layout layout,
+ final String filename,
+ final boolean append,
+ final boolean bufferedIO,
+ final int bufferSize)
+ throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, bufferedIO, bufferSize);
+ }
+
+ /**
+ * If the value of File is not null
, then {@link #setFile} is called with the values of File
+ * and Append properties.
+ *
+ * @since 0.8.1
+ */
+ public void activateOptions() {
+ if (fileName != null) {
+ try {
+ setFile(fileName, fileAppend, bufferedIO, bufferSize);
+ } catch (java.io.IOException e) {
+ errorHandler.error(
+ "setFile(" + fileName + "," + fileAppend + ") call failed.", e, ErrorCode.FILE_OPEN_FAILURE);
+ }
+ } else {
+ // LogLog.error("File option not set for appender ["+name+"].");
+ LogLog.warn("File option not set for appender [" + name + "].");
+ LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
+ }
+ }
+
+ /**
+ * Closes the previously opened file.
+ */
+ protected void closeFile() {
+ if (this.qw != null) {
+ try {
+ this.qw.close();
+ } catch (java.io.IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // Exceptionally, it does not make sense to delegate to an
+ // ErrorHandler. Since a closed appender is basically dead.
+ LogLog.error("Could not close " + qw, e);
+ }
+ }
+ }
+
+ /**
+ * Returns the value of the Append option.
+ */
+ public boolean getAppend() {
+ return fileAppend;
+ }
+
+ /**
+ * Get the value of the BufferedIO option.
+ *
+ *
+ * BufferedIO will significatnly increase performance on heavily loaded systems.
+ *
+ */
+ public boolean getBufferedIO() {
+ return this.bufferedIO;
+ }
+
+ /**
+ * Get the size of the IO buffer.
+ */
+ public int getBufferSize() {
+ return this.bufferSize;
+ }
+
+ /** Returns the value of the File option. */
+ public String getFile() {
+ return fileName;
+ }
+
+ /**
+ * Close any previously opened file and call the parent's reset
.
+ */
+ protected void reset() {
+ closeFile();
+ this.fileName = null;
+ super.reset();
+ }
+
+ /**
+ * The Append option takes a boolean value. It is set to true
by default. If true, then
+ * File
will be opened in append mode by {@link #setFile setFile} (see above). Otherwise, {@link #setFile
+ * setFile} will open File
in truncate mode.
+ *
+ *
+ * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the options are set.
+ *
+ */
+ public void setAppend(final boolean flag) {
+ fileAppend = flag;
+ }
+
+ /**
+ * The BufferedIO option takes a boolean value. It is set to false
by default. If true, then
+ * File
will be opened and the resulting {@link java.io.Writer} wrapped around a {@link BufferedWriter}.
+ *
+ * BufferedIO will significatnly increase performance on heavily loaded systems.
+ *
+ */
+ public void setBufferedIO(final boolean bufferedIO) {
+ this.bufferedIO = bufferedIO;
+ if (bufferedIO) {
+ immediateFlush = false;
+ }
+ }
+
+ /**
+ * Set the size of the IO buffer.
+ */
+ public void setBufferSize(final int bufferSize) {
+ this.bufferSize = bufferSize;
+ }
+
+ /**
+ * The File property takes a string value which should be the name of the file to append to.
+ *
+ * Note that the special values "System.out" or "System.err" are no longer honored.
+ *
+ *
+ * Note: Actual opening of the file is made when {@link #activateOptions} is called, not when the options are set.
+ *
+ */
+ public void setFile(final String file) {
+ // Trim spaces from both ends. The users probably does not want
+ // trailing spaces in file names.
+ final String val = file.trim();
+ fileName = val;
+ }
+
+ /**
+ * Sets and opens the file where the log output will go. The specified file must be writable.
+ *
+ * If there was already an opened file, then the previous file is closed first.
+ *
+ *
+ * Do not use this method directly. To configure a FileAppender or one of its subclasses, set its properties one by
+ * one and then call activateOptions.
+ *
+ *
+ * @param fileName The path to the log file.
+ * @param append If true will append to fileName. Otherwise will truncate fileName.
+ */
+ @SuppressFBWarnings(
+ value = {"PATH_TRAVERSAL_IN", "PATH_TRAVERSAL_OUT"},
+ justification = "The file name comes from a configuration file.")
+ public synchronized void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
+ throws IOException {
+ LogLog.debug("setFile called: " + fileName + ", " + append);
+
+ // It does not make sense to have immediate flush and bufferedIO.
+ if (bufferedIO) {
+ setImmediateFlush(false);
+ }
+
+ reset();
+ FileOutputStream ostream = null;
+ try {
+ //
+ // attempt to create file
+ //
+ ostream = new FileOutputStream(fileName, append);
+ } catch (FileNotFoundException ex) {
+ //
+ // if parent directory does not exist then
+ // attempt to create it and try to create file
+ // see bug 9150
+ //
+ final String parentName = new File(fileName).getParent();
+ if (parentName != null) {
+ final File parentDir = new File(parentName);
+ if (!parentDir.exists() && parentDir.mkdirs()) {
+ ostream = new FileOutputStream(fileName, append);
+ } else {
+ throw ex;
+ }
+ } else {
+ throw ex;
+ }
+ }
+ Writer fw = createWriter(ostream);
+ if (bufferedIO) {
+ fw = new BufferedWriter(fw, bufferSize);
+ }
+ this.setQWForFiles(fw);
+ this.fileName = fileName;
+ this.fileAppend = append;
+ this.bufferedIO = bufferedIO;
+ this.bufferSize = bufferSize;
+ writeHeader();
+ LogLog.debug("setFile ended");
+ }
+
+ /**
+ * Sets the quiet writer being used.
+ *
+ * This method is overriden by {@link RollingFileAppender}.
+ */
+ protected void setQWForFiles(final Writer writer) {
+ this.qw = new QuietWriter(writer, errorHandler);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
new file mode 100644
index 00000000000..850ea52bd61
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java
@@ -0,0 +1,537 @@
+/*
+ * 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.log4j;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.legacy.core.ContextUtil;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.spi.AbstractLoggerAdapter;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
+
+// WARNING This class MUST not have references to the Category or
+// WARNING RootCategory classes in its static initialization neither
+// WARNING directly nor indirectly.
+/**
+ * This class is specialized in retrieving loggers by name and also maintaining the logger hierarchy.
+ *
+ *
+ * The casual user does not have to deal with this class directly.
+ *
+ *
+ * The structure of the logger hierarchy is maintained by the {@link #getLogger} method. The hierarchy is such that
+ * children link to their parent but parents do not have any pointers to their children. Moreover, loggers can be
+ * instantiated in any order, in particular descendant before ancestor.
+ *
+ *
+ * In case a descendant is created before a particular ancestor, then it creates a provision node for the ancestor and
+ * adds itself to the provision node. Other descendants of the same ancestor add themselves to the previously created
+ * provision node.
+ *
+ */
+public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
+
+ private static class PrivateLoggerAdapter extends AbstractLoggerAdapter {
+
+ @Override
+ protected org.apache.logging.log4j.spi.LoggerContext getContext() {
+ return PrivateLogManager.getContext();
+ }
+
+ @Override
+ protected Logger newLogger(final String name, final org.apache.logging.log4j.spi.LoggerContext context) {
+ return new Logger(context, name);
+ }
+ }
+
+ /**
+ * Private LogManager.
+ */
+ private static class PrivateLogManager extends org.apache.logging.log4j.LogManager {
+ private static final String FQCN = Hierarchy.class.getName();
+
+ @SuppressFBWarnings(
+ value = "HSM_HIDING_METHOD",
+ justification = "The class is private, no confusion can arise.")
+ public static LoggerContext getContext() {
+ return getContext(FQCN, false);
+ }
+
+ @SuppressFBWarnings(
+ value = "HSM_HIDING_METHOD",
+ justification = "The class is private, no confusion can arise.")
+ public static org.apache.logging.log4j.Logger getLogger(final String name) {
+ return getLogger(FQCN, name);
+ }
+ }
+
+ private static final PrivateLoggerAdapter LOGGER_ADAPTER = new PrivateLoggerAdapter();
+
+ private static final WeakHashMap> CONTEXT_MAP = new WeakHashMap<>();
+
+ static LoggerContext getContext() {
+ return PrivateLogManager.getContext();
+ }
+
+ private Logger getInstance(final LoggerContext context, final String name) {
+ return getInstance(context, name, LOGGER_ADAPTER);
+ }
+
+ private Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) {
+ return getLoggersMap(context).computeIfAbsent(name, k -> {
+ final Logger logger = factory.makeNewLoggerInstance(name);
+ logger.setHierarchy(this);
+ return logger;
+ });
+ }
+
+ private Logger getInstance(final LoggerContext context, final String name, final PrivateLoggerAdapter factory) {
+ return getLoggersMap(context).computeIfAbsent(name, k -> {
+ final Logger logger = factory.newLogger(name, context);
+ logger.setHierarchy(this);
+ return logger;
+ });
+ }
+
+ static ConcurrentMap getLoggersMap(final LoggerContext context) {
+ synchronized (CONTEXT_MAP) {
+ return CONTEXT_MAP.computeIfAbsent(context, k -> new ConcurrentHashMap<>());
+ }
+ }
+
+ private final LoggerFactory defaultFactory;
+ private final Vector listeners;
+ Hashtable ht;
+ Logger root;
+ RendererMap rendererMap;
+ int thresholdInt;
+ Level threshold;
+ boolean emittedNoAppenderWarning;
+
+ boolean emittedNoResourceBundleWarning;
+
+ private ThrowableRenderer throwableRenderer;
+
+ /**
+ * Creates a new logger hierarchy.
+ *
+ * @param root The root of the new hierarchy.
+ *
+ */
+ public Hierarchy(final Logger root) {
+ ht = new Hashtable();
+ listeners = new Vector(1);
+ this.root = root;
+ // Enable all level levels by default.
+ setThreshold(Level.ALL);
+ this.root.setHierarchy(this);
+ rendererMap = new RendererMap();
+ defaultFactory = new DefaultCategoryFactory();
+ }
+
+ @Override
+ public void addHierarchyEventListener(final HierarchyEventListener listener) {
+ if (listeners.contains(listener)) {
+ LogLog.warn("Ignoring attempt to add an existent listener.");
+ } else {
+ listeners.addElement(listener);
+ }
+ }
+
+ /**
+ * Adds an object renderer for a specific class.
+ */
+ public void addRenderer(final Class classToRender, final ObjectRenderer or) {
+ rendererMap.put(classToRender, or);
+ }
+
+ /**
+ * This call will clear all logger definitions from the internal hashtable. Invoking this method will irrevocably mess
+ * up the logger hierarchy.
+ *
+ *
+ * You should really know what you are doing before invoking this method.
+ *
+ *
+ * @since 0.9.0
+ */
+ public void clear() {
+ // System.out.println("\n\nAbout to clear internal hash table.");
+ ht.clear();
+ getLoggersMap(getContext()).clear();
+ }
+
+ @Override
+ public void emitNoAppenderWarning(final Category cat) {
+ // No appenders in hierarchy, warn user only once.
+ if (!this.emittedNoAppenderWarning) {
+ LogLog.warn("No appenders could be found for logger (" + cat.getName() + ").");
+ LogLog.warn("Please initialize the log4j system properly.");
+ LogLog.warn("See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.");
+ this.emittedNoAppenderWarning = true;
+ }
+ }
+
+ /**
+ * Tests if the named logger exists in the hierarchy. If so return its reference, otherwise returns null
.
+ *
+ * @param name The name of the logger to search for.
+ *
+ */
+ @Override
+ public Logger exists(final String name) {
+ return exists(name, getContext());
+ }
+
+ Logger exists(final String name, final ClassLoader classLoader) {
+ return exists(name, getContext(classLoader));
+ }
+
+ Logger exists(final String name, final LoggerContext loggerContext) {
+ if (!loggerContext.hasLogger(name)) {
+ return null;
+ }
+ return Logger.getLogger(name);
+ }
+
+ @Override
+ public void fireAddAppenderEvent(final Category logger, final Appender appender) {
+ if (listeners != null) {
+ final int size = listeners.size();
+ HierarchyEventListener listener;
+ for (int i = 0; i < size; i++) {
+ listener = (HierarchyEventListener) listeners.elementAt(i);
+ listener.addAppenderEvent(logger, appender);
+ }
+ }
+ }
+
+ void fireRemoveAppenderEvent(final Category logger, final Appender appender) {
+ if (listeners != null) {
+ final int size = listeners.size();
+ HierarchyEventListener listener;
+ for (int i = 0; i < size; i++) {
+ listener = (HierarchyEventListener) listeners.elementAt(i);
+ listener.removeAppenderEvent(logger, appender);
+ }
+ }
+ }
+
+ LoggerContext getContext(final ClassLoader classLoader) {
+ return LogManager.getContext(classLoader);
+ }
+
+ /**
+ * @deprecated Please use {@link #getCurrentLoggers} instead.
+ */
+ @Deprecated
+ @Override
+ public Enumeration getCurrentCategories() {
+ return getCurrentLoggers();
+ }
+
+ /**
+ * Gets all the currently defined categories in this hierarchy as an {@link java.util.Enumeration Enumeration}.
+ *
+ *
+ * The root logger is not included in the returned {@link Enumeration}.
+ *
+ */
+ @Override
+ public Enumeration getCurrentLoggers() {
+ // The accumlation in v is necessary because not all elements in
+ // ht are Logger objects as there might be some ProvisionNodes
+ // as well.
+ // final Vector v = new Vector(ht.size());
+ //
+ // final Enumeration elems = ht.elements();
+ // while (elems.hasMoreElements()) {
+ // final Object o = elems.nextElement();
+ // if (o instanceof Logger) {
+ // v.addElement(o);
+ // }
+ // }
+ // return v.elements();
+
+ return LogManager.getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * Gets a new logger instance named as the first parameter using the default factory.
+ *
+ *
+ * If a logger of that name already exists, then it will be returned. Otherwise, a new logger will be instantiated and
+ * then linked with its existing ancestors as well as children.
+ *
+ *
+ * @param name The name of the logger to retrieve.
+ *
+ */
+ @Override
+ public Logger getLogger(final String name) {
+ return getInstance(getContext(), name);
+ }
+
+ Logger getLogger(final String name, final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), name);
+ }
+
+ /**
+ * Gets a new logger instance named as the first parameter using factory
.
+ *
+ *
+ * If a logger of that name already exists, then it will be returned. Otherwise, a new logger will be instantiated by
+ * the factory
parameter and linked with its existing ancestors as well as children.
+ *
+ *
+ * @param name The name of the logger to retrieve.
+ * @param factory The factory that will make the new logger instance.
+ *
+ */
+ @Override
+ public Logger getLogger(final String name, final LoggerFactory factory) {
+ return getInstance(getContext(), name, factory);
+ }
+
+ Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), name, factory);
+ }
+
+ /**
+ * Gets the renderer map for this hierarchy.
+ */
+ @Override
+ public RendererMap getRendererMap() {
+ return rendererMap;
+ }
+
+ /**
+ * Gets the root of this hierarchy.
+ *
+ * @since 0.9.0
+ */
+ @Override
+ public Logger getRootLogger() {
+ return getInstance(getContext(), org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+ }
+
+ Logger getRootLogger(final ClassLoader classLoader) {
+ return getInstance(getContext(classLoader), org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+ }
+
+ /**
+ * Gets a {@link Level} representation of the enable
state.
+ *
+ * @since 1.2
+ */
+ @Override
+ public Level getThreshold() {
+ return threshold;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ThrowableRenderer getThrowableRenderer() {
+ return throwableRenderer;
+ }
+
+ /**
+ * This method will return true
if this repository is disabled for level
object passed as
+ * parameter and false
otherwise. See also the {@link #setThreshold(Level) threshold} emthod.
+ */
+ @Override
+ public boolean isDisabled(final int level) {
+ return thresholdInt > level;
+ }
+
+ /**
+ * @deprecated Deprecated with no replacement.
+ */
+ @Deprecated
+ public void overrideAsNeeded(final String override) {
+ LogLog.warn("The Hiearchy.overrideAsNeeded method has been deprecated.");
+ }
+
+ /**
+ * Resets all values contained in this hierarchy instance to their default. This removes all appenders from all
+ * categories, sets the level of all non-root categories to null
, sets their additivity flag to
+ * true
and sets the level of the root logger to {@link Level#DEBUG DEBUG}. Moreover, message disabling is
+ * set its default "off" value.
+ *
+ *
+ * Existing categories are not removed. They are just reset.
+ *
+ *
+ *
+ * This method should be used sparingly and with care as it will block all logging until it is completed.
+ *
+ *
+ * @since 0.8.5
+ */
+ @Override
+ public void resetConfiguration() {
+ resetConfiguration(getContext());
+ }
+
+ void resetConfiguration(final ClassLoader classLoader) {
+ resetConfiguration(getContext(classLoader));
+ }
+
+ void resetConfiguration(final LoggerContext loggerContext) {
+ getLoggersMap(loggerContext).clear();
+
+ getRootLogger().setLevel(Level.DEBUG);
+ root.setResourceBundle(null);
+ setThreshold(Level.ALL);
+
+ // the synchronization is needed to prevent JDK 1.2.x hashtable
+ // surprises
+ synchronized (ht) {
+ shutdown(); // nested locks are OK
+
+ final Enumeration cats = getCurrentLoggers();
+ while (cats.hasMoreElements()) {
+ final Logger c = (Logger) cats.nextElement();
+ c.setLevel(null);
+ c.setAdditivity(true);
+ c.setResourceBundle(null);
+ }
+ }
+ rendererMap.clear();
+ throwableRenderer = null;
+ }
+
+ /**
+ * Does nothing.
+ *
+ * @deprecated Deprecated with no replacement.
+ */
+ @Deprecated
+ public void setDisableOverride(final String override) {
+ LogLog.warn("The Hiearchy.setDisableOverride method has been deprecated.");
+ }
+
+ /**
+ * Used by subclasses to add a renderer to the hierarchy passed as parameter.
+ */
+ @Override
+ public void setRenderer(final Class renderedClass, final ObjectRenderer renderer) {
+ rendererMap.put(renderedClass, renderer);
+ }
+
+ /**
+ * Enable logging for logging requests with level l
or higher. By default all levels are enabled.
+ *
+ * @param level The minimum level for which logging requests are sent to their appenders.
+ */
+ @Override
+ public void setThreshold(final Level level) {
+ if (level != null) {
+ thresholdInt = level.level;
+ threshold = level;
+ }
+ }
+
+ /**
+ * The string form of {@link #setThreshold(Level)}.
+ */
+ @Override
+ public void setThreshold(final String levelStr) {
+ final Level level = OptionConverter.toLevel(levelStr, null);
+ if (level != null) {
+ setThreshold(level);
+ } else {
+ LogLog.warn("Could not convert [" + levelStr + "] to Level.");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setThrowableRenderer(final ThrowableRenderer throwableRenderer) {
+ this.throwableRenderer = throwableRenderer;
+ }
+
+ /**
+ * Shutting down a hierarchy will safely close and remove all appenders in all categories including the root
+ * logger.
+ *
+ *
+ * Some appenders such as {@link org.apache.log4j.net.SocketAppender} and {@link AsyncAppender} need to be closed before
+ * the application exists. Otherwise, pending logging events might be lost.
+ *
+ *
+ * The shutdown
method is careful to close nested appenders before closing regular appenders. This is
+ * allows configurations where a regular appender is attached to a logger and again to a nested appender.
+ *
+ *
+ * @since 1.0
+ */
+ @Override
+ public void shutdown() {
+ shutdown(getContext());
+ }
+
+ public void shutdown(final ClassLoader classLoader) {
+ shutdown(org.apache.logging.log4j.LogManager.getContext(classLoader, false));
+ }
+
+ void shutdown(final LoggerContext context) {
+ // final Logger root = getRootLogger();
+ // // begin by closing nested appenders
+ // root.closeNestedAppenders();
+ //
+ // synchronized (ht) {
+ // Enumeration cats = this.getCurrentLoggers();
+ // while (cats.hasMoreElements()) {
+ // final Logger c = (Logger) cats.nextElement();
+ // c.closeNestedAppenders();
+ // }
+ //
+ // // then, remove all appenders
+ // root.removeAllAppenders();
+ // cats = this.getCurrentLoggers();
+ // while (cats.hasMoreElements()) {
+ // final Logger c = (Logger) cats.nextElement();
+ // c.removeAllAppenders();
+ // }
+ // }
+ getLoggersMap(context).clear();
+ if (LogManager.isLog4jCorePresent()) {
+ ContextUtil.shutdown(context);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
index 2ef4b1c7eaf..ae0866518e1 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Layout.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
@@ -24,6 +24,8 @@
*/
public abstract class Layout {
+ public static final String LINE_SEP = Strings.LINE_SEPARATOR;
+
/** Note that the line.separator property can be looked up even by applets. */
public static final int LINE_SEP_LEN = Strings.LINE_SEPARATOR.length();
@@ -61,7 +63,6 @@ public String getFooter() {
return null;
}
-
/**
* If the layout handles the throwable object contained within
* {@link LoggingEvent}, then the layout should return
@@ -84,4 +85,3 @@ public String getFooter() {
*/
public abstract boolean ignoresThrowable();
}
-
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java
index af5315417bd..7bc068a3faf 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Level.java
@@ -1,28 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
+import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
+
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
-import java.util.Locale;
-
+import org.apache.log4j.helpers.OptionConverter;
import org.apache.logging.log4j.util.Strings;
/**
@@ -48,50 +49,50 @@ public class Level extends Priority implements Serializable {
* The OFF
has the highest possible rank and is
* intended to turn off logging.
*/
- public static final Level OFF = new Level(OFF_INT, "OFF", 0);
+ public static final Level OFF = new Level(OFF_INT, "OFF", 0, org.apache.logging.log4j.Level.OFF);
/**
* The FATAL
level designates very severe error
* events that will presumably lead the application to abort.
*/
- public static final Level FATAL = new Level(FATAL_INT, "FATAL", 0);
+ public static final Level FATAL = new Level(FATAL_INT, "FATAL", 0, org.apache.logging.log4j.Level.FATAL);
/**
* The ERROR
level designates error events that
* might still allow the application to continue running.
*/
- public static final Level ERROR = new Level(ERROR_INT, "ERROR", 3);
+ public static final Level ERROR = new Level(ERROR_INT, "ERROR", 3, org.apache.logging.log4j.Level.ERROR);
/**
* The WARN
level designates potentially harmful situations.
*/
- public static final Level WARN = new Level(WARN_INT, "WARN", 4);
+ public static final Level WARN = new Level(WARN_INT, "WARN", 4, org.apache.logging.log4j.Level.WARN);
/**
* The INFO
level designates informational messages
* that highlight the progress of the application at coarse-grained
* level.
*/
- public static final Level INFO = new Level(INFO_INT, "INFO", 6);
+ public static final Level INFO = new Level(INFO_INT, "INFO", 6, org.apache.logging.log4j.Level.INFO);
/**
* The DEBUG
Level designates fine-grained
* informational events that are most useful to debug an
* application.
*/
- public static final Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7);
+ public static final Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7, org.apache.logging.log4j.Level.DEBUG);
/**
* The TRACE
Level designates finer-grained
* informational events than the DEBUG
level.
*/
- public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7);
+ public static final Level TRACE = new Level(TRACE_INT, "TRACE", 7, org.apache.logging.log4j.Level.TRACE);
/**
* The ALL
has the lowest possible rank and is intended to
* turn on all logging.
*/
- public static final Level ALL = new Level(ALL_INT, "ALL", 7);
+ public static final Level ALL = new Level(ALL_INT, "ALL", 7, org.apache.logging.log4j.Level.ALL);
/**
* Serialization version id.
@@ -99,16 +100,23 @@ public class Level extends Priority implements Serializable {
private static final long serialVersionUID = 3491141966387921974L;
/**
- * Instantiate a Level object.
+ * Instantiate a Level object. A corresponding Log4j 2.x level is also created.
*
* @param level The logging level.
* @param levelStr The level name.
* @param syslogEquivalent The matching syslog level.
*/
protected Level(final int level, final String levelStr, final int syslogEquivalent) {
- super(level, levelStr, syslogEquivalent);
+ this(level, levelStr, syslogEquivalent, null);
}
+ protected Level(
+ final int level,
+ final String levelStr,
+ final int syslogEquivalent,
+ final org.apache.logging.log4j.Level version2Equivalent) {
+ super(level, levelStr, syslogEquivalent, version2Equivalent);
+ }
/**
* Convert the string passed as argument to a level. If the
@@ -175,26 +183,26 @@ public static Level toLevel(final String sArg, final Level defaultLevel) {
if (sArg == null) {
return defaultLevel;
}
- final String s = sArg.toUpperCase(Locale.ROOT);
+ final String s = toRootUpperCase(sArg);
switch (s) {
- case "ALL":
- return Level.ALL;
- case "DEBUG":
- return Level.DEBUG;
- case "INFO":
- return Level.INFO;
- case "WARN":
- return Level.WARN;
- case "ERROR":
- return Level.ERROR;
- case "FATAL":
- return Level.FATAL;
- case "OFF":
- return Level.OFF;
- case "TRACE":
- return Level.TRACE;
- default:
- return defaultLevel;
+ case "ALL":
+ return Level.ALL;
+ case "DEBUG":
+ return Level.DEBUG;
+ case "INFO":
+ return Level.INFO;
+ case "WARN":
+ return Level.WARN;
+ case "ERROR":
+ return Level.ERROR;
+ case "FATAL":
+ return Level.FATAL;
+ case "OFF":
+ return Level.OFF;
+ case "TRACE":
+ return Level.TRACE;
+ default:
+ return defaultLevel;
}
}
@@ -213,6 +221,7 @@ private void readObject(final ObjectInputStream s) throws IOException, ClassNotF
if (levelStr == null) {
levelStr = Strings.EMPTY;
}
+ version2Level = OptionConverter.createLevel(this);
}
/**
@@ -247,6 +256,4 @@ protected Object readResolve() throws ObjectStreamException {
//
return this;
}
-
}
-
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
index 751cb86972d..933c97148ef 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/LogManager.java
@@ -1,222 +1,239 @@
-/*
- * 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.log4j;
-
-import java.util.Enumeration;
-
-import org.apache.log4j.helpers.NullEnumeration;
-import org.apache.log4j.spi.HierarchyEventListener;
-import org.apache.log4j.spi.LoggerFactory;
-import org.apache.log4j.spi.LoggerRepository;
-import org.apache.log4j.spi.RepositorySelector;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.util.Strings;
-
-/**
- *
- */
-public final class LogManager {
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become package protected in future versions.
- * */
- @Deprecated
- public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become private in future versions.
- * */
- @Deprecated
- public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become private in future versions.
- * */
- @Deprecated
- public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
-
- /**
- * @deprecated This variable is for internal use only. It will
- * become private in future versions.
- */
- @Deprecated
- public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
-
- static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
-
- private static final LoggerRepository REPOSITORY = new Repository();
-
- private LogManager() {
- }
-
- public static Logger getRootLogger() {
- return Category.getInstance(PrivateManager.getContext(), Strings.EMPTY);
- }
-
- public static Logger getLogger(final String name) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- public static Logger getLogger(final Class> clazz) {
- return Category.getInstance(PrivateManager.getContext(), clazz.getName());
- }
-
- public static Logger getLogger(final String name, final LoggerFactory factory) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- public static Logger exists(final String name) {
- final LoggerContext ctx = PrivateManager.getContext();
- if (!ctx.hasLogger(name)) {
- return null;
- }
- return Logger.getLogger(name);
- }
-
- @SuppressWarnings("rawtypes")
- public static Enumeration getCurrentLoggers() {
- return NullEnumeration.getInstance();
- }
-
- static void reconfigure() {
- final LoggerContext ctx = PrivateManager.getContext();
- ctx.reconfigure();
- }
-
- /**
- * No-op implementation.
- */
- public static void shutdown() {
- }
-
- /**
- * No-op implementation.
- */
- public static void resetConfiguration() {
- }
-
- /**
- * No-op implementation.
- * @param selector The RepositorySelector.
- * @param guard prevents calls at the incorrect time.
- * @throws IllegalArgumentException if a parameter is invalid.
- */
- public static void setRepositorySelector(final RepositorySelector selector, final Object guard)
- throws IllegalArgumentException {
- }
-
- public static LoggerRepository getLoggerRepository() {
- return REPOSITORY;
- }
-
- /**
- * The Repository.
- */
- private static class Repository implements LoggerRepository {
- @Override
- public void addHierarchyEventListener(final HierarchyEventListener listener) {
-
- }
-
- @Override
- public boolean isDisabled(final int level) {
- return false;
- }
-
- @Override
- public void setThreshold(final Level level) {
-
- }
-
- @Override
- public void setThreshold(final String val) {
-
- }
-
- @Override
- public void emitNoAppenderWarning(final Category cat) {
-
- }
-
- @Override
- public Level getThreshold() {
- return Level.OFF;
- }
-
- @Override
- public Logger getLogger(final String name) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- @Override
- public Logger getLogger(final String name, final LoggerFactory factory) {
- return Category.getInstance(PrivateManager.getContext(), name);
- }
-
- @Override
- public Logger getRootLogger() {
- return Category.getRoot(PrivateManager.getContext());
- }
-
- @Override
- public Logger exists(final String name) {
- return LogManager.exists(name);
- }
-
- @Override
- public void shutdown() {
- }
-
- @Override
- @SuppressWarnings("rawtypes")
- public Enumeration getCurrentLoggers() {
- return NullEnumeration.getInstance();
- }
-
- @Override
- @SuppressWarnings("rawtypes")
- public Enumeration getCurrentCategories() {
- return NullEnumeration.getInstance();
- }
-
- @Override
- public void fireAddAppenderEvent(final Category logger, final Appender appender) {
- }
-
- @Override
- public void resetConfiguration() {
- }
- }
-
- /**
- * Internal LogManager.
- */
- private static class PrivateManager extends org.apache.logging.log4j.LogManager {
- private static final String FQCN = LogManager.class.getName();
-
- public static LoggerContext getContext() {
- return (LoggerContext) getContext(FQCN, false);
- }
-
- public static org.apache.logging.log4j.Logger getLogger(final String name) {
- return getLogger(FQCN, name);
- }
- }
-}
+/*
+ * 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.log4j;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.stream.Collectors;
+import org.apache.log4j.legacy.core.ContextUtil;
+import org.apache.log4j.spi.DefaultRepositorySelector;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.NOPLoggerRepository;
+import org.apache.log4j.spi.RepositorySelector;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
+
+/**
+ * The main entry point to Log4j 1.
+ */
+public final class LogManager {
+
+ /**
+ * @deprecated This variable is for internal use only. It will become package protected in future versions.
+ */
+ @Deprecated
+ public static final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
+
+ /**
+ * @deprecated This variable is for internal use only. It will become private in future versions.
+ */
+ @Deprecated
+ public static final String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
+
+ /**
+ * @deprecated This variable is for internal use only. It will become private in future versions.
+ */
+ @Deprecated
+ public static final String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
+
+ /**
+ * @deprecated This variable is for internal use only. It will become private in future versions.
+ */
+ @Deprecated
+ public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
+
+ static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
+
+ private static RepositorySelector repositorySelector;
+
+ private static final boolean LOG4J_CORE_PRESENT;
+
+ static {
+ LOG4J_CORE_PRESENT = checkLog4jCore();
+ // By default, we use a DefaultRepositorySelector which always returns 'hierarchy'.
+ final Hierarchy hierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
+ repositorySelector = new DefaultRepositorySelector(hierarchy);
+ }
+
+ private static boolean checkLog4jCore() {
+ try {
+ return Class.forName("org.apache.logging.log4j.core.LoggerContext") != null;
+ } catch (final Throwable ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Tests if a logger for the given name exists.
+ *
+ * @param name logger name to test.
+ * @return whether a logger for the given name exists.
+ */
+ public static Logger exists(final String name) {
+ return exists(name, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static Logger exists(final String name, final ClassLoader classLoader) {
+ return getHierarchy().exists(name, classLoader);
+ }
+
+ /**
+ * Gets a LoggerContext.
+ *
+ * @param classLoader The ClassLoader for the context. If null the context will attempt to determine the appropriate
+ * ClassLoader.
+ * @return a LoggerContext.
+ */
+ static LoggerContext getContext(final ClassLoader classLoader) {
+ return org.apache.logging.log4j.LogManager.getContext(classLoader, false);
+ }
+
+ /**
+ * Gets an enumeration of the current loggers.
+ *
+ * @return an enumeration of the current loggers.
+ */
+ @SuppressWarnings("rawtypes")
+ public static Enumeration getCurrentLoggers() {
+ return getCurrentLoggers(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ @SuppressWarnings("rawtypes")
+ static Enumeration getCurrentLoggers(final ClassLoader classLoader) {
+ // @formatter:off
+ return Collections.enumeration(LogManager.getContext(classLoader).getLoggerRegistry().getLoggers().stream()
+ .map(e -> LogManager.getLogger(e.getName(), classLoader))
+ .collect(Collectors.toList()));
+ // @formatter:on
+ }
+
+ static Hierarchy getHierarchy() {
+ final LoggerRepository loggerRepository = getLoggerRepository();
+ return loggerRepository instanceof Hierarchy ? (Hierarchy) loggerRepository : null;
+ }
+
+ /**
+ * Gets the logger for the given class.
+ */
+ public static Logger getLogger(final Class> clazz) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null
+ ? hierarchy.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2))
+ : getLoggerRepository().getLogger(clazz.getName());
+ }
+
+ /**
+ * Gets the logger for the given name.
+ */
+ public static Logger getLogger(final String name) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null
+ ? hierarchy.getLogger(name, StackLocatorUtil.getCallerClassLoader(2))
+ : getLoggerRepository().getLogger(name);
+ }
+
+ static Logger getLogger(final String name, final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null
+ ? hierarchy.getLogger(name, classLoader)
+ : getLoggerRepository().getLogger(name);
+ }
+
+ public static Logger getLogger(final String name, final LoggerFactory factory) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null
+ ? hierarchy.getLogger(name, factory, StackLocatorUtil.getCallerClassLoader(2))
+ : getLoggerRepository().getLogger(name, factory);
+ }
+
+ static Logger getLogger(final String name, final LoggerFactory factory, final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null
+ ? hierarchy.getLogger(name, factory, classLoader)
+ : getLoggerRepository().getLogger(name, factory);
+ }
+
+ public static LoggerRepository getLoggerRepository() {
+ if (repositorySelector == null) {
+ repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
+ }
+ return repositorySelector.getLoggerRepository();
+ }
+
+ /**
+ * Gets the root logger.
+ */
+ public static Logger getRootLogger() {
+ return getRootLogger(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static Logger getRootLogger(final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ return hierarchy != null
+ ? hierarchy.getRootLogger(classLoader)
+ : getLoggerRepository().getRootLogger();
+ }
+
+ static boolean isLog4jCorePresent() {
+ return LOG4J_CORE_PRESENT;
+ }
+
+ static void reconfigure(final ClassLoader classLoader) {
+ if (isLog4jCorePresent()) {
+ ContextUtil.reconfigure(LogManager.getContext(classLoader));
+ }
+ }
+
+ public static void resetConfiguration() {
+ resetConfiguration(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static void resetConfiguration(final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ if (hierarchy != null) {
+ hierarchy.resetConfiguration(classLoader);
+ } else {
+ getLoggerRepository().resetConfiguration();
+ }
+ }
+
+ public static void setRepositorySelector(final RepositorySelector selector, final Object guard)
+ throws IllegalArgumentException {
+ if (selector == null) {
+ throw new IllegalArgumentException("RepositorySelector must be non-null.");
+ }
+ LogManager.repositorySelector = selector;
+ }
+
+ /**
+ * Shuts down the current configuration.
+ */
+ public static void shutdown() {
+ shutdown(StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ static void shutdown(final ClassLoader classLoader) {
+ final Hierarchy hierarchy = getHierarchy();
+ if (hierarchy != null) {
+ hierarchy.shutdown(classLoader);
+ } else {
+ getLoggerRepository().shutdown();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
index fb7277b5012..aa15d237cfb 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Logger.java
@@ -1,66 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
-
import org.apache.log4j.spi.LoggerFactory;
-import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.util.StackLocatorUtil;
/**
*
*/
public class Logger extends Category {
- protected Logger(final String name) {
- super(PrivateManager.getContext(), name);
- }
+ /**
+ * The fully qualified name of the Logger class.
+ */
+ private static final String FQCN = Logger.class.getName();
- Logger(final LoggerContext context, final String name) {
- super(context, name);
+ public static Logger getLogger(@SuppressWarnings("rawtypes") final Class clazz) {
+ // Depth 2 gets the call site of this method.
+ return LogManager.getLogger(clazz.getName(), StackLocatorUtil.getCallerClassLoader(2));
}
public static Logger getLogger(final String name) {
- return Category.getInstance(PrivateManager.getContext(), name);
+ // Depth 2 gets the call site of this method.
+ return LogManager.getLogger(name, StackLocatorUtil.getCallerClassLoader(2));
}
- public static Logger getLogger(final Class> clazz) {
- return Category.getInstance(PrivateManager.getContext(), clazz);
+ public static Logger getLogger(final String name, final LoggerFactory factory) {
+ // Depth 2 gets the call site of this method.
+ return LogManager.getLogger(name, factory, StackLocatorUtil.getCallerClassLoader(2));
}
public static Logger getRootLogger() {
- return Category.getRoot(PrivateManager.getContext());
+ return LogManager.getRootLogger();
}
- public static Logger getLogger(final String name, final LoggerFactory factory) {
- return Category.getInstance(PrivateManager.getContext(), name, factory);
+ Logger(final LoggerContext context, final String name) {
+ super(context, name);
}
- /**
- * Internal Log Manager.
- */
- private static class PrivateManager extends org.apache.logging.log4j.LogManager {
- private static final String FQCN = Logger.class.getName();
+ protected Logger(final String name) {
+ super(name);
+ }
+
+ public boolean isTraceEnabled() {
+ return getLogger().isTraceEnabled();
+ }
- public static LoggerContext getContext() {
- return (LoggerContext) getContext(FQCN, false);
- }
+ public void trace(final Object message) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, null);
+ }
- public static org.apache.logging.log4j.Logger getLogger(final String name) {
- return getLogger(FQCN, name);
- }
+ public void trace(final Object message, final Throwable t) {
+ maybeLog(FQCN, org.apache.logging.log4j.Level.TRACE, message, t);
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java b/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java
index ee7631a6994..83d2df57628 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/MDC.java
@@ -1,25 +1,24 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
-
import org.apache.logging.log4j.ThreadContext;
/**
@@ -28,30 +27,25 @@
*/
public final class MDC {
+ private static final ThreadLocal> localMap = new InheritableThreadLocal>() {
+ @Override
+ protected Map initialValue() {
+ return new HashMap<>();
+ }
- private static ThreadLocal> localMap =
- new InheritableThreadLocal>() {
- @Override
- protected Map initialValue() {
- return new HashMap<>();
- }
-
- @Override
- protected Map childValue(final Map parentValue) {
- return parentValue == null ? new HashMap() : new HashMap<>(parentValue);
- }
- };
-
- private MDC() {
- }
+ @Override
+ protected Map childValue(final Map parentValue) {
+ return parentValue == null ? new HashMap<>() : new HashMap<>(parentValue);
+ }
+ };
+ private MDC() {}
public static void put(final String key, final String value) {
localMap.get().put(key, value);
ThreadContext.put(key, value);
}
-
public static void put(final String key, final Object value) {
localMap.get().put(key, value);
ThreadContext.put(key, value.toString());
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/NDC.java b/log4j-1.2-api/src/main/java/org/apache/log4j/NDC.java
index a4e23dcd7d1..4631fc3f373 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/NDC.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/NDC.java
@@ -1,30 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
import java.util.Stack;
/**
- *
+ * This class does not use generics to provide better source compatibility.
*/
public final class NDC {
- private NDC() {
- }
+ private NDC() {}
/**
* Clear any nested diagnostic information if any. This method is
@@ -39,7 +38,6 @@ public static void clear() {
org.apache.logging.log4j.ThreadContext.clearStack();
}
-
/**
* Clone the diagnostic context for the current thread.
*
@@ -52,20 +50,20 @@ public static void clear() {
* The child thread uses the {@link #inherit inherit} method to
* inherit the parent's diagnostic context.
*
- * @return Stack A clone of the current thread's diagnostic context.
+ * @return Stack A clone of the current thread's diagnostic context, Stack of Strings.
*/
@SuppressWarnings("rawtypes")
public static Stack cloneStack() {
final Stack stack = new Stack<>();
- for (final String element : org.apache.logging.log4j.ThreadContext.cloneStack().asList()) {
+ for (final String element :
+ org.apache.logging.log4j.ThreadContext.cloneStack().asList()) {
stack.push(element);
}
return stack;
}
-
/**
- * Inherit the diagnostic context of another thread.
+ * Inherit the diagnostic context of another thread, a Stack of Strings.
*
* The parent thread can obtain a reference to its diagnostic
* context using the {@link #cloneStack} method. It should
@@ -83,13 +81,13 @@ public static Stack cloneStack() {
* there is no client-transparent way of inheriting diagnostic
* contexts. Do you know any solution to this problem?
*
- * @param stack The diagnostic context of the parent thread.
+ * @param stack The diagnostic context of the parent thread, a Stack of Strings.
*/
- public static void inherit(final Stack stack) {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static void inherit(final Stack stack) {
org.apache.logging.log4j.ThreadContext.setStack(stack);
}
-
/**
* Never use this method directly.
*
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
index c2e1251ee8c..7331cfeda9d 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PatternLayout.java
@@ -1,40 +1,132 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
+import org.apache.log4j.helpers.PatternConverter;
+import org.apache.log4j.helpers.PatternParser;
import org.apache.log4j.spi.LoggingEvent;
-import org.apache.logging.log4j.util.Strings;
/**
*
*/
public class PatternLayout extends Layout {
+ /**
+ * Default pattern string for log output. Currently set to the string {@value #DEFAULT_CONVERSION_PATTERN} which
+ * just prints the application supplied message.
+ */
+ public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
+
+ /**
+ * A conversion pattern equivalent to the TTCCCLayout. Current value is {@value #TTCC_CONVERSION_PATTERN}
+ */
+ public static final String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";
+
+ protected final int BUF_SIZE = 256;
+
+ protected final int MAX_CAPACITY = 1024;
+
+ // output buffer appended to when format() is invoked
+ private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
+
+ private String pattern;
+
+ private PatternConverter head;
+
+ /**
+ * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
+ *
+ * The default pattern just produces the application supplied message.
+ */
+ public PatternLayout() {
+ this(DEFAULT_CONVERSION_PATTERN);
+ }
+
+ /**
+ * Constructs a PatternLayout using the supplied conversion pattern.
+ */
public PatternLayout(final String pattern) {
+ this.pattern = pattern;
+ head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern)
+ .parse();
+ }
+
+ /**
+ * Does not do anything as options become effective
+ */
+ public void activateOptions() {
+ // nothing to do.
+ }
+ /**
+ * Returns PatternParser used to parse the conversion string. Subclasses may override this to return a subclass of
+ * PatternParser which recognize custom conversion characters.
+ *
+ * @since 0.9.0
+ */
+ protected PatternParser createPatternParser(final String pattern) {
+ return new PatternParser(pattern);
}
+ /**
+ * Produces a formatted string as specified by the conversion pattern.
+ */
@Override
public String format(final LoggingEvent event) {
- return Strings.EMPTY;
+ // Reset working stringbuffer
+ if (sbuf.capacity() > MAX_CAPACITY) {
+ sbuf = new StringBuffer(BUF_SIZE);
+ } else {
+ sbuf.setLength(0);
+ }
+
+ PatternConverter c = head;
+
+ while (c != null) {
+ c.format(sbuf, event);
+ c = c.next;
+ }
+ return sbuf.toString();
}
+ /**
+ * Returns the value of the ConversionPattern option.
+ */
+ public String getConversionPattern() {
+ return pattern;
+ }
+
+ /**
+ * The PatternLayout does not handle the throwable contained within {@link LoggingEvent LoggingEvents}. Thus, it returns
+ * true
.
+ *
+ * @since 0.8.4
+ */
@Override
public boolean ignoresThrowable() {
return true;
}
+
+ /**
+ * Set the ConversionPattern option. This is the string which controls formatting and consists of a mix of
+ * literal content and conversion specifiers.
+ */
+ public void setConversionPattern(final String conversionPattern) {
+ pattern = conversionPattern;
+ head = createPatternParser(conversionPattern).parse();
+ }
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java
index 8f6eee9b519..5b72275bd11 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Priority.java
@@ -1,21 +1,23 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
+import org.apache.log4j.helpers.OptionConverter;
+
/**
* Refrain from using this class directly, use
* the {@link Level} class instead.
@@ -53,7 +55,7 @@ public class Priority {
* application.
*/
public static final int DEBUG_INT = 10000;
- //public final static int FINE_INT = DEBUG_INT;
+ // public final static int FINE_INT = DEBUG_INT;
/**
* The ALL
has the lowest possible rank and is intended to
* turn on all logging.
@@ -64,31 +66,31 @@ public class Priority {
* @deprecated Use {@link Level#FATAL} instead.
*/
@Deprecated
- public static final Priority FATAL = new Level(FATAL_INT, "FATAL", 0);
+ public static final Priority FATAL = new Priority(FATAL_INT, "FATAL", 0, org.apache.logging.log4j.Level.FATAL);
/**
* @deprecated Use {@link Level#ERROR} instead.
*/
@Deprecated
- public static final Priority ERROR = new Level(ERROR_INT, "ERROR", 3);
+ public static final Priority ERROR = new Priority(ERROR_INT, "ERROR", 3, org.apache.logging.log4j.Level.ERROR);
/**
* @deprecated Use {@link Level#WARN} instead.
*/
@Deprecated
- public static final Priority WARN = new Level(WARN_INT, "WARN", 4);
+ public static final Priority WARN = new Priority(WARN_INT, "WARN", 4, org.apache.logging.log4j.Level.WARN);
/**
* @deprecated Use {@link Level#INFO} instead.
*/
@Deprecated
- public static final Priority INFO = new Level(INFO_INT, "INFO", 6);
+ public static final Priority INFO = new Priority(INFO_INT, "INFO", 6, org.apache.logging.log4j.Level.INFO);
/**
* @deprecated Use {@link Level#DEBUG} instead.
*/
@Deprecated
- public static final Priority DEBUG = new Level(DEBUG_INT, "DEBUG", 7);
+ public static final Priority DEBUG = new Priority(DEBUG_INT, "DEBUG", 7, org.apache.logging.log4j.Level.DEBUG);
/*
* These variables should be private but were not in Log4j 1.2 so are left the same way here.
@@ -96,14 +98,13 @@ public class Priority {
transient int level;
transient String levelStr;
transient int syslogEquivalent;
+ transient org.apache.logging.log4j.Level version2Level;
/**
* Default constructor for deserialization.
*/
protected Priority() {
- level = DEBUG_INT;
- levelStr = "DEBUG";
- syslogEquivalent = 7;
+ this(DEBUG_INT, "DEBUG", 7, org.apache.logging.log4j.Level.DEBUG);
}
/**
@@ -113,9 +114,18 @@ protected Priority() {
* @param syslogEquivalent The equivalent syslog value.
*/
protected Priority(final int level, final String levelStr, final int syslogEquivalent) {
+ this(level, levelStr, syslogEquivalent, null);
+ }
+
+ Priority(
+ final int level,
+ final String levelStr,
+ final int syslogEquivalent,
+ final org.apache.logging.log4j.Level version2Equivalent) {
this.level = level;
this.levelStr = levelStr;
this.syslogEquivalent = syslogEquivalent;
+ this.version2Level = version2Equivalent != null ? version2Equivalent : OptionConverter.createLevel(this);
}
/**
@@ -143,11 +153,18 @@ public int hashCode() {
* Returns the syslog equivalent of this priority as an integer.
* @return The equivalent syslog value.
*/
- public
- final int getSyslogEquivalent() {
+ public final int getSyslogEquivalent() {
return syslogEquivalent;
}
+ /**
+ * Gets the Log4j 2.x level associated with this priority
+ *
+ * @return a Log4j 2.x level.
+ */
+ public org.apache.logging.log4j.Level getVersion2Level() {
+ return version2Level;
+ }
/**
* Returns {@code true} if this level has a higher or equal
@@ -173,11 +190,9 @@ public boolean isGreaterOrEqual(final Priority r) {
*/
@Deprecated
public static Priority[] getAllPossiblePriorities() {
- return new Priority[]{Priority.FATAL, Priority.ERROR, Level.WARN,
- Priority.INFO, Priority.DEBUG};
+ return new Priority[] {Priority.FATAL, Priority.ERROR, Level.WARN, Priority.INFO, Priority.DEBUG};
}
-
/**
* Returns the string representation of this priority.
* @return The name of the Priority.
@@ -223,7 +238,8 @@ public static Priority toPriority(final int val) {
*/
@Deprecated
public static Priority toPriority(final int val, final Priority defaultPriority) {
- return Level.toLevel(val, (Level) defaultPriority);
+ Level result = Level.toLevel(val, null);
+ return result == null ? defaultPriority : result;
}
/**
@@ -234,6 +250,7 @@ public static Priority toPriority(final int val, final Priority defaultPriority)
*/
@Deprecated
public static Priority toPriority(final String sArg, final Priority defaultPriority) {
- return Level.toLevel(sArg, (Level) defaultPriority);
+ Level result = Level.toLevel(sArg, null);
+ return result == null ? defaultPriority : result;
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
index 0fe1fe0e60c..647b86e5b92 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/PropertyConfigurator.java
@@ -1,126 +1,674 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.InterruptedIOException;
import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Map;
import java.util.Properties;
-
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.net.UrlConnectionFactory;
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.util.StackLocatorUtil;
/**
- * A configurator for properties.
+ * Configures Log4j from properties.
*/
-public class PropertyConfigurator {
+public class PropertyConfigurator implements Configurator {
+
+ static class NameValue {
+ String key, value;
+
+ public NameValue(final String key, final String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return key + "=" + value;
+ }
+ }
+
+ static class PropertyWatchdog extends FileWatchdog {
+
+ private final ClassLoader classLoader;
+
+ PropertyWatchdog(final String fileName, final ClassLoader classLoader) {
+ super(fileName);
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Call {@link PropertyConfigurator#configure(String)} with the filename
to reconfigure log4j.
+ */
+ @Override
+ public void doOnChange() {
+ new PropertyConfigurator().doConfigure(filename, LogManager.getLoggerRepository(), classLoader);
+ }
+ }
+
+ class SortedKeyEnumeration implements Enumeration {
+
+ private final Enumeration e;
+
+ public SortedKeyEnumeration(final Hashtable ht) {
+ final Enumeration f = ht.keys();
+ final Vector keys = new Vector(ht.size());
+ for (int i, last = 0; f.hasMoreElements(); ++last) {
+ final String key = (String) f.nextElement();
+ for (i = 0; i < last; ++i) {
+ final String s = (String) keys.get(i);
+ if (key.compareTo(s) <= 0) {
+ break;
+ }
+ }
+ keys.add(i, key);
+ }
+ e = keys.elements();
+ }
+
+ @Override
+ public boolean hasMoreElements() {
+ return e.hasMoreElements();
+ }
+
+ @Override
+ public Object nextElement() {
+ return e.nextElement();
+ }
+ }
+
+ private static final String CATEGORY_PREFIX = "log4j.category.";
+ private static final String LOGGER_PREFIX = "log4j.logger.";
+ private static final String FACTORY_PREFIX = "log4j.factory";
+ private static final String ADDITIVITY_PREFIX = "log4j.additivity.";
+ private static final String APPENDER_PREFIX = "log4j.appender.";
+ private static final String RENDERER_PREFIX = "log4j.renderer.";
+
+ private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
+ private static final String LOGGER_REF = "logger-ref";
+ private static final String ROOT_REF = "root-ref";
+ private static final String APPENDER_REF_TAG = "appender-ref";
/**
- * Read configuration options from configuration file.
- *
- * @param configFileName The configuration file
- * @param hierarchy The hierarchy
+ * Key for specifying the {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}. Currently set to
+ * "log4j.loggerFactory
".
+ */
+ public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
+
+ /**
+ * If property set to true, then hierarchy will be reset before configuration.
*/
- public void doConfigure(final String configFileName, final LoggerRepository hierarchy) {
+ private static final String RESET_KEY = "log4j.reset";
+
+ private static final String INTERNAL_ROOT_NAME = "root";
+
+ private static boolean isFullCompatibilityEnabled() {
+ return PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL);
+ }
+ private static void warnFullCompatibilityDisabled() {
+ LogLog.warn(
+ "Ignoring `PropertyConfigurator` call, since `log4j1.compatibility` is not enabled.\n"
+ + "See https://logging.staged.apache.org/log4j/2.x/migrate-from-log4j1.html#log4j1.compatibility for details.");
}
/**
- * Read configuration options from properties
.
+ * Reads configuration options from an InputStream.
+ *
+ * @param inputStream The input stream
+ */
+ public static void configure(final InputStream inputStream) {
+ new PropertyConfigurator()
+ .doConfigure(inputStream, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * Reads configuration options from properties
.
*
* See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
* @param properties The properties
- * @param hierarchy The hierarchy
*/
- public void doConfigure(final Properties properties, final LoggerRepository hierarchy) {
+ public static void configure(final Properties properties) {
+ new PropertyConfigurator()
+ .doConfigure(properties, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from an InputStream.
+ * Reads configuration options from configuration file.
*
- * @param inputStream The input stream
- * @param hierarchy The hierarchy
+ * @param fileName The configuration file.
*/
- public void doConfigure(final InputStream inputStream, final LoggerRepository hierarchy) {
+ public static void configure(final String fileName) {
+ new PropertyConfigurator()
+ .doConfigure(fileName, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from url configURL
.
+ * Reads configuration options from url configURL
.
*
* @param configURL The configuration URL
- * @param hierarchy The hierarchy
*/
- public void doConfigure(final URL configURL, final LoggerRepository hierarchy) {
+ public static void configure(final URL configURL) {
+ new PropertyConfigurator()
+ .doConfigure(configURL, LogManager.getLoggerRepository(), StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from configuration file.
+ * Like {@link #configureAndWatch(String, long)} except that the default delay as defined by FileWatchdog.DEFAULT_DELAY
+ * is used.
*
- * @param configFileName The configuration file.
+ * @param configFilename A file in key=value format.
*/
- public static void configure(final String configFileName) {
+ public static void configureAndWatch(final String configFilename) {
+ configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY, StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Read configuration options from url configURL
.
+ * Reads the configuration file configFilename
if it exists. Moreover, a thread will be created that will
+ * periodically check if configFilename
has been created or modified. The period is determined by the
+ * delay
argument. If a change or file creation is detected, then configFilename
is read to
+ * configure log4j.
*
- * @param configURL The configuration URL
+ * @param configFilename A file in key=value format.
+ * @param delayMillis The delay in milliseconds to wait between each check.
*/
- public static void configure(final URL configURL) {
+ public static void configureAndWatch(final String configFilename, final long delayMillis) {
+ configureAndWatch(configFilename, delayMillis, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ private static void configureAndWatch(
+ final String configFilename, final long delay, final ClassLoader classLoader) {
+ if (isFullCompatibilityEnabled()) {
+ final PropertyWatchdog watchdog = new PropertyWatchdog(configFilename, classLoader);
+ watchdog.setDelay(delay);
+ watchdog.start();
+ } else {
+ warnFullCompatibilityDisabled();
+ }
+ }
+
+ /**
+ * Used internally to keep track of configured appenders.
+ */
+ protected Hashtable registry = new Hashtable(11);
+
+ private LoggerRepository repository;
+
+ protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
+
+ /**
+ * Checks the provided Properties
object for a {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}
+ * entry specified by {@link #LOGGER_FACTORY_KEY}. If such an entry exists, an attempt is made to create an instance
+ * using the default constructor. This instance is used for subsequent Category creations within this configurator.
+ *
+ * @see #parseCatsAndRenderers
+ */
+ protected void configureLoggerFactory(final Properties properties) {
+ if (isFullCompatibilityEnabled()) {
+ final String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY, properties);
+ if (factoryClassName != null) {
+ LogLog.debug("Setting category factory to [" + factoryClassName + "].");
+ loggerFactory = (LoggerFactory)
+ OptionConverter.instantiateByClassName(factoryClassName, LoggerFactory.class, loggerFactory);
+ PropertySetter.setProperties(loggerFactory, properties, FACTORY_PREFIX + ".");
+ }
+ } else {
+ warnFullCompatibilityDisabled();
+ }
}
/**
* Reads configuration options from an InputStream.
*
* @param inputStream The input stream
+ * @param loggerRepository The hierarchy
*/
- public static void configure(final InputStream inputStream) {
+ @Override
+ public void doConfigure(final InputStream inputStream, final LoggerRepository loggerRepository) {
+ doConfigure(inputStream, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ private void doConfigure(
+ final InputStream inputStream, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ doConfigure(loadProperties(inputStream), loggerRepository, classLoader);
}
/**
- * Read configuration options from properties
.
+ * Reads configuration options from properties
.
*
* See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
* @param properties The properties
+ * @param loggerRepository The hierarchy
*/
- public static void configure(final Properties properties) {
+ public void doConfigure(final Properties properties, final LoggerRepository loggerRepository) {
+ doConfigure(properties, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
}
/**
- * Like {@link #configureAndWatch(String, long)} except that the
- * default delay as defined by FileWatchdog.DEFAULT_DELAY is
- * used.
+ * Reads configuration options from properties
.
+ *
+ * See {@link #doConfigure(String, LoggerRepository)} for the expected format.
*
- * @param configFilename A file in key=value format.
+ * @param properties The properties
+ * @param loggerRepository The hierarchy
*/
- public static void configureAndWatch(final String configFilename) {
+ private void doConfigure(
+ final Properties properties, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ if (isFullCompatibilityEnabled()) {
+ final PropertiesConfiguration configuration =
+ new PropertiesConfiguration(LogManager.getContext(classLoader), properties);
+ configuration.doConfigure();
+
+ repository = loggerRepository;
+
+ // We don't want to hold references to appenders preventing their
+ // garbage collection.
+ registry.clear();
+
+ org.apache.logging.log4j.core.config.Configurator.reconfigure(configuration);
+ } else {
+ warnFullCompatibilityDisabled();
+ }
}
/**
- * Read the configuration file configFilename
if it
- * exists. Moreover, a thread will be created that will periodically
- * check if configFilename
has been created or
- * modified. The period is determined by the delay
- * argument. If a change or file creation is detected, then
- * configFilename
is read to configure log4j.
+ * Reads configuration options from configuration file.
*
- * @param configFilename A file in key=value format.
- * @param delay The delay in milliseconds to wait between each check.
+ * @param fileName The configuration file
+ * @param loggerRepository The hierarchy
+ */
+ public void doConfigure(final String fileName, final LoggerRepository loggerRepository) {
+ doConfigure(fileName, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ /**
+ * Reads configuration options from configuration file.
+ *
+ * @param fileName The configuration file
+ * @param loggerRepository The hierarchy
+ */
+ @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "The filename comes from a system property.")
+ private void doConfigure(
+ final String fileName, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ if (isFullCompatibilityEnabled()) {
+ try (final InputStream inputStream = Files.newInputStream(Paths.get(fileName))) {
+ doConfigure(inputStream, loggerRepository, classLoader);
+ } catch (final Exception e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not read configuration file [" + fileName + "].", e);
+ LogLog.error("Ignoring configuration file [" + fileName + "].");
+ }
+ } else {
+ warnFullCompatibilityDisabled();
+ }
+ }
+
+ /**
+ * Read configuration options from url configURL
.
+ *
+ * @param url The configuration URL
+ * @param loggerRepository The hierarchy
*/
- public static void configureAndWatch(final String configFilename, final long delay) {
+ @Override
+ public void doConfigure(final URL url, final LoggerRepository loggerRepository) {
+ doConfigure(url, loggerRepository, StackLocatorUtil.getCallerClassLoader(2));
+ }
+
+ private void doConfigure(final URL url, final LoggerRepository loggerRepository, final ClassLoader classLoader) {
+ if (isFullCompatibilityEnabled()) {
+ LogLog.debug("Reading configuration from URL " + url);
+ try {
+ final URLConnection urlConnection = UrlConnectionFactory.createConnection(url);
+ try (final InputStream inputStream = urlConnection.getInputStream()) {
+ doConfigure(inputStream, loggerRepository, classLoader);
+ }
+ } catch (final IOException e) {
+ LogLog.error("Could not read configuration file from URL [" + url + "].", e);
+ LogLog.error("Ignoring configuration file [" + url + "].");
+ }
+ } else {
+ warnFullCompatibilityDisabled();
+ }
+ }
+
+ private Properties loadProperties(final InputStream inputStream) {
+ final Properties loaded = new Properties();
+ try {
+ loaded.load(inputStream);
+ } catch (final IOException | IllegalArgumentException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not read configuration file from InputStream [" + inputStream + "].", e);
+ LogLog.error("Ignoring configuration InputStream [" + inputStream + "].");
+ return null;
+ }
+ return loaded;
+ }
+
+ /**
+ * Parse the additivity option for a non-root category.
+ */
+ private void parseAdditivityForLogger(final Properties properties, final Logger logger, final String loggerName) {
+ final String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName, properties);
+ LogLog.debug("Handling " + ADDITIVITY_PREFIX + loggerName + "=[" + value + "]");
+ // touch additivity only if necessary
+ if (value != null && !value.isEmpty()) {
+ final boolean additivity = OptionConverter.toBoolean(value, true);
+ LogLog.debug("Setting additivity for \"" + loggerName + "\" to " + additivity);
+ logger.setAdditivity(additivity);
+ }
+ }
+
+ private Appender parseAppender(final Properties properties, final String appenderName) {
+ Appender appender = (Appender) registry.get(appenderName);
+ if ((appender != null)) {
+ LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
+ return appender;
+ }
+ // Appender was not previously initialized.
+ final String prefix = APPENDER_PREFIX + appenderName;
+ final String layoutPrefix = prefix + ".layout";
+
+ appender =
+ (Appender) OptionConverter.instantiateByKey(properties, prefix, org.apache.log4j.Appender.class, null);
+ if (appender == null) {
+ LogLog.error("Could not instantiate appender named \"" + appenderName + "\".");
+ return null;
+ }
+ appender.setName(appenderName);
+
+ if (appender instanceof OptionHandler) {
+ if (appender.requiresLayout()) {
+ final Layout layout =
+ (Layout) OptionConverter.instantiateByKey(properties, layoutPrefix, Layout.class, null);
+ if (layout != null) {
+ appender.setLayout(layout);
+ LogLog.debug("Parsing layout options for \"" + appenderName + "\".");
+ // configureOptionHandler(layout, layoutPrefix + ".", props);
+ PropertySetter.setProperties(layout, properties, layoutPrefix + ".");
+ LogLog.debug("End of parsing for \"" + appenderName + "\".");
+ }
+ }
+ final String errorHandlerPrefix = prefix + ".errorhandler";
+ final String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, properties);
+ if (errorHandlerClass != null) {
+ final ErrorHandler eh = (ErrorHandler)
+ OptionConverter.instantiateByKey(properties, errorHandlerPrefix, ErrorHandler.class, null);
+ if (eh != null) {
+ appender.setErrorHandler(eh);
+ LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\".");
+ parseErrorHandler(eh, errorHandlerPrefix, properties, repository);
+ final Properties edited = new Properties();
+ final String[] keys = new String[] {
+ errorHandlerPrefix + "." + ROOT_REF,
+ errorHandlerPrefix + "." + LOGGER_REF,
+ errorHandlerPrefix + "." + APPENDER_REF_TAG
+ };
+ for (final Object element : properties.entrySet()) {
+ final Map.Entry entry = (Map.Entry) element;
+ int i = 0;
+ for (; i < keys.length; i++) {
+ if (keys[i].equals(entry.getKey())) {
+ break;
+ }
+ }
+ if (i == keys.length) {
+ edited.put(entry.getKey(), entry.getValue());
+ }
+ }
+ PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
+ LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\".");
+ }
+ }
+ // configureOptionHandler((OptionHandler) appender, prefix + ".", props);
+ PropertySetter.setProperties(appender, properties, prefix + ".");
+ LogLog.debug("Parsed \"" + appenderName + "\" options.");
+ }
+ parseAppenderFilters(properties, appenderName, appender);
+ registry.put(appender.getName(), appender);
+ return appender;
+ }
+
+ private void parseAppenderFilters(final Properties properties, final String appenderName, final Appender appender) {
+ // extract filters and filter options from props into a hashtable mapping
+ // the property name defining the filter class to a list of pre-parsed
+ // name-value pairs associated to that filter
+ final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
+ final int fIdx = filterPrefix.length();
+ final Hashtable filters = new Hashtable();
+ final Enumeration e = properties.keys();
+ String name = "";
+ while (e.hasMoreElements()) {
+ final String key = (String) e.nextElement();
+ if (key.startsWith(filterPrefix)) {
+ final int dotIdx = key.indexOf('.', fIdx);
+ String filterKey = key;
+ if (dotIdx != -1) {
+ filterKey = key.substring(0, dotIdx);
+ name = key.substring(dotIdx + 1);
+ }
+ Vector filterOpts = (Vector) filters.get(filterKey);
+ if (filterOpts == null) {
+ filterOpts = new Vector();
+ filters.put(filterKey, filterOpts);
+ }
+ if (dotIdx != -1) {
+ final String value = OptionConverter.findAndSubst(key, properties);
+ filterOpts.add(new NameValue(name, value));
+ }
+ }
+ }
+
+ // sort filters by IDs, insantiate filters, set filter options,
+ // add filters to the appender
+ final Enumeration g = new SortedKeyEnumeration(filters);
+ Filter head = null;
+ while (g.hasMoreElements()) {
+ final String key = (String) g.nextElement();
+ final String clazz = properties.getProperty(key);
+ if (clazz != null) {
+ LogLog.debug("Filter key: [" + key + "] class: [" + properties.getProperty(key) + "] props: "
+ + filters.get(key));
+ final Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null);
+ if (filter != null) {
+ final PropertySetter propSetter = new PropertySetter(filter);
+ final Vector v = (Vector) filters.get(key);
+ final Enumeration filterProps = v.elements();
+ while (filterProps.hasMoreElements()) {
+ final NameValue kv = (NameValue) filterProps.nextElement();
+ propSetter.setProperty(kv.key, kv.value);
+ }
+ propSetter.activate();
+ LogLog.debug("Adding filter of type [" + filter.getClass() + "] to appender named ["
+ + appender.getName() + "].");
+ head = FilterAdapter.addFilter(head, filter);
+ }
+ } else {
+ LogLog.warn("Missing class definition for filter: [" + key + "]");
+ }
+ }
+ appender.addFilter(head);
+ }
+
+ /**
+ * This method must work for the root category as well.
+ */
+ private void parseCategory(
+ final Properties properties,
+ final Logger logger,
+ final String optionKey,
+ final String loggerName,
+ final String value) {
+
+ LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "].");
+ // We must skip over ',' but not white space
+ final StringTokenizer st = new StringTokenizer(value, ",");
+
+ // If value is not in the form ", appender.." or "", then we should set
+ // the level of the loggeregory.
+
+ if (!(value.startsWith(",") || value.isEmpty())) {
+
+ // just to be on the safe side...
+ if (!st.hasMoreTokens()) {
+ return;
+ }
+
+ final String levelStr = st.nextToken();
+ LogLog.debug("Level token is [" + levelStr + "].");
+
+ // If the level value is inherited, set category level value to
+ // null. We also check that the user has not specified inherited for the
+ // root category.
+ if (INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) {
+ if (loggerName.equals(INTERNAL_ROOT_NAME)) {
+ LogLog.warn("The root logger cannot be set to null.");
+ } else {
+ logger.setLevel(null);
+ }
+ } else {
+ logger.setLevel(OptionConverter.toLevel(levelStr, Log4j1Configuration.DEFAULT_LEVEL));
+ }
+ LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
+ }
+
+ // Begin by removing all existing appenders.
+ logger.removeAllAppenders();
+
+ Appender appender;
+ String appenderName;
+ while (st.hasMoreTokens()) {
+ appenderName = st.nextToken().trim();
+ if (appenderName == null || appenderName.equals(",")) {
+ continue;
+ }
+ LogLog.debug("Parsing appender named \"" + appenderName + "\".");
+ appender = parseAppender(properties, appenderName);
+ if (appender != null) {
+ logger.addAppender(appender);
+ }
+ }
+ }
+
+ /**
+ * Parse non-root elements, such non-root categories and renderers.
+ */
+ protected void parseCatsAndRenderers(final Properties properties, final LoggerRepository loggerRepository) {
+ if (!isFullCompatibilityEnabled()) {
+ warnFullCompatibilityDisabled();
+ return;
+ }
+ final Enumeration enumeration = properties.propertyNames();
+ while (enumeration.hasMoreElements()) {
+ final String key = (String) enumeration.nextElement();
+ if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
+ String loggerName = null;
+ if (key.startsWith(CATEGORY_PREFIX)) {
+ loggerName = key.substring(CATEGORY_PREFIX.length());
+ } else if (key.startsWith(LOGGER_PREFIX)) {
+ loggerName = key.substring(LOGGER_PREFIX.length());
+ }
+ final String value = OptionConverter.findAndSubst(key, properties);
+ final Logger logger = loggerRepository.getLogger(loggerName, loggerFactory);
+ synchronized (logger) {
+ parseCategory(properties, logger, key, loggerName, value);
+ parseAdditivityForLogger(properties, logger, loggerName);
+ }
+ } else if (key.startsWith(RENDERER_PREFIX)) {
+ final String renderedClass = key.substring(RENDERER_PREFIX.length());
+ final String renderingClass = OptionConverter.findAndSubst(key, properties);
+ if (loggerRepository instanceof RendererSupport) {
+ RendererMap.addRenderer((RendererSupport) loggerRepository, renderedClass, renderingClass);
+ }
+ } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {
+ if (loggerRepository instanceof ThrowableRendererSupport) {
+ final ThrowableRenderer tr = (ThrowableRenderer) OptionConverter.instantiateByKey(
+ properties, THROWABLE_RENDERER_PREFIX, org.apache.log4j.spi.ThrowableRenderer.class, null);
+ if (tr == null) {
+ LogLog.error("Could not instantiate throwableRenderer.");
+ } else {
+ final PropertySetter setter = new PropertySetter(tr);
+ setter.setProperties(properties, THROWABLE_RENDERER_PREFIX + ".");
+ ((ThrowableRendererSupport) loggerRepository).setThrowableRenderer(tr);
+ }
+ }
+ }
+ }
+ }
+
+ private void parseErrorHandler(
+ final ErrorHandler errorHandler,
+ final String errorHandlerPrefix,
+ final Properties props,
+ final LoggerRepository loggerRepository) {
+ if (errorHandler != null && loggerRepository != null) {
+ final boolean rootRef = OptionConverter.toBoolean(
+ OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false);
+ if (rootRef) {
+ errorHandler.setLogger(loggerRepository.getRootLogger());
+ }
+ final String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF, props);
+ if (loggerName != null) {
+ final Logger logger = loggerFactory == null
+ ? loggerRepository.getLogger(loggerName)
+ : loggerRepository.getLogger(loggerName, loggerFactory);
+ errorHandler.setLogger(logger);
+ }
+ final String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props);
+ if (appenderName != null) {
+ final Appender backup = parseAppender(props, appenderName);
+ if (backup != null) {
+ errorHandler.setBackupAppender(backup);
+ }
+ }
+ }
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java b/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java
new file mode 100644
index 00000000000..0ac82732e6e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/ProvisionNode.java
@@ -0,0 +1,28 @@
+/*
+ * 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.log4j;
+
+import java.util.Vector;
+
+class ProvisionNode extends Vector {
+ private static final long serialVersionUID = -4479121426311014469L;
+
+ ProvisionNode(final Logger logger) {
+ super();
+ this.addElement(logger);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java b/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java
new file mode 100644
index 00000000000..5e6fbcf9d44
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/RenderedMessage.java
@@ -0,0 +1,59 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.logging.log4j.message.Message;
+
+/**
+ * Implements object rendering for Log4j 1.x compatibility.
+ */
+public class RenderedMessage implements Message {
+
+ private final ObjectRenderer renderer;
+ private final Object object;
+ private String rendered = null;
+
+ public RenderedMessage(final ObjectRenderer renderer, final Object object) {
+ this.renderer = renderer;
+ this.object = object;
+ }
+
+ @Override
+ public String getFormattedMessage() {
+ if (rendered == null) {
+ rendered = renderer.doRender(object);
+ }
+
+ return rendered;
+ }
+
+ @Override
+ public String getFormat() {
+ return getFormattedMessage();
+ }
+
+ @Override
+ public Object[] getParameters() {
+ return null;
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java
new file mode 100644
index 00000000000..ce03ec2bc8a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/RollingFileAppender.java
@@ -0,0 +1,255 @@
+/*
+ * 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.log4j;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.File;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.Writer;
+import org.apache.log4j.helpers.CountingQuietWriter;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * RollingFileAppender extends FileAppender to backup the log files when they reach a certain size.
+ *
+ * The log4j extras companion includes alternatives which should be considered for new deployments and which are
+ * discussed in the documentation for org.apache.log4j.rolling.RollingFileAppender.
+ */
+public class RollingFileAppender extends FileAppender {
+
+ /**
+ * The default maximum file size is 10MB.
+ */
+ protected long maxFileSize = 10 * 1024 * 1024;
+
+ /**
+ * There is one backup file by default.
+ */
+ protected int maxBackupIndex = 1;
+
+ private long nextRollover = 0;
+
+ /**
+ * The default constructor simply calls its {@link FileAppender#FileAppender parents constructor}.
+ */
+ public RollingFileAppender() {
+ super();
+ }
+
+ /**
+ * Constructs a RollingFileAppender and open the file designated by filename
. The opened filename will
+ * become the ouput destination for this appender.
+ *
+ *
+ * If the append
parameter is true, the file will be appended to. Otherwise, the file desginated by
+ * filename
will be truncated before being opened.
+ *
+ */
+ public RollingFileAppender(final Layout layout, final String filename, final boolean append) throws IOException {
+ super(layout, filename, append);
+ }
+
+ /**
+ * Constructs a FileAppender and open the file designated by filename
. The opened filename will become the
+ * output destination for this appender.
+ *
+ *
+ * The file will be appended to.
+ *
+ */
+ public RollingFileAppender(final Layout layout, final String filename) throws IOException {
+ super(layout, filename);
+ }
+
+ /**
+ * Gets the value of the MaxBackupIndex option.
+ */
+ public int getMaxBackupIndex() {
+ return maxBackupIndex;
+ }
+
+ /**
+ * Gets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ * @since 1.1
+ */
+ public long getMaximumFileSize() {
+ return maxFileSize;
+ }
+
+ /**
+ * Implements the usual roll over behaviour.
+ *
+ * If MaxBackupIndex
is positive, then files {File.1
, ...,
+ * File.MaxBackupIndex -1
} are renamed to {File.2
, ..., File.MaxBackupIndex
}.
+ * Moreover, File
is renamed File.1
and closed. A new File
is created to receive
+ * further log output.
+ *
+ *
+ * If MaxBackupIndex
is equal to zero, then the File
is truncated with no backup files
+ * created.
+ *
+ */
+ @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "The filename comes from a system property.")
+ public // synchronization not necessary since doAppend is alreasy synched
+ void rollOver() {
+ File target;
+ File file;
+
+ if (qw != null) {
+ final long size = ((CountingQuietWriter) qw).getCount();
+ LogLog.debug("rolling over count=" + size);
+ // if operation fails, do not roll again until
+ // maxFileSize more bytes are written
+ nextRollover = size + maxFileSize;
+ }
+ LogLog.debug("maxBackupIndex=" + maxBackupIndex);
+
+ boolean renameSucceeded = true;
+ // If maxBackups <= 0, then there is no file renaming to be done.
+ if (maxBackupIndex > 0) {
+ // Delete the oldest file, to keep Windows happy.
+ file = new File(fileName + '.' + maxBackupIndex);
+ if (file.exists()) renameSucceeded = file.delete();
+
+ // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
+ for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
+ file = new File(fileName + "." + i);
+ if (file.exists()) {
+ target = new File(fileName + '.' + (i + 1));
+ LogLog.debug("Renaming file " + file + " to " + target);
+ renameSucceeded = file.renameTo(target);
+ }
+ }
+
+ if (renameSucceeded) {
+ // Rename fileName to fileName.1
+ target = new File(fileName + "." + 1);
+
+ this.closeFile(); // keep windows happy.
+
+ file = new File(fileName);
+ LogLog.debug("Renaming file " + file + " to " + target);
+ renameSucceeded = file.renameTo(target);
+ //
+ // if file rename failed, reopen file with append = true
+ //
+ if (!renameSucceeded) {
+ try {
+ this.setFile(fileName, true, bufferedIO, bufferSize);
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("setFile(" + fileName + ", true) call failed.", e);
+ }
+ }
+ }
+ }
+
+ //
+ // if all renames were successful, then
+ //
+ if (renameSucceeded) {
+ try {
+ // This will also close the file. This is OK since multiple
+ // close operations are safe.
+ this.setFile(fileName, false, bufferedIO, bufferSize);
+ nextRollover = 0;
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("setFile(" + fileName + ", false) call failed.", e);
+ }
+ }
+ }
+
+ @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "The file name comes from a configuration file.")
+ public synchronized void setFile(
+ final String fileName, final boolean append, final boolean bufferedIO, final int bufferSize)
+ throws IOException {
+ super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
+ if (append) {
+ final File f = new File(fileName);
+ ((CountingQuietWriter) qw).setCount(f.length());
+ }
+ }
+
+ /**
+ * Sets the maximum number of backup files to keep around.
+ *
+ *
+ * The MaxBackupIndex option determines how many backup files are kept before the oldest is erased. This option
+ * takes a positive integer value. If set to zero, then there will be no backup files and the log file will be truncated
+ * when it reaches MaxFileSize
.
+ *
+ */
+ public void setMaxBackupIndex(final int maxBackups) {
+ this.maxBackupIndex = maxBackups;
+ }
+
+ /**
+ * Sets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ *
+ * This method is equivalent to {@link #setMaxFileSize} except that it is required for differentiating the setter taking
+ * a long
argument from the setter taking a String
argument by the JavaBeans
+ * {@link java.beans.Introspector Introspector}.
+ *
+ *
+ * @see #setMaxFileSize(String)
+ */
+ public void setMaximumFileSize(long maxFileSize) {
+ this.maxFileSize = maxFileSize;
+ }
+
+ /**
+ * Sets the maximum size that the output file is allowed to reach before being rolled over to backup files.
+ *
+ *
+ * In configuration files, the MaxFileSize option takes an long integer in the range 0 - 2^63. You can specify
+ * the value with the suffixes "KB", "MB" or "GB" so that the integer is interpreted being expressed respectively in
+ * kilobytes, megabytes or gigabytes. For example, the value "10KB" will be interpreted as 10240.
+ *
+ */
+ public void setMaxFileSize(final String value) {
+ maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
+ }
+
+ protected void setQWForFiles(final Writer writer) {
+ this.qw = new CountingQuietWriter(writer, errorHandler);
+ }
+
+ /**
+ * This method differentiates RollingFileAppender from its super class.
+ *
+ * @since 0.9.0
+ */
+ protected void subAppend(final LoggingEvent event) {
+ super.subAppend(event);
+ if (fileName != null && qw != null) {
+ final long size = ((CountingQuietWriter) qw).getCount();
+ if (size >= maxFileSize && size >= nextRollover) {
+ rollOver();
+ }
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java
new file mode 100644
index 00000000000..1fd0ee76f26
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/SimpleLayout.java
@@ -0,0 +1,42 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Simple-layout.
+ */
+public class SimpleLayout extends Layout {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String format(final LoggingEvent theEvent) {
+ return Strings.EMPTY;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean ignoresThrowable() {
+ return true;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/VectorAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/VectorAppender.java
new file mode 100644
index 00000000000..678705a6f74
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/VectorAppender.java
@@ -0,0 +1,77 @@
+/*
+ * 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.log4j;
+
+import java.util.Vector;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Appends logging events to a vector.
+ */
+public class VectorAppender extends AppenderSkeleton {
+
+ public Vector vector;
+
+ public VectorAppender() {
+ vector = new Vector();
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void activateOptions() {
+ // noop
+ }
+
+ /**
+ * This method is called by the {@link AppenderSkeleton#doAppend} method.
+ *
+ */
+ @Override
+ public void append(final LoggingEvent event) {
+ // System.out.println("---Vector appender called with message ["+event.getRenderedMessage()+"].");
+ // System.out.flush();
+ try {
+ Thread.sleep(100);
+ } catch (final Exception e) {
+ // ignore
+ }
+ vector.addElement(event);
+ }
+
+ @Override
+ public synchronized void close() {
+ if (this.closed) {
+ return;
+ }
+ this.closed = true;
+ }
+
+ public Vector getVector() {
+ return vector;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java
new file mode 100644
index 00000000000..23f731077bc
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/WriterAppender.java
@@ -0,0 +1,381 @@
+/*
+ * 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.log4j;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * WriterAppender appends log events to a {@link Writer} or an
+ * {@link OutputStream} depending on the user's choice.
+ */
+public class WriterAppender extends AppenderSkeleton {
+ private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+ /**
+ * Immediate flush means that the underlying writer or output stream
+ * will be flushed at the end of each append operation unless shouldFlush()
+ * is overridden. Immediate
+ * flush is slower but ensures that each append request is actually
+ * written. If immediateFlush
is set to
+ * false
, then there is a good chance that the last few
+ * logs events are not actually written to persistent media if and
+ * when the application crashes.
+ *
+ * The immediateFlush
variable is set to
+ * true
by default.
+ */
+ protected boolean immediateFlush = true;
+
+ /**
+ * The encoding to use when writing.
The
+ * encoding
variable is set to null
by
+ * default which results in the utilization of the system's default
+ * encoding.
+ */
+ protected String encoding;
+
+ /**
+ * This is the {@link QuietWriter quietWriter} where we will write
+ * to.
+ */
+ protected QuietWriter qw;
+
+ /**
+ * This default constructor does nothing.
+ */
+ public WriterAppender() {}
+
+ /**
+ * Instantiate a WriterAppender and set the output destination to a
+ * new {@link OutputStreamWriter} initialized with os
+ * as its {@link OutputStream}.
+ * @param layout The Layout.
+ * @param os The OutputStream.
+ */
+ public WriterAppender(final Layout layout, final OutputStream os) {
+ this(layout, new OutputStreamWriter(os));
+ }
+
+ /**
+ * Instantiate a WriterAppender and set the output destination to
+ * writer
.
+ *
+ *
The writer
must have been previously opened by
+ * the user.
+ *
+ * @param layout The Layout.
+ * @param writer The Writer.
+ */
+ public WriterAppender(final Layout layout, final Writer writer) {
+ this.layout = layout;
+ this.setWriter(writer);
+ }
+
+ /**
+ * Returns value of the ImmediateFlush option.
+ * @return the value of the immediate flush setting.
+ */
+ public boolean getImmediateFlush() {
+ return immediateFlush;
+ }
+
+ /**
+ * If the ImmediateFlush option is set to
+ * true
, the appender will flush at the end of each
+ * write. This is the default behavior. If the option is set to
+ * false
, then the underlying stream can defer writing
+ * to physical medium to a later time.
+ *
+ *
Avoiding the flush operation at the end of each append results in
+ * a performance gain of 10 to 20 percent. However, there is safety
+ * tradeoff involved in skipping flushing. Indeed, when flushing is
+ * skipped, then it is likely that the last few log events will not
+ * be recorded on disk when the application exits. This is a high
+ * price to pay even for a 20% performance gain.
+ *
+ * @param value the value to set the immediate flush setting to.
+ */
+ public void setImmediateFlush(final boolean value) {
+ immediateFlush = value;
+ }
+
+ /**
+ * Does nothing.
+ */
+ @Override
+ public void activateOptions() {}
+
+ /**
+ * This method is called by the {@link AppenderSkeleton#doAppend}
+ * method.
+ *
+ *
If the output stream exists and is writable then write a log
+ * statement to the output stream. Otherwise, write a single warning
+ * message to System.err
.
+ *
+ *
The format of the output will depend on this appender's
+ * layout.
+ */
+ @Override
+ public void append(final LoggingEvent event) {
+
+ // Reminder: the nesting of calls is:
+ //
+ // doAppend()
+ // - check threshold
+ // - filter
+ // - append();
+ // - checkEntryConditions();
+ // - subAppend();
+
+ if (!checkEntryConditions()) {
+ return;
+ }
+ subAppend(event);
+ }
+
+ /**
+ * This method determines if there is a sense in attempting to append.
+ *
+ *
It checks whether there is a set output target and also if
+ * there is a set layout. If these checks fail, then the boolean
+ * value false
is returned.
+ * @return true if appending is allowed, false otherwise.
+ */
+ protected boolean checkEntryConditions() {
+ if (this.closed) {
+ LOGGER.warn("Not allowed to write to a closed appender.");
+ return false;
+ }
+
+ if (this.qw == null) {
+ errorHandler.error("No output stream or file set for the appender named [" + name + "].");
+ return false;
+ }
+
+ if (this.layout == null) {
+ errorHandler.error("No layout set for the appender named [" + name + "].");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Close this appender instance. The underlying stream or writer is
+ * also closed.
+ *
+ *
Closed appenders cannot be reused.
+ *
+ * @see #setWriter
+ * @since 0.8.4
+ */
+ @Override
+ public synchronized void close() {
+ if (this.closed) {
+ return;
+ }
+ this.closed = true;
+ writeFooter();
+ reset();
+ }
+
+ /**
+ * Close the underlying {@link Writer}.
+ */
+ protected void closeWriter() {
+ if (qw != null) {
+ try {
+ qw.close();
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // There is do need to invoke an error handler at this late
+ // stage.
+ LOGGER.error("Could not close " + qw, e);
+ }
+ }
+ }
+
+ /**
+ * Returns an OutputStreamWriter when passed an OutputStream. The
+ * encoding used will depend on the value of the
+ * encoding
property. If the encoding value is
+ * specified incorrectly the writer will be opened using the default
+ * system encoding (an error message will be printed to the LOGGER.
+ * @param os The OutputStream.
+ * @return The OutputStreamWriter.
+ */
+ protected OutputStreamWriter createWriter(final OutputStream os) {
+ OutputStreamWriter retval = null;
+
+ final String enc = getEncoding();
+ if (enc != null) {
+ try {
+ retval = new OutputStreamWriter(os, enc);
+ } catch (final UnsupportedEncodingException e) {
+ LOGGER.warn("Error initializing output writer: encoding {} is not supported.", enc, e);
+ }
+ }
+ if (retval == null) {
+ retval = new OutputStreamWriter(os);
+ }
+ return retval;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(final String value) {
+ encoding = value;
+ }
+
+ /**
+ * Set the {@link ErrorHandler} for this WriterAppender and also the
+ * underlying {@link QuietWriter} if any.
+ */
+ @Override
+ public synchronized void setErrorHandler(final ErrorHandler eh) {
+ if (eh == null) {
+ LOGGER.warn("You have tried to set a null error-handler.");
+ } else {
+ this.errorHandler = eh;
+ if (this.qw != null) {
+ this.qw.setErrorHandler(eh);
+ }
+ }
+ }
+
+ /**
+ *
Sets the Writer where the log output will go. The
+ * specified Writer must be opened by the user and be
+ * writable.
+ *
+ *
The java.io.Writer
will be closed when the
+ * appender instance is closed.
+ *
+ *
+ *
WARNING: Logging to an unopened Writer will fail.
+ *
+ *
+ * @param writer An already opened Writer.
+ */
+ public synchronized void setWriter(final Writer writer) {
+ reset();
+ this.qw = new QuietWriter(writer, errorHandler);
+ // this.tp = new TracerPrintWriter(qw);
+ writeHeader();
+ }
+
+ /**
+ * Actual writing occurs here.
+ *
+ *
Most subclasses of WriterAppender
will need to
+ * override this method.
+ * @param event The event to log.
+ *
+ * @since 0.9.0
+ */
+ protected void subAppend(final LoggingEvent event) {
+ this.qw.write(this.layout.format(event));
+
+ if (layout.ignoresThrowable()) {
+ final String[] s = event.getThrowableStrRep();
+ if (s != null) {
+ final int len = s.length;
+ for (int i = 0; i < len; i++) {
+ this.qw.write(s[i]);
+ this.qw.write(Layout.LINE_SEP);
+ }
+ }
+ }
+
+ if (shouldFlush(event)) {
+ this.qw.flush();
+ }
+ }
+
+ /**
+ * The WriterAppender requires a layout. Hence, this method returns
+ * true
.
+ */
+ @Override
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ /**
+ * Clear internal references to the writer and other variables.
+ *
+ * Subclasses can override this method for an alternate closing
+ * behavior.
+ */
+ protected void reset() {
+ closeWriter();
+ this.qw = null;
+ // this.tp = null;
+ }
+
+ /**
+ * Write a footer as produced by the embedded layout's {@link
+ * Layout#getFooter} method.
+ */
+ protected void writeFooter() {
+ if (layout != null) {
+ final String f = layout.getFooter();
+ if (f != null && this.qw != null) {
+ this.qw.write(f);
+ this.qw.flush();
+ }
+ }
+ }
+
+ /**
+ * Write a header as produced by the embedded layout's {@link
+ * Layout#getHeader} method.
+ */
+ protected void writeHeader() {
+ if (layout != null) {
+ final String h = layout.getHeader();
+ if (h != null && this.qw != null) {
+ this.qw.write(h);
+ }
+ }
+ }
+
+ /**
+ * Determines whether the writer should be flushed after
+ * this event is written.
+ * @param event The event to log.
+ * @return true if the writer should be flushed.
+ *
+ * @since 1.2.16
+ */
+ protected boolean shouldFlush(final LoggingEvent event) {
+ return immediateFlush;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
new file mode 100644
index 00000000000..0e3bdd1c232
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderAdapter.java
@@ -0,0 +1,101 @@
+/*
+ * 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.log4j.bridge;
+
+import java.io.Serializable;
+import org.apache.log4j.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Property;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Binds a Log4j 1.x Appender to Log4j 2.
+ */
+public final class AppenderAdapter {
+
+ private final Appender appender;
+ private final Adapter adapter;
+
+ /**
+ * Adapts a Log4j 1.x appender into a Log4j 2.x appender. Applying this method
+ * on the result of
+ * {@link AppenderWrapper#adapt(org.apache.logging.log4j.core.Appender)} should
+ * return the original Log4j 2.x appender.
+ *
+ * @param appender a Log4j 1.x appender
+ * @return a Log4j 2.x appender or {@code null} if the parameter is {@code null}
+ */
+ public static org.apache.logging.log4j.core.Appender adapt(final Appender appender) {
+ if (appender instanceof org.apache.logging.log4j.core.Appender) {
+ return (org.apache.logging.log4j.core.Appender) appender;
+ }
+ if (appender instanceof AppenderWrapper) {
+ return ((AppenderWrapper) appender).getAppender();
+ }
+ if (appender != null) {
+ return new AppenderAdapter(appender).getAdapter();
+ }
+ return null;
+ }
+
+ /**
+ * Constructor.
+ * @param appender The Appender to wrap.
+ */
+ private AppenderAdapter(final Appender appender) {
+ this.appender = appender;
+ final org.apache.logging.log4j.core.Filter appenderFilter = FilterAdapter.adapt(appender.getFilter());
+ String name = appender.getName();
+ if (Strings.isEmpty(name)) {
+ name = String.format("0x%08x", appender.hashCode());
+ }
+ this.adapter = new Adapter(name, appenderFilter, null, true, null);
+ }
+
+ public Adapter getAdapter() {
+ return adapter;
+ }
+
+ public class Adapter extends AbstractAppender {
+
+ protected Adapter(
+ final String name,
+ final Filter filter,
+ final Layout extends Serializable> layout,
+ final boolean ignoreExceptions,
+ final Property[] properties) {
+ super(name, filter, layout, ignoreExceptions, properties);
+ }
+
+ @Override
+ public void append(final LogEvent event) {
+ appender.doAppend(new LogEventAdapter(event));
+ }
+
+ @Override
+ public void stop() {
+ appender.close();
+ }
+
+ public Appender getAppender() {
+ return appender;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
new file mode 100644
index 00000000000..36a96be1315
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/AppenderWrapper.java
@@ -0,0 +1,146 @@
+/*
+ * 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.log4j.bridge;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderAdapter.Adapter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.filter.AbstractFilterable;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Wraps a Log4j 2 Appender in an empty Log4j 1 Appender so it can be extracted when constructing the configuration.
+ * Allows a Log4j 1 Appender to reference a Log4j 2 Appender.
+ */
+public class AppenderWrapper implements Appender {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private final org.apache.logging.log4j.core.Appender appender;
+
+ /**
+ * Adapts a Log4j 2.x appender into a Log4j 1.x appender. Applying this method
+ * on the result of {@link AppenderAdapter#adapt(Appender)} should return the
+ * original Log4j 1.x appender.
+ *
+ * @param appender a Log4j 2.x appender
+ * @return a Log4j 1.x appender or {@code null} if the parameter is {@code null}
+ */
+ public static Appender adapt(final org.apache.logging.log4j.core.Appender appender) {
+ if (appender instanceof Appender) {
+ return (Appender) appender;
+ }
+ if (appender instanceof Adapter) {
+ final Adapter adapter = (Adapter) appender;
+ // Don't unwrap an appender with filters
+ if (!adapter.hasFilter()) {
+ return adapter.getAppender();
+ }
+ }
+ if (appender != null) {
+ return new AppenderWrapper(appender);
+ }
+ return null;
+ }
+
+ /**
+ * Constructs a new instance for a Core Appender.
+ *
+ * @param appender a Core Appender.
+ */
+ public AppenderWrapper(final org.apache.logging.log4j.core.Appender appender) {
+ this.appender = appender;
+ }
+
+ /**
+ * Gets the wrapped Core Appender.
+ *
+ * @return the wrapped Core Appender.
+ */
+ public org.apache.logging.log4j.core.Appender getAppender() {
+ return appender;
+ }
+
+ @Override
+ public void addFilter(final Filter newFilter) {
+ if (appender instanceof AbstractFilterable) {
+ ((AbstractFilterable) appender).addFilter(FilterAdapter.adapt(newFilter));
+ } else {
+ LOGGER.warn("Unable to add filter to appender {}, it does not support filters", appender.getName());
+ }
+ }
+
+ @Override
+ public Filter getFilter() {
+ return null;
+ }
+
+ @Override
+ public void clearFilters() {
+ // noop
+ }
+
+ @Override
+ public void close() {
+ // Not supported with Log4j 2.
+ }
+
+ @Override
+ public void doAppend(final LoggingEvent event) {
+ if (event instanceof LogEventAdapter) {
+ appender.append(((LogEventAdapter) event).getEvent());
+ }
+ }
+
+ @Override
+ public String getName() {
+ return appender.getName();
+ }
+
+ @Override
+ public void setErrorHandler(final ErrorHandler errorHandler) {
+ appender.setHandler(new ErrorHandlerAdapter(errorHandler));
+ }
+
+ @Override
+ public ErrorHandler getErrorHandler() {
+ return ((ErrorHandlerAdapter) appender.getHandler()).getHandler();
+ }
+
+ @Override
+ public void setLayout(final Layout layout) {
+ // Log4j 2 doesn't support this.
+ }
+
+ @Override
+ public Layout getLayout() {
+ return new LayoutWrapper(appender.getLayout());
+ }
+
+ @Override
+ public void setName(final String name) {
+ // Log4j 2 doesn't support this.
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java
new file mode 100644
index 00000000000..72354d899b7
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/ErrorHandlerAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.log4j.bridge;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.logging.log4j.core.LogEvent;
+
+/**
+ * Makes a Log4j 1 ErrorHandler usable by a Log4j 2 Appender.
+ */
+public class ErrorHandlerAdapter implements org.apache.logging.log4j.core.ErrorHandler {
+
+ private final ErrorHandler errorHandler;
+
+ public ErrorHandlerAdapter(final ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ public ErrorHandler getHandler() {
+ return errorHandler;
+ }
+
+ @Override
+ public void error(final String msg) {
+ errorHandler.error(msg);
+ }
+
+ @Override
+ public void error(final String msg, final Throwable t) {
+ if (t instanceof Exception) {
+ errorHandler.error(msg, (Exception) t, 0);
+ } else {
+ errorHandler.error(msg);
+ }
+ }
+
+ @Override
+ public void error(final String msg, final LogEvent event, final Throwable t) {
+ if (t == null || t instanceof Exception) {
+ errorHandler.error(msg, (Exception) t, 0, new LogEventAdapter(event));
+ } else {
+ errorHandler.error(msg);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
new file mode 100644
index 00000000000..06204842eaa
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterAdapter.java
@@ -0,0 +1,112 @@
+/*
+ * 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.log4j.bridge;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.filter.AbstractFilter;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+
+/**
+ * Binds a Log4j 1.x Filter with Log4j 2.
+ */
+public final class FilterAdapter extends AbstractFilter {
+
+ private final Filter filter;
+
+ /**
+ * Adapts a Log4j 1.x filter into a Log4j 2.x filter. Applying this method to
+ * the result of
+ * {@link FilterWrapper#adapt(org.apache.logging.log4j.core.Filter)} should
+ * return the original Log4j 2.x filter.
+ *
+ * @param filter a Log4j 1.x filter
+ * @return a Log4j 2.x filter or {@code null} if the parameter is {@code null}
+ */
+ public static org.apache.logging.log4j.core.Filter adapt(final Filter filter) {
+ if (filter instanceof org.apache.logging.log4j.core.Filter) {
+ return (org.apache.logging.log4j.core.Filter) filter;
+ }
+ // Don't unwrap the head of a filter chain
+ if (filter instanceof FilterWrapper && filter.getNext() == null) {
+ return ((FilterWrapper) filter).getFilter();
+ }
+ if (filter != null) {
+ return new FilterAdapter(filter);
+ }
+ return null;
+ }
+
+ /**
+ * Appends one filter to another using Log4j 2.x concatenation utilities.
+ * @param first
+ * @param second
+ * @return
+ */
+ public static Filter addFilter(final Filter first, final Filter second) {
+ if (first == null) {
+ return second;
+ }
+ if (second == null) {
+ return first;
+ }
+ final CompositeFilter composite;
+ if (first instanceof FilterWrapper && ((FilterWrapper) first).getFilter() instanceof CompositeFilter) {
+ composite = (CompositeFilter) ((FilterWrapper) first).getFilter();
+ } else {
+ composite = CompositeFilter.createFilters(adapt(first));
+ }
+ return FilterWrapper.adapt(composite.addFilter(adapt(second)));
+ }
+
+ private FilterAdapter(final Filter filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public Result filter(final LogEvent event) {
+ final LoggingEvent loggingEvent = new LogEventAdapter(event);
+ Filter next = filter;
+ while (next != null) {
+ switch (next.decide(loggingEvent)) {
+ case Filter.ACCEPT:
+ return Result.ACCEPT;
+ case Filter.DENY:
+ return Result.DENY;
+ default:
+ }
+ next = next.getNext();
+ }
+ return Result.NEUTRAL;
+ }
+
+ /**
+ * Gets the actual filter.
+ *
+ * @return the actual filter.
+ * @since 2.17.1
+ */
+ public Filter getFilter() {
+ return filter;
+ }
+
+ @Override
+ public void start() {
+ filter.activateOptions();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java
new file mode 100644
index 00000000000..aadb2dbdb3e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/FilterWrapper.java
@@ -0,0 +1,68 @@
+/*
+ * 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.log4j.bridge;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This acts as a container for Log4j 2 Filters to be attached to Log4j 1 components. However, the Log4j 2
+ * Filters will always be called directly so this class just acts as a container.
+ */
+public class FilterWrapper extends Filter {
+
+ private final org.apache.logging.log4j.core.Filter filter;
+
+ /**
+ * Adapts a Log4j 2.x filter into a Log4j 1.x filter. Applying this method to
+ * the result of {@link FilterAdapter#adapt(Filter)} should return the original
+ * Log4j 1.x filter.
+ *
+ * @param filter a Log4j 2.x filter
+ * @return a Log4j 1.x filter or {@code null} if the parameter is {@code null}
+ */
+ public static Filter adapt(final org.apache.logging.log4j.core.Filter filter) {
+ if (filter instanceof Filter) {
+ return (Filter) filter;
+ }
+ if (filter instanceof FilterAdapter) {
+ return ((FilterAdapter) filter).getFilter();
+ }
+ if (filter != null) {
+ return new FilterWrapper(filter);
+ }
+ return null;
+ }
+
+ public FilterWrapper(final org.apache.logging.log4j.core.Filter filter) {
+ this.filter = filter;
+ }
+
+ public org.apache.logging.log4j.core.Filter getFilter() {
+ return filter;
+ }
+
+ /**
+ * This method is never called.
+ * @param event The LoggingEvent to decide upon.
+ * @return 0
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ return 0;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
new file mode 100644
index 00000000000..0321225cd25
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutAdapter.java
@@ -0,0 +1,94 @@
+/*
+ * 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.log4j.bridge;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.log4j.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.layout.ByteBufferDestination;
+
+/**
+ * Class Description goes here.
+ */
+public final class LayoutAdapter implements org.apache.logging.log4j.core.Layout {
+ private final Layout layout;
+
+ /**
+ * Adapts a Log4j 1.x layout into a Log4j 2.x layout. Applying this method to
+ * the result of
+ * {@link LayoutWrapper#adapt(org.apache.logging.log4j.core.Layout)} should
+ * return the original Log4j 2.x layout.
+ *
+ * @param layout a Log4j 1.x layout
+ * @return a Log4j 2.x layout or {@code null} if the parameter is {@code null}
+ */
+ public static org.apache.logging.log4j.core.Layout> adapt(final Layout layout) {
+ if (layout instanceof LayoutWrapper) {
+ return ((LayoutWrapper) layout).getLayout();
+ }
+ if (layout != null) {
+ return new LayoutAdapter(layout);
+ }
+ return null;
+ }
+
+ private LayoutAdapter(final Layout layout) {
+ this.layout = layout;
+ }
+
+ public Layout getLayout() {
+ return layout;
+ }
+
+ @Override
+ public byte[] getFooter() {
+ return layout.getFooter() == null ? null : layout.getFooter().getBytes();
+ }
+
+ @Override
+ public byte[] getHeader() {
+ return layout.getHeader() == null ? null : layout.getHeader().getBytes();
+ }
+
+ @Override
+ public byte[] toByteArray(final LogEvent event) {
+ final String result = layout.format(new LogEventAdapter(event));
+ return result == null ? null : result.getBytes();
+ }
+
+ @Override
+ public String toSerializable(final LogEvent event) {
+ return layout.format(new LogEventAdapter(event));
+ }
+
+ @Override
+ public String getContentType() {
+ return layout.getContentType();
+ }
+
+ @Override
+ public Map getContentFormat() {
+ return new HashMap<>();
+ }
+
+ @Override
+ public void encode(final LogEvent event, final ByteBufferDestination destination) {
+ final byte[] data = toByteArray(event);
+ destination.writeBytes(data, 0, data.length);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
new file mode 100644
index 00000000000..6ac6ff65208
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LayoutWrapper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.log4j.bridge;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Bridge between the Log4j 1 Layout and a Log4j 2 Layout.
+ */
+public class LayoutWrapper extends Layout {
+
+ private final org.apache.logging.log4j.core.Layout> layout;
+
+ /**
+ * Adapts a Log4j 2.x layout into a Log4j 1.x layout. Applying this method to
+ * the result of {@link LayoutAdapter#adapt(Layout)} should return the original
+ * Log4j 1.x layout.
+ *
+ * @param layout a Log4j 2.x layout
+ * @return a Log4j 1.x layout or {@code null} if the parameter is {@code null}
+ */
+ public static Layout adapt(final org.apache.logging.log4j.core.Layout> layout) {
+ if (layout instanceof LayoutAdapter) {
+ return ((LayoutAdapter) layout).getLayout();
+ }
+ if (layout != null) {
+ return new LayoutWrapper(layout);
+ }
+ return null;
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param layout The layout to wrap.
+ */
+ public LayoutWrapper(final org.apache.logging.log4j.core.Layout> layout) {
+ this.layout = layout;
+ }
+
+ @Override
+ public String format(final LoggingEvent event) {
+ return layout.toSerializable(((LogEventAdapter) event).getEvent()).toString();
+ }
+
+ /**
+ * Unwraps.
+ *
+ * @return The wrapped object.
+ */
+ public org.apache.logging.log4j.core.Layout> getLayout() {
+ return this.layout;
+ }
+
+ @Override
+ public boolean ignoresThrowable() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("LayoutWrapper [layout=%s]", layout);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
new file mode 100644
index 00000000000..d3684731326
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventAdapter.java
@@ -0,0 +1,216 @@
+/*
+ * 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.log4j.bridge;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.core.util.Throwables;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Converts a Log4j 2 LogEvent into the components needed by a Log4j 1.x LoggingEvent.
+ * This class requires Log4j 2.
+ */
+public class LogEventAdapter extends LoggingEvent {
+
+ public static final long JVM_START_TIME = initStartTime();
+
+ private final LogEvent event;
+
+ public LogEventAdapter(final LogEvent event) {
+ this.event = event;
+ }
+
+ /**
+ * Returns the time when the application started, in milliseconds
+ * elapsed since 01.01.1970.
+ * @return the time when the JVM started.
+ */
+ public static long getJvmStartTime() {
+ return JVM_START_TIME;
+ }
+
+ /**
+ * Returns the result of {@code ManagementFactory.getRuntimeMXBean().getStartTime()},
+ * or the current system time if JMX is not available.
+ */
+ private static long initStartTime() {
+ // We'd like to call ManagementFactory.getRuntimeMXBean().getStartTime(),
+ // but Google App Engine throws a java.lang.NoClassDefFoundError
+ // "java.lang.management.ManagementFactory is a restricted class".
+ // The reflection is necessary because without it, Google App Engine
+ // will refuse to initialize this class.
+ try {
+ final Class> factoryClass = Loader.loadSystemClass("java.lang.management.ManagementFactory");
+ final Method getRuntimeMXBean = factoryClass.getMethod("getRuntimeMXBean");
+ final Object runtimeMXBean = getRuntimeMXBean.invoke(null);
+
+ final Class> runtimeMXBeanClass = Loader.loadSystemClass("java.lang.management.RuntimeMXBean");
+ final Method getStartTime = runtimeMXBeanClass.getMethod("getStartTime");
+ return (Long) getStartTime.invoke(runtimeMXBean);
+ } catch (final Throwable t) {
+ StatusLogger.getLogger()
+ .error(
+ "Unable to call ManagementFactory.getRuntimeMXBean().getStartTime(), "
+ + "using system time for OnStartupTriggeringPolicy",
+ t);
+ // We have little option but to declare "now" as the beginning of time.
+ return System.currentTimeMillis();
+ }
+ }
+
+ public LogEvent getEvent() {
+ return this.event;
+ }
+
+ /**
+ * Set the location information for this logging event. The collected
+ * information is cached for future use.
+ */
+ @Override
+ public LocationInfo getLocationInformation() {
+ return new LocationInfo(event.getSource());
+ }
+
+ /**
+ * Return the level of this event. Use this form instead of directly
+ * accessing the level
field.
+ */
+ @Override
+ public Level getLevel() {
+ return OptionConverter.convertLevel(event.getLevel());
+ }
+
+ /**
+ * Return the name of the logger. Use this form instead of directly
+ * accessing the categoryName
field.
+ */
+ @Override
+ public String getLoggerName() {
+ return event.getLoggerName();
+ }
+
+ @Override
+ public long getTimeStamp() {
+ return event.getTimeMillis();
+ }
+
+ /**
+ * Gets the logger of the event.
+ */
+ @Override
+ public Category getLogger() {
+ return Category.getInstance(event.getLoggerName());
+ }
+
+ /*
+ Return the message for this logging event.
+ */
+ @Override
+ public Object getMessage() {
+ return event.getMessage();
+ }
+
+ /*
+ * This method returns the NDC for this event.
+ */
+ @Override
+ public String getNDC() {
+ return event.getContextStack().toString();
+ }
+
+ /*
+ Returns the context corresponding to the key
parameter.
+ */
+ @Override
+ public Object getMDC(final String key) {
+ if (event.getContextData() != null) {
+ return event.getContextData().getValue(key);
+ }
+ return null;
+ }
+
+ /**
+ * Obtain a copy of this thread's MDC prior to serialization or
+ * asynchronous logging.
+ */
+ @Override
+ public void getMDCCopy() {}
+
+ @Override
+ public String getRenderedMessage() {
+ return event.getMessage().getFormattedMessage();
+ }
+
+ @Override
+ public String getThreadName() {
+ return event.getThreadName();
+ }
+
+ /**
+ * Returns the throwable information contained within this
+ * event. May be null
if there is no such information.
+ *
+ * Note that the {@link Throwable} object contained within a
+ * {@link ThrowableInformation} does not survive serialization.
+ *
+ * @since 1.1
+ */
+ @Override
+ public ThrowableInformation getThrowableInformation() {
+ if (event.getThrown() != null) {
+ return new ThrowableInformation(event.getThrown());
+ }
+ return null;
+ }
+
+ /**
+ * Return this event's throwable's string[] representaion.
+ */
+ @Override
+ public String[] getThrowableStrRep() {
+ if (event.getThrown() != null) {
+ return Throwables.toStringList(event.getThrown()).toArray(Strings.EMPTY_ARRAY);
+ }
+ return null;
+ }
+
+ @Override
+ public String getProperty(final String key) {
+ return event.getContextData().getValue(key);
+ }
+
+ @Override
+ public Set getPropertyKeySet() {
+ return event.getContextData().toMap().keySet();
+ }
+
+ @Override
+ public Map getProperties() {
+ return event.getContextData().toMap();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java
new file mode 100644
index 00000000000..3c5448b210d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.java
@@ -0,0 +1,236 @@
+/*
+ * 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.log4j.bridge;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.log4j.NDC;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.ThrowableProxy;
+import org.apache.logging.log4j.core.time.Instant;
+import org.apache.logging.log4j.core.time.MutableInstant;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.MutableThreadContextStack;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.TriConsumer;
+
+/**
+ * Exposes a Log4j 1 logging event as a Log4j 2 LogEvent.
+ */
+public class LogEventWrapper implements LogEvent {
+
+ private final LoggingEvent event;
+ private final ContextDataMap contextData;
+ private final MutableThreadContextStack contextStack;
+ private Thread thread;
+
+ public LogEventWrapper(final LoggingEvent event) {
+ this.event = event;
+ this.contextData = new ContextDataMap(event.getProperties());
+ this.contextStack = new MutableThreadContextStack(NDC.cloneStack());
+ this.thread =
+ Objects.equals(event.getThreadName(), Thread.currentThread().getName()) ? Thread.currentThread() : null;
+ }
+
+ @Override
+ public LogEvent toImmutable() {
+ return this;
+ }
+
+ @Override
+ public Map getContextMap() {
+ return contextData;
+ }
+
+ @Override
+ public ReadOnlyStringMap getContextData() {
+ return contextData;
+ }
+
+ @Override
+ public ThreadContext.ContextStack getContextStack() {
+ return contextStack;
+ }
+
+ @Override
+ public String getLoggerFqcn() {
+ return null;
+ }
+
+ @Override
+ public Level getLevel() {
+ return OptionConverter.convertLevel(event.getLevel());
+ }
+
+ @Override
+ public String getLoggerName() {
+ return event.getLoggerName();
+ }
+
+ @Override
+ public Marker getMarker() {
+ return null;
+ }
+
+ @Override
+ public Message getMessage() {
+ return new SimpleMessage(event.getRenderedMessage());
+ }
+
+ @Override
+ public long getTimeMillis() {
+ return event.getTimeStamp();
+ }
+
+ @Override
+ public Instant getInstant() {
+ final MutableInstant mutable = new MutableInstant();
+ mutable.initFromEpochMilli(event.getTimeStamp(), 0);
+ return mutable;
+ }
+
+ @Override
+ public StackTraceElement getSource() {
+ final LocationInfo info = event.getLocationInformation();
+ return new StackTraceElement(
+ info.getClassName(), info.getMethodName(), info.getFileName(), Integer.parseInt(info.getLineNumber()));
+ }
+
+ @Override
+ public String getThreadName() {
+ return event.getThreadName();
+ }
+
+ @Override
+ public long getThreadId() {
+ final Thread thread = getThread();
+ return thread != null ? thread.getId() : 0;
+ }
+
+ @Override
+ public int getThreadPriority() {
+ final Thread thread = getThread();
+ return thread != null ? thread.getPriority() : 0;
+ }
+
+ private Thread getThread() {
+ if (thread == null && event.getThreadName() != null) {
+ for (Thread thread : Thread.getAllStackTraces().keySet()) {
+ if (thread.getName().equals(event.getThreadName())) {
+ this.thread = thread;
+ return thread;
+ }
+ }
+ }
+ return thread;
+ }
+
+ @Override
+ public Throwable getThrown() {
+ final ThrowableInformation throwableInformation = event.getThrowableInformation();
+ return throwableInformation == null ? null : throwableInformation.getThrowable();
+ }
+
+ @Override
+ public ThrowableProxy getThrownProxy() {
+ return null;
+ }
+
+ @Override
+ public boolean isEndOfBatch() {
+ return false;
+ }
+
+ @Override
+ public boolean isIncludeLocation() {
+ return false;
+ }
+
+ @Override
+ public void setEndOfBatch(final boolean endOfBatch) {}
+
+ @Override
+ public void setIncludeLocation(final boolean locationRequired) {}
+
+ @Override
+ public long getNanoTime() {
+ return 0;
+ }
+
+ private static class ContextDataMap extends HashMap implements ReadOnlyStringMap {
+
+ ContextDataMap(final Map map) {
+ if (map != null) {
+ super.putAll(map);
+ }
+ }
+
+ @Override
+ public Map toMap() {
+ return this;
+ }
+
+ @Override
+ public boolean containsKey(final String key) {
+ return super.containsKey(key);
+ }
+
+ @Override
+ public void forEach(final BiConsumer action) {
+ super.forEach((k, v) -> action.accept(k, (V) v));
+ }
+
+ @Override
+ public void forEach(final TriConsumer action, final S state) {
+ super.forEach((k, v) -> action.accept(k, (V) v, state));
+ }
+
+ @Override
+ public V getValue(final String key) {
+ return (V) super.get(key);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj instanceof ReadOnlyStringMap) {
+ // Convert to maps and compare
+ final Map thisMap = toMap();
+ final Map otherMap = ((ReadOnlyStringMap) obj).toMap();
+ return thisMap.equals(otherMap);
+ }
+ return super.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return toMap().hashCode();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java
new file mode 100644
index 00000000000..8e82ece2c26
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.log4j.bridge;
+
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+
+/**
+ * Binds a Log4j 1.x RewritePolicy to Log4j 2.
+ */
+public class RewritePolicyAdapter implements org.apache.logging.log4j.core.appender.rewrite.RewritePolicy {
+
+ private final RewritePolicy policy;
+
+ /**
+ * Constructor.
+ * @param policy The Rewrite policy.
+ */
+ public RewritePolicyAdapter(final RewritePolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public LogEvent rewrite(final LogEvent source) {
+ final LoggingEvent event = policy.rewrite(new LogEventAdapter(source));
+ return event instanceof LogEventAdapter ? ((LogEventAdapter) event).getEvent() : new LogEventWrapper(event);
+ }
+
+ public RewritePolicy getPolicy() {
+ return this.policy;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java
new file mode 100644
index 00000000000..64f4dfe4e9c
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyWrapper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.log4j.bridge;
+
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+
+/**
+ * Binds a Log4j 2 RewritePolicy to Log4j 1.
+ */
+public class RewritePolicyWrapper implements RewritePolicy {
+
+ private final org.apache.logging.log4j.core.appender.rewrite.RewritePolicy policy;
+
+ public RewritePolicyWrapper(final org.apache.logging.log4j.core.appender.rewrite.RewritePolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public LoggingEvent rewrite(final LoggingEvent source) {
+ final LogEvent event =
+ source instanceof LogEventAdapter ? ((LogEventAdapter) source).getEvent() : new LogEventWrapper(source);
+ return new LogEventAdapter(policy.rewrite(event));
+ }
+
+ public org.apache.logging.log4j.core.appender.rewrite.RewritePolicy getPolicy() {
+ return policy;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
new file mode 100644
index 00000000000..dfd4319b409
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/AbstractBuilder.java
@@ -0,0 +1,246 @@
+/*
+ * 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.log4j.builders;
+
+import static org.apache.log4j.xml.XmlConfiguration.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+/**
+ * Base class for Log4j 1 component builders.
+ *
+ * @param The type to build.
+ */
+public abstract class AbstractBuilder implements Builder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ protected static final String FILE_PARAM = "File";
+ protected static final String APPEND_PARAM = "Append";
+ protected static final String BUFFERED_IO_PARAM = "BufferedIO";
+ protected static final String BUFFER_SIZE_PARAM = "BufferSize";
+ protected static final String IMMEDIATE_FLUSH_PARAM = "ImmediateFlush";
+ protected static final String MAX_SIZE_PARAM = "MaxFileSize";
+ protected static final String MAX_BACKUP_INDEX = "MaxBackupIndex";
+ protected static final String RELATIVE = "RELATIVE";
+ protected static final String NULL = "NULL";
+
+ private final String prefix;
+ private final Properties properties;
+
+ public AbstractBuilder() {
+ this(null, new Properties());
+ }
+
+ public AbstractBuilder(final String prefix, final Properties props) {
+ this.prefix = prefix != null ? prefix + "." : null;
+ this.properties = (Properties) props.clone();
+ final Map map = new HashMap<>();
+ System.getProperties().forEach((k, v) -> map.put(k.toString(), v.toString()));
+ props.forEach((k, v) -> map.put(k.toString(), v.toString()));
+ // normalize keys to lower case for case-insensitive access.
+ props.forEach((k, v) -> map.put(toBeanKey(k.toString()), v.toString()));
+ props.entrySet().forEach(e -> this.properties.put(toBeanKey(e.getKey().toString()), e.getValue()));
+ }
+
+ protected static org.apache.logging.log4j.core.Filter buildFilters(final String level, final Filter filter) {
+ Filter head = null;
+ if (level != null) {
+ final org.apache.logging.log4j.core.Filter thresholdFilter = ThresholdFilter.createFilter(
+ OptionConverter.convertLevel(level, Level.TRACE),
+ org.apache.logging.log4j.core.Filter.Result.NEUTRAL,
+ org.apache.logging.log4j.core.Filter.Result.DENY);
+ head = new FilterWrapper(thresholdFilter);
+ }
+ if (filter != null) {
+ head = FilterAdapter.addFilter(head, filter);
+ }
+ return FilterAdapter.adapt(head);
+ }
+
+ private String capitalize(final String value) {
+ if (Strings.isEmpty(value) || Character.isUpperCase(value.charAt(0))) {
+ return value;
+ }
+ final char[] chars = value.toCharArray();
+ chars[0] = Character.toUpperCase(chars[0]);
+ return new String(chars);
+ }
+
+ public boolean getBooleanProperty(final String key, final boolean defaultValue) {
+ return Boolean.parseBoolean(getProperty(key, Boolean.toString(defaultValue)));
+ }
+
+ public boolean getBooleanProperty(final String key) {
+ return getBooleanProperty(key, false);
+ }
+
+ protected boolean getBooleanValueAttribute(final Element element) {
+ return Boolean.parseBoolean(getValueAttribute(element));
+ }
+
+ public int getIntegerProperty(final String key, final int defaultValue) {
+ String value = null;
+ try {
+ value = getProperty(key);
+ if (value != null) {
+ return Integer.parseInt(value);
+ }
+ } catch (final Exception ex) {
+ LOGGER.warn("Error converting value {} of {} to an integer: {}", value, key, ex.getMessage());
+ }
+ return defaultValue;
+ }
+
+ public long getLongProperty(final String key, final long defaultValue) {
+ String value = null;
+ try {
+ value = getProperty(key);
+ if (value != null) {
+ return Long.parseLong(value);
+ }
+ } catch (final Exception ex) {
+ LOGGER.warn("Error converting value {} of {} to a long: {}", value, key, ex.getMessage());
+ }
+ return defaultValue;
+ }
+
+ protected String getNameAttribute(final Element element) {
+ return element.getAttribute(NAME_ATTR);
+ }
+
+ protected String getNameAttributeKey(final Element element) {
+ return toBeanKey(element.getAttribute(NAME_ATTR));
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public String getProperty(final String key) {
+ return getProperty(key, null);
+ }
+
+ public String getProperty(final String key, final String defaultValue) {
+ String value = properties.getProperty(prefix + toJavaKey(key));
+ value = value != null ? value : properties.getProperty(prefix + toBeanKey(key), defaultValue);
+ value = value != null ? substVars(value) : defaultValue;
+ return value != null ? value.trim() : defaultValue;
+ }
+
+ protected String getValueAttribute(final Element element) {
+ return getValueAttribute(element, null);
+ }
+
+ protected String getValueAttribute(final Element element, final String defaultValue) {
+ final String attribute = element.getAttribute(VALUE_ATTR);
+ return substVars(attribute != null ? attribute.trim() : defaultValue);
+ }
+
+ protected String substVars(final String value) {
+ return OptionConverter.substVars(value, properties);
+ }
+
+ String toBeanKey(final String value) {
+ return capitalize(value);
+ }
+
+ String toJavaKey(final String value) {
+ return uncapitalize(value);
+ }
+
+ private String uncapitalize(final String value) {
+ if (Strings.isEmpty(value) || Character.isLowerCase(value.charAt(0))) {
+ return value;
+ }
+ final char[] chars = value.toCharArray();
+ chars[0] = Character.toLowerCase(chars[0]);
+ return new String(chars);
+ }
+
+ protected void set(final String name, final Element element, final AtomicBoolean ref) {
+ final String value = getValueAttribute(element);
+ if (value == null) {
+ LOGGER.warn("No value for {} parameter, using default {}", name, ref);
+ } else {
+ ref.set(Boolean.parseBoolean(value));
+ }
+ }
+
+ protected void set(final String name, final Element element, final AtomicInteger ref) {
+ final String value = getValueAttribute(element);
+ if (value == null) {
+ LOGGER.warn("No value for {} parameter, using default {}", name, ref);
+ } else {
+ try {
+ ref.set(Integer.parseInt(value));
+ } catch (NumberFormatException e) {
+ LOGGER.warn(
+ "{} parsing {} parameter, using default {}: {}",
+ e.getClass().getName(),
+ name,
+ ref,
+ e.getMessage(),
+ e);
+ }
+ }
+ }
+
+ protected void set(final String name, final Element element, final AtomicLong ref) {
+ final String value = getValueAttribute(element);
+ if (value == null) {
+ LOGGER.warn("No value for {} parameter, using default {}", name, ref);
+ } else {
+ try {
+ ref.set(Long.parseLong(value));
+ } catch (NumberFormatException e) {
+ LOGGER.warn(
+ "{} parsing {} parameter, using default {}: {}",
+ e.getClass().getName(),
+ name,
+ ref,
+ e.getMessage(),
+ e);
+ }
+ }
+ }
+
+ protected void set(final String name, final Element element, final AtomicReference ref) {
+ final String value = getValueAttribute(element);
+ if (value == null) {
+ LOGGER.warn("No value for {} parameter, using default {}", name, ref);
+ } else {
+ ref.set(value);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BooleanHolder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BooleanHolder.java
new file mode 100644
index 00000000000..564800d8b76
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BooleanHolder.java
@@ -0,0 +1,39 @@
+/*
+ * 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.log4j.builders;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Holds Boolean values created inside of a Lambda expression.
+ *
+ * @deprecated Use {@link AtomicReference}.
+ */
+@Deprecated
+public class BooleanHolder extends Holder {
+
+ public BooleanHolder() {
+ super(Boolean.FALSE);
+ }
+
+ @Override
+ public void set(final Boolean value) {
+ if (value != null) {
+ super.set(value);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Builder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Builder.java
new file mode 100644
index 00000000000..a829b7afbc9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Builder.java
@@ -0,0 +1,26 @@
+/*
+ * 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.log4j.builders;
+
+/**
+ * A marker interface for Log4j 1.x component builders.
+ *
+ * @param The type to build.
+ */
+public interface Builder {
+ // empty
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
new file mode 100644
index 00000000000..e799531a361
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
@@ -0,0 +1,189 @@
+/*
+ * 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.log4j.builders;
+
+import static org.apache.logging.log4j.util.Strings.toRootLowerCase;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.function.Function;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.bridge.RewritePolicyWrapper;
+import org.apache.log4j.builders.appender.AppenderBuilder;
+import org.apache.log4j.builders.filter.FilterBuilder;
+import org.apache.log4j.builders.layout.LayoutBuilder;
+import org.apache.log4j.builders.rewrite.RewritePolicyBuilder;
+import org.apache.log4j.builders.rolling.TriggeringPolicyBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
+import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Element;
+
+/**
+ *
+ */
+public class BuilderManager {
+
+ /** Plugin category. */
+ public static final String CATEGORY = "Log4j Builder";
+
+ public static final Appender INVALID_APPENDER = new AppenderWrapper(null);
+ public static final Filter INVALID_FILTER = new FilterWrapper(null);
+ public static final Layout INVALID_LAYOUT = new LayoutWrapper(null);
+ public static final RewritePolicy INVALID_REWRITE_POLICY = new RewritePolicyWrapper(null);
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final Class>[] CONSTRUCTOR_PARAMS = new Class[] {String.class, Properties.class};
+ private final Map> plugins;
+
+ /**
+ * Constructs a new instance.
+ */
+ public BuilderManager() {
+ final PluginManager manager = new PluginManager(CATEGORY);
+ manager.collectPlugins();
+ plugins = manager.getPlugins();
+ }
+
+ private , U> T createBuilder(
+ final PluginType plugin, final String prefix, final Properties props) {
+ if (plugin == null) {
+ return null;
+ }
+ try {
+ final Class clazz = plugin.getPluginClass();
+ if (AbstractBuilder.class.isAssignableFrom(clazz)) {
+ return clazz.getConstructor(CONSTRUCTOR_PARAMS).newInstance(prefix, props);
+ }
+ final T builder = LoaderUtil.newInstanceOf(clazz);
+ // Reasonable message instead of `ClassCastException`
+ if (!Builder.class.isAssignableFrom(clazz)) {
+ LOGGER.warn("Unable to load plugin: builder {} does not implement {}", clazz, Builder.class);
+ return null;
+ }
+ return builder;
+ } catch (final ReflectiveOperationException ex) {
+ LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private PluginType getPlugin(final String className) {
+ Objects.requireNonNull(plugins, "plugins");
+ Objects.requireNonNull(className, "className");
+ final String key = toRootLowerCase(className).trim();
+ final PluginType> pluginType = plugins.get(key);
+ if (pluginType == null) {
+ LOGGER.warn("Unable to load plugin class name {} with key {}", className, key);
+ }
+ return (PluginType) pluginType;
+ }
+
+ private , U> U newInstance(
+ final PluginType plugin, final Function consumer, final U invalidValue) {
+ if (plugin != null) {
+ try {
+ final T builder = LoaderUtil.newInstanceOf(plugin.getPluginClass());
+ if (builder != null) {
+ final U result = consumer.apply(builder);
+ // returning an empty wrapper is short for "we support this legacy class, but it has validation
+ // errors"
+ return result != null ? result : invalidValue;
+ }
+ } catch (final ReflectiveOperationException ex) {
+ LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+ }
+ }
+ return null;
+ }
+
+ public , T> T parse(
+ final String className,
+ final String prefix,
+ final Properties props,
+ final PropertiesConfiguration config,
+ final T invalidValue) {
+ final P parser = createBuilder(getPlugin(className), prefix, props);
+ if (parser != null) {
+ final T value = parser.parse(config);
+ return value != null ? value : invalidValue;
+ }
+ return null;
+ }
+
+ public Appender parseAppender(
+ final String className, final Element appenderElement, final XmlConfiguration config) {
+ return newInstance(
+ this.>getPlugin(className),
+ b -> b.parseAppender(appenderElement, config),
+ INVALID_APPENDER);
+ }
+
+ public Appender parseAppender(
+ final String name,
+ final String className,
+ final String prefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration config) {
+ final AppenderBuilder builder = createBuilder(getPlugin(className), prefix, props);
+ if (builder != null) {
+ final Appender appender = builder.parseAppender(name, prefix, layoutPrefix, filterPrefix, props, config);
+ return appender != null ? appender : INVALID_APPENDER;
+ }
+ return null;
+ }
+
+ public Filter parseFilter(final String className, final Element filterElement, final XmlConfiguration config) {
+ return newInstance(
+ this.getPlugin(className), b -> b.parse(filterElement, config), INVALID_FILTER);
+ }
+
+ public Layout parseLayout(final String className, final Element layoutElement, final XmlConfiguration config) {
+ return newInstance(
+ this.getPlugin(className), b -> b.parse(layoutElement, config), INVALID_LAYOUT);
+ }
+
+ public RewritePolicy parseRewritePolicy(
+ final String className, final Element rewriteElement, final XmlConfiguration config) {
+ return newInstance(
+ this.getPlugin(className),
+ b -> b.parse(rewriteElement, config),
+ INVALID_REWRITE_POLICY);
+ }
+
+ public TriggeringPolicy parseTriggeringPolicy(
+ final String className, final Element policyElement, final XmlConfiguration config) {
+ return newInstance(
+ this.getPlugin(className),
+ b -> b.parse(policyElement, config),
+ (TriggeringPolicy) null);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Holder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Holder.java
new file mode 100644
index 00000000000..d116af58602
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Holder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.log4j.builders;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Provides a place to hold values generated inside of a Lambda expression.
+ *
+ * @param The type of object referred to by this reference.
+ * @deprecated Use {@link AtomicReference}.
+ */
+@Deprecated
+public class Holder {
+ private V value;
+
+ public Holder() {}
+
+ public Holder(final V defaultValue) {
+ this.value = defaultValue;
+ }
+
+ public void set(final V value) {
+ this.value = value;
+ }
+
+ public V get() {
+ return value;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Parser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Parser.java
new file mode 100644
index 00000000000..c4b2ee590ff
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/Parser.java
@@ -0,0 +1,46 @@
+/*
+ * 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.log4j.builders;
+
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.w3c.dom.Element;
+
+/**
+ * Parses DOM and properties.
+ *
+ * @param The type to build.
+ */
+public interface Parser extends Builder {
+
+ /**
+ * Parses a DOM Element.
+ *
+ * @param element the DOM Element.
+ * @param config the XML configuration.
+ * @return parse result.
+ */
+ T parse(Element element, XmlConfiguration config);
+
+ /**
+ * Parses a PropertiesConfigurationt.
+ *
+ * @param element the PropertiesConfiguration.
+ * @return parse result.
+ */
+ T parse(PropertiesConfiguration config);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
new file mode 100644
index 00000000000..c19e2c12ba2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AppenderBuilder.java
@@ -0,0 +1,42 @@
+/*
+ * 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.log4j.builders.appender;
+
+import java.util.Properties;
+import org.apache.log4j.Appender;
+import org.apache.log4j.builders.Builder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.w3c.dom.Element;
+
+/**
+ * Define an Appender Builder.
+ *
+ * @param The type to build.
+ */
+public interface AppenderBuilder extends Builder {
+
+ Appender parseAppender(Element element, XmlConfiguration configuration);
+
+ Appender parseAppender(
+ String name,
+ String appenderPrefix,
+ String layoutPrefix,
+ String filterPrefix,
+ Properties props,
+ PropertiesConfiguration configuration);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
new file mode 100644
index 00000000000..000a6a1f61d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/AsyncAppenderBuilder.java
@@ -0,0 +1,173 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.AsyncAppender;
+import org.apache.logging.log4j.core.appender.AsyncAppender.Builder;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+/**
+ * Build an Async Appender
+ */
+@Plugin(name = "org.apache.log4j.AsyncAppender", category = CATEGORY)
+public class AsyncAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String BLOCKING_PARAM = "Blocking";
+ private static final String INCLUDE_LOCATION_PARAM = "IncludeLocation";
+
+ public AsyncAppenderBuilder() {}
+
+ public AsyncAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference> appenderRefs = new AtomicReference<>(new ArrayList<>());
+ final AtomicBoolean blocking = new AtomicBoolean();
+ final AtomicBoolean includeLocation = new AtomicBoolean();
+ final AtomicReference level = new AtomicReference<>("trace");
+ final AtomicInteger bufferSize = new AtomicInteger(1024);
+ final AtomicReference filter = new AtomicReference<>();
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG:
+ final Appender appender = config.findAppenderByReference(currentElement);
+ if (appender != null) {
+ appenderRefs.get().add(appender.getName());
+ }
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG: {
+ switch (getNameAttributeKey(currentElement)) {
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case BLOCKING_PARAM:
+ set(BLOCKING_PARAM, currentElement, blocking);
+ break;
+ case INCLUDE_LOCATION_PARAM:
+ set(INCLUDE_LOCATION_PARAM, currentElement, includeLocation);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ }
+ break;
+ }
+ }
+ });
+ return createAppender(
+ name,
+ level.get(),
+ appenderRefs.get().toArray(Strings.EMPTY_ARRAY),
+ blocking.get(),
+ bufferSize.get(),
+ includeLocation.get(),
+ filter.get(),
+ config);
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final String appenderRef = getProperty(APPENDER_REF_TAG);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final boolean blocking = getBooleanProperty(BLOCKING_PARAM);
+ final boolean includeLocation = getBooleanProperty(INCLUDE_LOCATION_PARAM);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 1024);
+ if (appenderRef == null) {
+ LOGGER.error("No appender references configured for AsyncAppender {}", name);
+ return null;
+ }
+ final Appender appender = configuration.parseAppender(props, appenderRef);
+ if (appender == null) {
+ LOGGER.error("Cannot locate Appender {}", appenderRef);
+ return null;
+ }
+ return createAppender(
+ name, level, new String[] {appenderRef}, blocking, bufferSize, includeLocation, filter, configuration);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final String level,
+ final String[] appenderRefs,
+ final boolean blocking,
+ final int bufferSize,
+ final boolean includeLocation,
+ final Filter filter,
+ final T configuration) {
+ if (appenderRefs.length == 0) {
+ LOGGER.error("No appender references configured for AsyncAppender {}", name);
+ return null;
+ }
+ final Level logLevel = OptionConverter.convertLevel(level, Level.TRACE);
+ final AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+ int index = 0;
+ for (final String appenderRef : appenderRefs) {
+ refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+ }
+ final Builder builder = AsyncAppender.newBuilder();
+ builder.setFilter(FilterAdapter.adapt(filter));
+ return AppenderWrapper.adapt(builder.setName(name)
+ .setAppenderRefs(refs)
+ .setBlocking(blocking)
+ .setBufferSize(bufferSize)
+ .setIncludeLocation(includeLocation)
+ .setConfiguration(configuration)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
new file mode 100644
index 00000000000..19a98ecb5a5
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/ConsoleAppenderBuilder.java
@@ -0,0 +1,168 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Console Appender
+ */
+@Plugin(name = "org.apache.log4j.ConsoleAppender", category = CATEGORY)
+public class ConsoleAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String SYSTEM_OUT = "System.out";
+ private static final String SYSTEM_ERR = "System.err";
+ private static final String TARGET_PARAM = "Target";
+ private static final String FOLLOW_PARAM = "Follow";
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public ConsoleAppenderBuilder() {}
+
+ public ConsoleAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference target = new AtomicReference<>(SYSTEM_OUT);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean follow = new AtomicBoolean();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG: {
+ switch (getNameAttributeKey(currentElement)) {
+ case TARGET_PARAM:
+ final String value = getValueAttribute(currentElement);
+ if (value == null) {
+ LOGGER.warn("No value supplied for target parameter. Defaulting to " + SYSTEM_OUT);
+ } else {
+ switch (value) {
+ case SYSTEM_OUT:
+ target.set(SYSTEM_OUT);
+ break;
+ case SYSTEM_ERR:
+ target.set(SYSTEM_ERR);
+ break;
+ default:
+ LOGGER.warn(
+ "Invalid value \"{}\" for target parameter. Using default of {}",
+ value,
+ SYSTEM_OUT);
+ }
+ }
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case FOLLOW_PARAM:
+ set(FOLLOW_PARAM, currentElement, follow);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ }
+ });
+ return createAppender(
+ name,
+ layout.get(),
+ filter.get(),
+ level.get(),
+ target.get(),
+ immediateFlush.get(),
+ follow.get(),
+ config);
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final String target = getProperty(TARGET_PARAM);
+ final boolean follow = getBooleanProperty(FOLLOW_PARAM);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM);
+ return createAppender(name, layout, filter, level, target, immediateFlush, follow, configuration);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final Layout layout,
+ final Filter filter,
+ final String level,
+ final String target,
+ final boolean immediateFlush,
+ final boolean follow,
+ final T configuration) {
+ final org.apache.logging.log4j.core.Layout> consoleLayout = LayoutAdapter.adapt(layout);
+
+ final org.apache.logging.log4j.core.Filter consoleFilter = buildFilters(level, filter);
+ final ConsoleAppender.Target consoleTarget =
+ SYSTEM_ERR.equals(target) ? ConsoleAppender.Target.SYSTEM_ERR : ConsoleAppender.Target.SYSTEM_OUT;
+ return AppenderWrapper.adapt(ConsoleAppender.newBuilder()
+ .setName(name)
+ .setTarget(consoleTarget)
+ .setFollow(follow)
+ .setLayout(consoleLayout)
+ .setFilter(consoleFilter)
+ .setConfiguration(configuration)
+ .setImmediateFlush(immediateFlush)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
new file mode 100644
index 00000000000..4920aaa80ae
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/DailyRollingFileAppenderBuilder.java
@@ -0,0 +1,204 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Daily Rolling File Appender
+ */
+@Plugin(name = "org.apache.log4j.DailyRollingFileAppender", category = CATEGORY)
+public class DailyRollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String DEFAULT_DATE_PATTERN = ".yyyy-MM-dd";
+ private static final String DATE_PATTERN_PARAM = "DatePattern";
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public DailyRollingFileAppenderBuilder() {}
+
+ public DailyRollingFileAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference fileName = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ final AtomicBoolean append = new AtomicBoolean(true);
+ final AtomicBoolean bufferedIo = new AtomicBoolean();
+ final AtomicInteger bufferSize = new AtomicInteger(8192);
+ final AtomicReference datePattern = new AtomicReference<>(DEFAULT_DATE_PATTERN);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FILE_PARAM:
+ set(FILE_PARAM, currentElement, fileName);
+ break;
+ case APPEND_PARAM:
+ set(APPEND_PARAM, currentElement, append);
+ break;
+ case BUFFERED_IO_PARAM:
+ set(BUFFERED_IO_PARAM, currentElement, bufferedIo);
+ break;
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case DATE_PATTERN_PARAM:
+ set(DATE_PATTERN_PARAM, currentElement, datePattern);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+ return createAppender(
+ name,
+ layout.get(),
+ filter.get(),
+ fileName.get(),
+ append.get(),
+ immediateFlush.get(),
+ level.get(),
+ bufferedIo.get(),
+ bufferSize.get(),
+ datePattern.get(),
+ config);
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String fileName = getProperty(FILE_PARAM);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final boolean append = getBooleanProperty(APPEND_PARAM, true);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM, true);
+ final boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM, false);
+ final int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 8192);
+ final String datePattern = getProperty(DATE_PATTERN_PARAM, DEFAULT_DATE_PATTERN);
+ return createAppender(
+ name,
+ layout,
+ filter,
+ fileName,
+ append,
+ immediateFlush,
+ level,
+ bufferedIo,
+ bufferSize,
+ datePattern,
+ configuration);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final Layout layout,
+ final Filter filter,
+ final String fileName,
+ final boolean append,
+ boolean immediateFlush,
+ final String level,
+ final boolean bufferedIo,
+ final int bufferSize,
+ final String datePattern,
+ final T configuration) {
+
+ final org.apache.logging.log4j.core.Layout> fileLayout = LayoutAdapter.adapt(layout);
+ if (bufferedIo) {
+ immediateFlush = false;
+ }
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
+ LOGGER.error("Unable to create DailyRollingFileAppender, no file name provided");
+ return null;
+ }
+ final String filePattern = fileName + "%d{" + datePattern + "}";
+ final TriggeringPolicy timePolicy =
+ TimeBasedTriggeringPolicy.newBuilder().setModulate(true).build();
+ final TriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(timePolicy);
+ final RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+ .setConfig(configuration)
+ .setMax(Integer.toString(Integer.MAX_VALUE))
+ .build();
+ return AppenderWrapper.adapt(RollingFileAppender.newBuilder()
+ .setName(name)
+ .setConfiguration(configuration)
+ .setLayout(fileLayout)
+ .setFilter(fileFilter)
+ .setFileName(fileName)
+ .setAppend(append)
+ .setBufferedIo(bufferedIo)
+ .setBufferSize(bufferSize)
+ .setImmediateFlush(immediateFlush)
+ .setFilePattern(filePattern)
+ .setPolicy(policy)
+ .setStrategy(strategy)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/EnhancedRollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/EnhancedRollingFileAppenderBuilder.java
new file mode 100644
index 00000000000..26377e7d1be
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/EnhancedRollingFileAppenderBuilder.java
@@ -0,0 +1,293 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.rolling.RollingFileAppender", category = CATEGORY)
+public class EnhancedRollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String TIME_BASED_ROLLING_POLICY = "org.apache.log4j.rolling.TimeBasedRollingPolicy";
+ private static final String FIXED_WINDOW_ROLLING_POLICY = "org.apache.log4j.rolling.FixedWindowRollingPolicy";
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String TRIGGERING_TAG = "triggeringPolicy";
+ private static final String ROLLING_TAG = "rollingPolicy";
+ private static final int DEFAULT_MIN_INDEX = 1;
+ private static final int DEFAULT_MAX_INDEX = 7;
+ private static final String ACTIVE_FILE_PARAM = "ActiveFileName";
+ private static final String FILE_PATTERN_PARAM = "FileNamePattern";
+ private static final String MIN_INDEX_PARAM = "MinIndex";
+ private static final String MAX_INDEX_PARAM = "MaxIndex";
+
+ public EnhancedRollingFileAppenderBuilder() {}
+
+ public EnhancedRollingFileAppenderBuilder(final String prefix, final Properties properties) {
+ super(prefix, properties);
+ }
+
+ private void parseRollingPolicy(
+ final Element element,
+ final XmlConfiguration configuration,
+ final AtomicReference rollingPolicyClassName,
+ final AtomicReference activeFileName,
+ final AtomicReference fileNamePattern,
+ final AtomicInteger minIndex,
+ final AtomicInteger maxIndex) {
+ rollingPolicyClassName.set(configuration.subst(element.getAttribute("class"), getProperties()));
+ forEachElement(element.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case ACTIVE_FILE_PARAM:
+ set(ACTIVE_FILE_PARAM, currentElement, activeFileName);
+ break;
+ case FILE_PATTERN_PARAM:
+ set(FILE_PATTERN_PARAM, currentElement, fileNamePattern);
+ break;
+ case MIN_INDEX_PARAM:
+ set(MIN_INDEX_PARAM, currentElement, minIndex);
+ break;
+ case MAX_INDEX_PARAM:
+ set(MAX_INDEX_PARAM, currentElement, maxIndex);
+ }
+ }
+ });
+ }
+
+ @Override
+ public Appender parseAppender(final Element element, final XmlConfiguration configuration) {
+ // FileAppender
+ final String name = getNameAttribute(element);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference fileName = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ final AtomicBoolean append = new AtomicBoolean(true);
+ final AtomicBoolean bufferedIo = new AtomicBoolean();
+ final AtomicInteger bufferSize = new AtomicInteger(8192);
+ // specific to RollingFileAppender
+ final AtomicReference rollingPolicyClassName = new AtomicReference<>();
+ final AtomicReference activeFileName = new AtomicReference<>();
+ final AtomicReference fileNamePattern = new AtomicReference<>();
+ final AtomicInteger minIndex = new AtomicInteger(DEFAULT_MIN_INDEX);
+ final AtomicInteger maxIndex = new AtomicInteger(DEFAULT_MAX_INDEX);
+ final AtomicReference triggeringPolicy = new AtomicReference<>();
+ forEachElement(element.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case ROLLING_TAG:
+ parseRollingPolicy(
+ currentElement,
+ configuration,
+ rollingPolicyClassName,
+ activeFileName,
+ fileNamePattern,
+ minIndex,
+ maxIndex);
+ break;
+ case TRIGGERING_TAG:
+ triggeringPolicy.set(configuration.parseTriggeringPolicy(currentElement));
+ break;
+ case LAYOUT_TAG:
+ layout.set(configuration.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ configuration.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FILE_PARAM:
+ set(FILE_PARAM, currentElement, fileName);
+ break;
+ case APPEND_PARAM:
+ set(APPEND_PARAM, currentElement, append);
+ break;
+ case BUFFERED_IO_PARAM:
+ set(BUFFERED_IO_PARAM, currentElement, bufferedIo);
+ break;
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+ return createAppender(
+ name,
+ layout.get(),
+ filter.get(),
+ fileName.get(),
+ level.get(),
+ immediateFlush.get(),
+ append.get(),
+ bufferedIo.get(),
+ bufferSize.get(),
+ rollingPolicyClassName.get(),
+ activeFileName.get(),
+ fileNamePattern.get(),
+ minIndex.get(),
+ maxIndex.get(),
+ triggeringPolicy.get(),
+ configuration);
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final String fileName = getProperty(FILE_PARAM);
+ final boolean append = getBooleanProperty(APPEND_PARAM, true);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM, true);
+ final boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM, false);
+ final int bufferSize = Integer.parseInt(getProperty(BUFFER_SIZE_PARAM, "8192"));
+ final String rollingPolicyClassName = getProperty(ROLLING_TAG);
+ final int minIndex = getIntegerProperty(ROLLING_TAG + "." + MIN_INDEX_PARAM, DEFAULT_MIN_INDEX);
+ final int maxIndex = getIntegerProperty(ROLLING_TAG + "." + MAX_INDEX_PARAM, DEFAULT_MAX_INDEX);
+ final String activeFileName = getProperty(ROLLING_TAG + "." + ACTIVE_FILE_PARAM);
+ final String fileNamePattern = getProperty(ROLLING_TAG + "." + FILE_PATTERN_PARAM);
+ final TriggeringPolicy triggeringPolicy =
+ configuration.parseTriggeringPolicy(props, appenderPrefix + "." + TRIGGERING_TAG);
+ return createAppender(
+ name,
+ layout,
+ filter,
+ fileName,
+ level,
+ immediateFlush,
+ append,
+ bufferedIo,
+ bufferSize,
+ rollingPolicyClassName,
+ activeFileName,
+ fileNamePattern,
+ minIndex,
+ maxIndex,
+ triggeringPolicy,
+ configuration);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final Layout layout,
+ final Filter filter,
+ final String fileName,
+ final String level,
+ final boolean immediateFlush,
+ final boolean append,
+ final boolean bufferedIo,
+ final int bufferSize,
+ final String rollingPolicyClassName,
+ final String activeFileName,
+ final String fileNamePattern,
+ final int minIndex,
+ final int maxIndex,
+ final TriggeringPolicy triggeringPolicy,
+ final Configuration configuration) {
+ final org.apache.logging.log4j.core.Layout> fileLayout = LayoutAdapter.adapt(layout);
+ final boolean actualImmediateFlush = bufferedIo ? false : immediateFlush;
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (rollingPolicyClassName == null) {
+ LOGGER.error("Unable to create RollingFileAppender, no rolling policy provided.");
+ return null;
+ }
+ final String actualFileName = activeFileName != null ? activeFileName : fileName;
+ if (actualFileName == null) {
+ LOGGER.error("Unable to create RollingFileAppender, no file name provided.");
+ return null;
+ }
+ if (fileNamePattern == null) {
+ LOGGER.error("Unable to create RollingFileAppender, no file name pattern provided.");
+ return null;
+ }
+ final DefaultRolloverStrategy.Builder rolloverStrategyBuilder = DefaultRolloverStrategy.newBuilder();
+ switch (rollingPolicyClassName) {
+ case FIXED_WINDOW_ROLLING_POLICY:
+ rolloverStrategyBuilder.setMin(Integer.toString(minIndex)).setMax(Integer.toString(maxIndex));
+ break;
+ case TIME_BASED_ROLLING_POLICY:
+ break;
+ default:
+ LOGGER.warn("Unsupported rolling policy: {}", rollingPolicyClassName);
+ }
+ final TriggeringPolicy actualTriggeringPolicy;
+ if (triggeringPolicy != null) {
+ actualTriggeringPolicy = triggeringPolicy;
+ } else if (rollingPolicyClassName.equals(TIME_BASED_ROLLING_POLICY)) {
+ actualTriggeringPolicy = TimeBasedTriggeringPolicy.newBuilder().build();
+ } else {
+ LOGGER.error("Unable to create RollingFileAppender, no triggering policy provided.");
+ return null;
+ }
+ return AppenderWrapper.adapt(RollingFileAppender.newBuilder()
+ .setAppend(append)
+ .setBufferedIo(bufferedIo)
+ .setBufferSize(bufferedIo ? bufferSize : 0)
+ .setConfiguration(configuration)
+ .setFileName(actualFileName)
+ .setFilePattern(fileNamePattern)
+ .setFilter(fileFilter)
+ .setImmediateFlush(actualImmediateFlush)
+ .setLayout(fileLayout)
+ .setName(name)
+ .setPolicy(actualTriggeringPolicy)
+ .setStrategy(rolloverStrategyBuilder.build())
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
new file mode 100644
index 00000000000..9d1f29a962e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/FileAppenderBuilder.java
@@ -0,0 +1,168 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.FileAppender", category = CATEGORY)
+public class FileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public FileAppenderBuilder() {}
+
+ public FileAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference fileName = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ final AtomicBoolean append = new AtomicBoolean(true);
+ final AtomicBoolean bufferedIo = new AtomicBoolean();
+ final AtomicInteger bufferSize = new AtomicInteger(8192);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FILE_PARAM:
+ set(FILE_PARAM, currentElement, fileName);
+ break;
+ case APPEND_PARAM:
+ set(APPEND_PARAM, currentElement, append);
+ break;
+ case BUFFERED_IO_PARAM:
+ set(BUFFERED_IO_PARAM, currentElement, bufferedIo);
+ break;
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+
+ return createAppender(
+ name,
+ config,
+ layout.get(),
+ filter.get(),
+ fileName.get(),
+ level.get(),
+ immediateFlush.get(),
+ append.get(),
+ bufferedIo.get(),
+ bufferSize.get());
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final String fileName = getProperty(FILE_PARAM);
+ final boolean append = getBooleanProperty(APPEND_PARAM, true);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM, true);
+ final boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM, false);
+ final int bufferSize = Integer.parseInt(getProperty(BUFFER_SIZE_PARAM, "8192"));
+ return createAppender(
+ name, configuration, layout, filter, fileName, level, immediateFlush, append, bufferedIo, bufferSize);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final Log4j1Configuration configuration,
+ final Layout layout,
+ final Filter filter,
+ final String fileName,
+ final String level,
+ boolean immediateFlush,
+ final boolean append,
+ final boolean bufferedIo,
+ final int bufferSize) {
+ final org.apache.logging.log4j.core.Layout> fileLayout = LayoutAdapter.adapt(layout);
+ if (bufferedIo) {
+ immediateFlush = false;
+ }
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
+ LOGGER.error("Unable to create FileAppender, no file name provided");
+ return null;
+ }
+ return AppenderWrapper.adapt(FileAppender.newBuilder()
+ .setName(name)
+ .setConfiguration(configuration)
+ .setLayout(fileLayout)
+ .setFilter(fileFilter)
+ .setFileName(fileName)
+ .setImmediateFlush(immediateFlush)
+ .setAppend(append)
+ .setBufferedIo(bufferedIo)
+ .setBufferSize(bufferSize)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
new file mode 100644
index 00000000000..622096c5c51
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/NullAppenderBuilder.java
@@ -0,0 +1,52 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+import java.util.Properties;
+import org.apache.log4j.Appender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Null Appender
+ */
+@Plugin(name = "org.apache.log4j.varia.NullAppender", category = CATEGORY)
+public class NullAppenderBuilder implements AppenderBuilder {
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = appenderElement.getAttribute("name");
+ return AppenderWrapper.adapt(NullAppender.createAppender(name));
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ return AppenderWrapper.adapt(NullAppender.createAppender(name));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
new file mode 100644
index 00000000000..6e8718a8b3a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
@@ -0,0 +1,161 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.APPENDER_REF_TAG;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.RewritePolicyAdapter;
+import org.apache.log4j.bridge.RewritePolicyWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Rewrite Appender
+ */
+@Plugin(name = "org.apache.log4j.rewrite.RewriteAppender", category = CATEGORY)
+public class RewriteAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String REWRITE_POLICY_TAG = "rewritePolicy";
+
+ public RewriteAppenderBuilder() {}
+
+ public RewriteAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference> appenderRefs = new AtomicReference<>(new ArrayList<>());
+ final AtomicReference rewritePolicyHolder = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG:
+ final Appender appender = config.findAppenderByReference(currentElement);
+ if (appender != null) {
+ appenderRefs.get().add(appender.getName());
+ }
+ break;
+ case REWRITE_POLICY_TAG:
+ final RewritePolicy policy = config.parseRewritePolicy(currentElement);
+ if (policy != null) {
+ rewritePolicyHolder.set(policy);
+ }
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ if (getNameAttributeKey(currentElement).equalsIgnoreCase(THRESHOLD_PARAM)) {
+ set(THRESHOLD_PARAM, currentElement, level);
+ }
+ break;
+ }
+ });
+ return createAppender(
+ name,
+ level.get(),
+ appenderRefs.get().toArray(Strings.EMPTY_ARRAY),
+ rewritePolicyHolder.get(),
+ filter.get(),
+ config);
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final String appenderRef = getProperty(APPENDER_REF_TAG);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String policyPrefix = appenderPrefix + ".rewritePolicy";
+ final String className = getProperty(policyPrefix);
+ final RewritePolicy policy = configuration
+ .getBuilderManager()
+ .parse(className, policyPrefix, props, configuration, BuilderManager.INVALID_REWRITE_POLICY);
+ final String level = getProperty(THRESHOLD_PARAM);
+ if (appenderRef == null) {
+ LOGGER.error("No appender references configured for RewriteAppender {}", name);
+ return null;
+ }
+ final Appender appender = configuration.parseAppender(props, appenderRef);
+ if (appender == null) {
+ LOGGER.error("Cannot locate Appender {}", appenderRef);
+ return null;
+ }
+ return createAppender(name, level, new String[] {appenderRef}, policy, filter, configuration);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final String level,
+ final String[] appenderRefs,
+ final RewritePolicy policy,
+ final Filter filter,
+ final T configuration) {
+ if (appenderRefs.length == 0) {
+ LOGGER.error("No appender references configured for RewriteAppender {}", name);
+ return null;
+ }
+ final Level logLevel = OptionConverter.convertLevel(level, Level.TRACE);
+ final AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+ int index = 0;
+ for (final String appenderRef : appenderRefs) {
+ refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+ }
+ final org.apache.logging.log4j.core.Filter rewriteFilter = buildFilters(level, filter);
+ org.apache.logging.log4j.core.appender.rewrite.RewritePolicy rewritePolicy;
+ if (policy instanceof RewritePolicyWrapper) {
+ rewritePolicy = ((RewritePolicyWrapper) policy).getPolicy();
+ } else {
+ rewritePolicy = new RewritePolicyAdapter(policy);
+ }
+ return AppenderWrapper.adapt(
+ RewriteAppender.createAppender(name, "true", refs, configuration, rewritePolicy, rewriteFilter));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
new file mode 100644
index 00000000000..72243829667
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RollingFileAppenderBuilder.java
@@ -0,0 +1,210 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.RollingFileAppender", category = CATEGORY)
+public class RollingFileAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String DEFAULT_MAX_SIZE = "10 MB";
+ private static final String DEFAULT_MAX_BACKUPS = "1";
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ public RollingFileAppenderBuilder() {}
+
+ public RollingFileAppenderBuilder(final String prefix, final Properties properties) {
+ super(prefix, properties);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference fileName = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ final AtomicBoolean append = new AtomicBoolean(true);
+ final AtomicBoolean bufferedIo = new AtomicBoolean();
+ final AtomicInteger bufferSize = new AtomicInteger(8192);
+ final AtomicReference maxSize = new AtomicReference<>(DEFAULT_MAX_SIZE);
+ final AtomicReference maxBackups = new AtomicReference<>(DEFAULT_MAX_BACKUPS);
+ final AtomicReference level = new AtomicReference<>();
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FILE_PARAM:
+ set(FILE_PARAM, currentElement, fileName);
+ break;
+ case APPEND_PARAM:
+ set(APPEND_PARAM, currentElement, append);
+ break;
+ case BUFFERED_IO_PARAM:
+ set(BUFFERED_IO_PARAM, currentElement, bufferedIo);
+ break;
+ case BUFFER_SIZE_PARAM:
+ set(BUFFER_SIZE_PARAM, currentElement, bufferSize);
+ break;
+ case MAX_BACKUP_INDEX:
+ set(MAX_BACKUP_INDEX, currentElement, maxBackups);
+ break;
+ case MAX_SIZE_PARAM:
+ set(MAX_SIZE_PARAM, currentElement, maxSize);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+ return createAppender(
+ name,
+ config,
+ layout.get(),
+ filter.get(),
+ append.get(),
+ bufferedIo.get(),
+ bufferSize.get(),
+ immediateFlush.get(),
+ fileName.get(),
+ level.get(),
+ maxSize.get(),
+ maxBackups.get());
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final String fileName = getProperty(FILE_PARAM);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final boolean append = getBooleanProperty(APPEND_PARAM, true);
+ final boolean immediateFlush = getBooleanProperty(IMMEDIATE_FLUSH_PARAM, true);
+ final boolean bufferedIo = getBooleanProperty(BUFFERED_IO_PARAM, false);
+ final int bufferSize = getIntegerProperty(BUFFER_SIZE_PARAM, 8192);
+ final String maxSize = getProperty(MAX_SIZE_PARAM, DEFAULT_MAX_SIZE);
+ final String maxBackups = getProperty(MAX_BACKUP_INDEX, DEFAULT_MAX_BACKUPS);
+ return createAppender(
+ name,
+ configuration,
+ layout,
+ filter,
+ append,
+ bufferedIo,
+ bufferSize,
+ immediateFlush,
+ fileName,
+ level,
+ maxSize,
+ maxBackups);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final Log4j1Configuration config,
+ final Layout layout,
+ final Filter filter,
+ final boolean append,
+ final boolean bufferedIo,
+ final int bufferSize,
+ boolean immediateFlush,
+ final String fileName,
+ final String level,
+ final String maxSize,
+ final String maxBackups) {
+ final org.apache.logging.log4j.core.Layout> fileLayout = LayoutAdapter.adapt(layout);
+ if (!bufferedIo) {
+ immediateFlush = false;
+ }
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ if (fileName == null) {
+ LOGGER.error("Unable to create RollingFileAppender, no file name provided");
+ return null;
+ }
+ final String filePattern = fileName + ".%i";
+ final SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy(maxSize);
+ final CompositeTriggeringPolicy policy = CompositeTriggeringPolicy.createPolicy(sizePolicy);
+ final RolloverStrategy strategy = DefaultRolloverStrategy.newBuilder()
+ .setConfig(config)
+ .setMax(maxBackups)
+ .setFileIndex("min")
+ .build();
+ return AppenderWrapper.adapt(RollingFileAppender.newBuilder()
+ .setName(name)
+ .setConfiguration(config)
+ .setLayout(fileLayout)
+ .setFilter(fileFilter)
+ .setAppend(append)
+ .setBufferedIo(bufferedIo)
+ .setBufferSize(bufferSize)
+ .setImmediateFlush(immediateFlush)
+ .setFileName(fileName)
+ .setFilePattern(filePattern)
+ .setPolicy(policy)
+ .setStrategy(strategy)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
new file mode 100644
index 00000000000..4e9ec159041
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SocketAppenderBuilder.java
@@ -0,0 +1,167 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Console Appender
+ */
+@Plugin(name = "org.apache.log4j.net.SocketAppender", category = CATEGORY)
+public class SocketAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String HOST_PARAM = "RemoteHost";
+ private static final String PORT_PARAM = "Port";
+ private static final String RECONNECTION_DELAY_PARAM = "ReconnectionDelay";
+ private static final int DEFAULT_PORT = 4560;
+
+ /**
+ * The default reconnection delay (30000 milliseconds or 30 seconds).
+ */
+ private static final int DEFAULT_RECONNECTION_DELAY = 30_000;
+
+ public static final Logger LOGGER = StatusLogger.getLogger();
+
+ public SocketAppenderBuilder() {}
+
+ public SocketAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final String host,
+ final int port,
+ final Layout layout,
+ final Filter filter,
+ final String level,
+ final boolean immediateFlush,
+ final int reconnectDelayMillis,
+ final T configuration) {
+ final org.apache.logging.log4j.core.Layout> actualLayout = LayoutAdapter.adapt(layout);
+ final org.apache.logging.log4j.core.Filter actualFilter = buildFilters(level, filter);
+ // @formatter:off
+ return AppenderWrapper.adapt(SocketAppender.newBuilder()
+ .setHost(host)
+ .setPort(port)
+ .setReconnectDelayMillis(reconnectDelayMillis)
+ .setName(name)
+ .setLayout(actualLayout)
+ .setFilter(actualFilter)
+ .setConfiguration(configuration)
+ .setImmediateFlush(immediateFlush)
+ .build());
+ // @formatter:on
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference host = new AtomicReference<>("localhost");
+ final AtomicInteger port = new AtomicInteger(DEFAULT_PORT);
+ final AtomicInteger reconnectDelay = new AtomicInteger(DEFAULT_RECONNECTION_DELAY);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean immediateFlush = new AtomicBoolean(true);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case HOST_PARAM:
+ set(HOST_PARAM, currentElement, host);
+ break;
+ case PORT_PARAM:
+ set(PORT_PARAM, currentElement, port);
+ break;
+ case RECONNECTION_DELAY_PARAM:
+ set(RECONNECTION_DELAY_PARAM, currentElement, reconnectDelay);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ case IMMEDIATE_FLUSH_PARAM:
+ set(IMMEDIATE_FLUSH_PARAM, currentElement, immediateFlush);
+ break;
+ }
+ break;
+ }
+ });
+ return createAppender(
+ name,
+ host.get(),
+ port.get(),
+ layout.get(),
+ filter.get(),
+ level.get(),
+ immediateFlush.get(),
+ reconnectDelay.get(),
+ config);
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ // @formatter:off
+ return createAppender(
+ name,
+ getProperty(HOST_PARAM),
+ getIntegerProperty(PORT_PARAM, DEFAULT_PORT),
+ configuration.parseLayout(layoutPrefix, name, props),
+ configuration.parseAppenderFilters(props, filterPrefix, name),
+ getProperty(THRESHOLD_PARAM),
+ getBooleanProperty(IMMEDIATE_FLUSH_PARAM),
+ getIntegerProperty(RECONNECTION_DELAY_PARAM, DEFAULT_RECONNECTION_DELAY),
+ configuration);
+ // @formatter:on
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
new file mode 100644
index 00000000000..8d4436e03eb
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.java
@@ -0,0 +1,212 @@
+/*
+ * 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.log4j.builders.appender;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.config.Log4j1Configuration.THRESHOLD_PARAM;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.io.Serializable;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.layout.Log4j1SyslogLayout;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.appender.SyslogAppender;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Strings;
+import org.w3c.dom.Element;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.net.SyslogAppender", category = CATEGORY)
+public class SyslogAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final String DEFAULT_HOST = "localhost";
+ private static int DEFAULT_PORT = 514;
+ private static final String DEFAULT_FACILITY = "LOCAL0";
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String FACILITY_PARAM = "Facility";
+ private static final String FACILITY_PRINTING_PARAM = "FacilityPrinting";
+ private static final String HEADER_PARAM = "Header";
+ private static final String PROTOCOL_PARAM = "Protocol";
+ private static final String SYSLOG_HOST_PARAM = "SyslogHost";
+
+ public SyslogAppenderBuilder() {}
+
+ public SyslogAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ final String name = getNameAttribute(appenderElement);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ final AtomicReference facility = new AtomicReference<>();
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicReference host = new AtomicReference<>();
+ final AtomicReference protocol = new AtomicReference<>(Protocol.TCP);
+ final AtomicBoolean header = new AtomicBoolean(false);
+ final AtomicBoolean facilityPrinting = new AtomicBoolean(false);
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ config.addFilter(filter, currentElement);
+ break;
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case FACILITY_PARAM:
+ set(FACILITY_PARAM, currentElement, facility);
+ break;
+ case FACILITY_PRINTING_PARAM:
+ set(FACILITY_PRINTING_PARAM, currentElement, facilityPrinting);
+ break;
+ case HEADER_PARAM:
+ set(HEADER_PARAM, currentElement, header);
+ break;
+ case PROTOCOL_PARAM:
+ protocol.set(Protocol.valueOf(getValueAttribute(currentElement, Protocol.TCP.name())));
+ break;
+ case SYSLOG_HOST_PARAM:
+ set(SYSLOG_HOST_PARAM, currentElement, host);
+ break;
+ case THRESHOLD_PARAM:
+ set(THRESHOLD_PARAM, currentElement, level);
+ break;
+ }
+ break;
+ }
+ });
+
+ return createAppender(
+ name,
+ config,
+ layout.get(),
+ facility.get(),
+ filter.get(),
+ host.get(),
+ level.get(),
+ protocol.get(),
+ header.get(),
+ facilityPrinting.get());
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final String level = getProperty(THRESHOLD_PARAM);
+ final String facility = getProperty(FACILITY_PARAM, DEFAULT_FACILITY);
+ final boolean facilityPrinting = getBooleanProperty(FACILITY_PRINTING_PARAM, false);
+ final boolean header = getBooleanProperty(HEADER_PARAM, false);
+ final String protocol = getProperty(PROTOCOL_PARAM, Protocol.TCP.name());
+ final String syslogHost = getProperty(SYSLOG_HOST_PARAM, DEFAULT_HOST + ":" + DEFAULT_PORT);
+
+ return createAppender(
+ name,
+ configuration,
+ layout,
+ facility,
+ filter,
+ syslogHost,
+ level,
+ Protocol.valueOf(protocol),
+ header,
+ facilityPrinting);
+ }
+
+ private Appender createAppender(
+ final String name,
+ final Log4j1Configuration configuration,
+ final Layout layout,
+ final String facility,
+ final Filter filter,
+ final String syslogHost,
+ final String level,
+ final Protocol protocol,
+ final boolean header,
+ final boolean facilityPrinting) {
+ final AtomicReference host = new AtomicReference<>();
+ final AtomicInteger port = new AtomicInteger();
+ resolveSyslogHost(syslogHost, host, port);
+ final org.apache.logging.log4j.core.Layout extends Serializable> messageLayout = LayoutAdapter.adapt(layout);
+ final Log4j1SyslogLayout appenderLayout = Log4j1SyslogLayout.newBuilder()
+ .setHeader(header)
+ .setFacility(Facility.toFacility(facility))
+ .setFacilityPrinting(facilityPrinting)
+ .setMessageLayout(messageLayout)
+ .build();
+
+ final org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ return AppenderWrapper.adapt(SyslogAppender.newSyslogAppenderBuilder()
+ .setName(name)
+ .setConfiguration(configuration)
+ .setLayout(appenderLayout)
+ .setFilter(fileFilter)
+ .setPort(port.get())
+ .setProtocol(protocol)
+ .setHost(host.get())
+ .build());
+ }
+
+ private void resolveSyslogHost(
+ final String syslogHost, final AtomicReference host, final AtomicInteger port) {
+ //
+ // If not an unbracketed IPv6 address then
+ // parse as a URL
+ //
+ final String[] parts = syslogHost != null ? syslogHost.split(":") : Strings.EMPTY_ARRAY;
+ if (parts.length == 1) {
+ host.set(parts[0]);
+ port.set(DEFAULT_PORT);
+ } else if (parts.length == 2) {
+ host.set(parts[0]);
+ port.set(Integer.parseInt(parts[1].trim()));
+ } else {
+ LOGGER.warn("Invalid {} setting: {}. Using default.", SYSLOG_HOST_PARAM, syslogHost);
+ host.set(DEFAULT_HOST);
+ port.set(DEFAULT_PORT);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/package-info.java
new file mode 100644
index 00000000000..49a049a8390
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@Open("org.apache.logging.log4j.core")
+package org.apache.log4j.builders.appender;
+
+import aQute.bnd.annotation.jpms.Open;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
new file mode 100644
index 00000000000..385d4d87975
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/DenyAllFilterBuilder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.log4j.builders.filter;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.DenyAllFilter;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.varia.DenyAllFilter", category = CATEGORY)
+public class DenyAllFilterBuilder implements FilterBuilder {
+
+ @Override
+ public Filter parse(final Element filterElement, final XmlConfiguration config) {
+ return new FilterWrapper(DenyAllFilter.newBuilder().build());
+ }
+
+ @Override
+ public Filter parse(final PropertiesConfiguration config) {
+ return new FilterWrapper(DenyAllFilter.newBuilder().build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
new file mode 100644
index 00000000000..9db96a971da
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/FilterBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.log4j.builders.filter;
+
+import org.apache.log4j.builders.Parser;
+import org.apache.log4j.spi.Filter;
+
+/**
+ * Define a Filter Builder.
+ */
+public interface FilterBuilder extends Parser {
+ // empty
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
new file mode 100644
index 00000000000..1ef21f4d726
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelMatchFilterBuilder.java
@@ -0,0 +1,91 @@
+/*
+ * 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.log4j.builders.filter;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.LevelMatchFilter;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Level match filter.
+ */
+@Plugin(name = "org.apache.log4j.varia.LevelMatchFilter", category = CATEGORY)
+public class LevelMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
+
+ private static final String LEVEL = "LevelToMatch";
+ private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
+
+ public LevelMatchFilterBuilder() {}
+
+ public LevelMatchFilterBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Filter parse(final Element filterElement, final XmlConfiguration config) {
+ final AtomicReference level = new AtomicReference<>();
+ final AtomicBoolean acceptOnMatch = new AtomicBoolean();
+ forEachElement(filterElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals("param")) {
+ switch (getNameAttributeKey(currentElement)) {
+ case LEVEL:
+ level.set(getValueAttribute(currentElement));
+ break;
+ case ACCEPT_ON_MATCH:
+ acceptOnMatch.set(getBooleanValueAttribute(currentElement));
+ break;
+ }
+ }
+ });
+ return createFilter(level.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parse(final PropertiesConfiguration config) {
+ final String level = getProperty(LEVEL);
+ final boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+ return createFilter(level, acceptOnMatch);
+ }
+
+ private Filter createFilter(final String level, final boolean acceptOnMatch) {
+ Level lvl = Level.ERROR;
+ if (level != null) {
+ lvl = OptionConverter.toLevel(level, org.apache.log4j.Level.ERROR).getVersion2Level();
+ }
+ final org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
+ ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+ : org.apache.logging.log4j.core.Filter.Result.DENY;
+ return FilterWrapper.adapt(LevelMatchFilter.newBuilder()
+ .setLevel(lvl)
+ .setOnMatch(onMatch)
+ .setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
new file mode 100644
index 00000000000..e5bf23213ae
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilder.java
@@ -0,0 +1,106 @@
+/*
+ * 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.log4j.builders.filter;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.LevelRangeFilter;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Level range filter.
+ * In this class, order of {@link Level} is log4j1 way, i.e.,
+ * {@link Level#ALL} and {@link Level#OFF} have minimum and maximum order, respectively.
+ * (see: LOG4J2-2315)
+ */
+@Plugin(name = "org.apache.log4j.varia.LevelRangeFilter", category = CATEGORY)
+public class LevelRangeFilterBuilder extends AbstractBuilder implements FilterBuilder {
+
+ private static final String LEVEL_MAX = "LevelMax";
+ private static final String LEVEL_MIN = "LevelMin";
+ private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
+
+ public LevelRangeFilterBuilder() {}
+
+ public LevelRangeFilterBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Filter parse(final Element filterElement, final XmlConfiguration config) {
+ final AtomicReference levelMax = new AtomicReference<>();
+ final AtomicReference levelMin = new AtomicReference<>();
+ final AtomicBoolean acceptOnMatch = new AtomicBoolean();
+ forEachElement(filterElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals("param")) {
+ switch (getNameAttributeKey(currentElement)) {
+ case LEVEL_MAX:
+ levelMax.set(getValueAttribute(currentElement));
+ break;
+ case LEVEL_MIN:
+ levelMin.set(getValueAttribute(currentElement));
+ break;
+ case ACCEPT_ON_MATCH:
+ acceptOnMatch.set(getBooleanValueAttribute(currentElement));
+ break;
+ }
+ }
+ });
+ return createFilter(levelMax.get(), levelMin.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parse(final PropertiesConfiguration config) {
+ final String levelMax = getProperty(LEVEL_MAX);
+ final String levelMin = getProperty(LEVEL_MIN);
+ final boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+ return createFilter(levelMax, levelMin, acceptOnMatch);
+ }
+
+ private Filter createFilter(final String levelMax, final String levelMin, final boolean acceptOnMatch) {
+ Level max = Level.OFF;
+ Level min = Level.ALL;
+ if (levelMax != null) {
+ max = OptionConverter.toLevel(levelMax, org.apache.log4j.Level.OFF).getVersion2Level();
+ }
+ if (levelMin != null) {
+ min = OptionConverter.toLevel(levelMin, org.apache.log4j.Level.ALL).getVersion2Level();
+ }
+ final org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
+ ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+ : org.apache.logging.log4j.core.Filter.Result.NEUTRAL;
+
+ // XXX: LOG4J2-2315
+ // log4j1 order: ALL < TRACE < DEBUG < ... < FATAL < OFF
+ // log4j2 order: ALL > TRACE > DEBUG > ... > FATAL > OFF
+ // So we create as LevelRangeFilter.createFilter(minLevel=max, maxLevel=min, ...)
+ return FilterWrapper.adapt(
+ LevelRangeFilter.createFilter(max, min, onMatch, org.apache.logging.log4j.core.Filter.Result.DENY));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
new file mode 100644
index 00000000000..c1adeed2e35
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/StringMatchFilterBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * 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.log4j.builders.filter;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.filter.StringMatchFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+/**
+ * Build a String match filter.
+ */
+@Plugin(name = "org.apache.log4j.varia.StringMatchFilter", category = CATEGORY)
+public class StringMatchFilterBuilder extends AbstractBuilder implements FilterBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String STRING_TO_MATCH = "StringToMatch";
+ private static final String ACCEPT_ON_MATCH = "AcceptOnMatch";
+
+ public StringMatchFilterBuilder() {
+ super();
+ }
+
+ public StringMatchFilterBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Filter parse(final Element filterElement, final XmlConfiguration config) {
+ final AtomicBoolean acceptOnMatch = new AtomicBoolean();
+ final AtomicReference text = new AtomicReference<>();
+ forEachElement(filterElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals("param")) {
+ switch (getNameAttributeKey(currentElement)) {
+ case STRING_TO_MATCH:
+ text.set(getValueAttribute(currentElement));
+ break;
+ case ACCEPT_ON_MATCH:
+ acceptOnMatch.set(getBooleanValueAttribute(currentElement));
+ break;
+ }
+ }
+ });
+ return createFilter(text.get(), acceptOnMatch.get());
+ }
+
+ @Override
+ public Filter parse(final PropertiesConfiguration config) {
+ final String text = getProperty(STRING_TO_MATCH);
+ final boolean acceptOnMatch = getBooleanProperty(ACCEPT_ON_MATCH);
+ return createFilter(text, acceptOnMatch);
+ }
+
+ private Filter createFilter(final String text, final boolean acceptOnMatch) {
+ if (text == null) {
+ LOGGER.error("No text provided for StringMatchFilter");
+ return null;
+ }
+ final org.apache.logging.log4j.core.Filter.Result onMatch = acceptOnMatch
+ ? org.apache.logging.log4j.core.Filter.Result.ACCEPT
+ : org.apache.logging.log4j.core.Filter.Result.DENY;
+ return FilterWrapper.adapt(StringMatchFilter.newBuilder()
+ .setText(text)
+ .setOnMatch(onMatch)
+ .setOnMismatch(org.apache.logging.log4j.core.Filter.Result.NEUTRAL)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/package-info.java
new file mode 100644
index 00000000000..1051cdee9e8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/filter/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@Open("org.apache.logging.log4j.core")
+package org.apache.log4j.builders.filter;
+
+import aQute.bnd.annotation.jpms.Open;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
new file mode 100644
index 00000000000..5ece07d6300
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/HtmlLayoutBuilder.java
@@ -0,0 +1,80 @@
+/*
+ * 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.log4j.builders.layout;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.HTMLLayout", category = CATEGORY)
+public class HtmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+ private static final String DEFAULT_TITLE = "Log4J Log Messages";
+ private static final String TITLE_PARAM = "Title";
+ private static final String LOCATION_INFO_PARAM = "LocationInfo";
+
+ public HtmlLayoutBuilder() {}
+
+ public HtmlLayoutBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Layout parse(final Element layoutElement, final XmlConfiguration config) {
+ final AtomicReference title = new AtomicReference<>("Log4J Log Messages");
+ final AtomicBoolean locationInfo = new AtomicBoolean();
+ forEachElement(layoutElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ if (TITLE_PARAM.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ title.set(currentElement.getAttribute("value"));
+ } else if (LOCATION_INFO_PARAM.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ locationInfo.set(getBooleanValueAttribute(currentElement));
+ }
+ }
+ });
+ return createLayout(title.get(), locationInfo.get());
+ }
+
+ @Override
+ public Layout parse(final PropertiesConfiguration config) {
+ final String title = getProperty(TITLE_PARAM, DEFAULT_TITLE);
+ final boolean locationInfo = getBooleanProperty(LOCATION_INFO_PARAM);
+ return createLayout(title, locationInfo);
+ }
+
+ private Layout createLayout(final String title, final boolean locationInfo) {
+ return LayoutWrapper.adapt(HtmlLayout.newBuilder()
+ .setTitle(title)
+ .setLocationInfo(locationInfo)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
new file mode 100644
index 00000000000..3eceb5d732f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/LayoutBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.log4j.builders.layout;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.builders.Parser;
+
+/**
+ * Define a Layout Builder.
+ */
+public interface LayoutBuilder extends Parser {
+ // empty
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
new file mode 100644
index 00000000000..9eaffb4bc0d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/PatternLayoutBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * 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.log4j.builders.layout;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+
+import java.util.Properties;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginAliases;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.PatternLayout", category = CATEGORY)
+@PluginAliases("org.apache.log4j.EnhancedPatternLayout")
+public class PatternLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String PATTERN = "ConversionPattern";
+
+ public PatternLayoutBuilder() {}
+
+ public PatternLayoutBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Layout parse(final Element layoutElement, final XmlConfiguration config) {
+ final NodeList params = layoutElement.getElementsByTagName("param");
+ final int length = params.getLength();
+ String pattern = null;
+ for (int index = 0; index < length; ++index) {
+ final Node currentNode = params.item(index);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ final Element currentElement = (Element) currentNode;
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ if (PATTERN.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ pattern = currentElement.getAttribute("value");
+ break;
+ }
+ }
+ }
+ }
+ return createLayout(pattern, config);
+ }
+
+ @Override
+ public Layout parse(final PropertiesConfiguration config) {
+ final String pattern = getProperty(PATTERN);
+ return createLayout(pattern, config);
+ }
+
+ Layout createLayout(String pattern, final Log4j1Configuration config) {
+ if (pattern == null) {
+ LOGGER.info("No pattern provided for pattern layout, using default pattern");
+ pattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
+ }
+ return LayoutWrapper.adapt(PatternLayout.newBuilder()
+ .setPattern(pattern
+ // Log4j 2 and Log4j 1 level names differ for custom levels
+ .replaceAll("%([-\\.\\d]*)p(?!\\w)", "%$1v1Level")
+ // Log4j 2's %x (NDC) is not compatible with Log4j 1's
+ // %x
+ // Log4j 1: "foo bar baz"
+ // Log4j 2: "[foo, bar, baz]"
+ // Use %ndc to get the Log4j 1 format
+ .replaceAll("%([-\\.\\d]*)x(?!\\w)", "%$1ndc")
+
+ // Log4j 2's %X (MDC) is not compatible with Log4j 1's
+ // %X
+ // Log4j 1: "{{foo,bar}{hoo,boo}}"
+ // Log4j 2: "{foo=bar,hoo=boo}"
+ // Use %properties to get the Log4j 1 format
+ .replaceAll("%([-\\.\\d]*)X(?!\\w)", "%$1properties"))
+ .setConfiguration(config)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
new file mode 100644
index 00000000000..7b16e54d871
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/SimpleLayoutBuilder.java
@@ -0,0 +1,50 @@
+/*
+ * 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.log4j.builders.layout;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.SimpleLayout", category = CATEGORY)
+public class SimpleLayoutBuilder implements LayoutBuilder {
+
+ @Override
+ public Layout parse(final Element layoutElement, final XmlConfiguration config) {
+ return new LayoutWrapper(PatternLayout.newBuilder()
+ .setPattern("%v1Level - %m%n")
+ .setConfiguration(config)
+ .build());
+ }
+
+ @Override
+ public Layout parse(final PropertiesConfiguration config) {
+ return new LayoutWrapper(PatternLayout.newBuilder()
+ .setPattern("%v1Level - %m%n")
+ .setConfiguration(config)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
new file mode 100644
index 00000000000..ae07ff39489
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/TTCCLayoutBuilder.java
@@ -0,0 +1,137 @@
+/*
+ * 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.log4j.builders.layout;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.w3c.dom.Element;
+
+/**
+ * Build a Pattern Layout
+ */
+@Plugin(name = "org.apache.log4j.TTCCLayout", category = CATEGORY)
+public class TTCCLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+ private static final String THREAD_PRINTING_PARAM = "ThreadPrinting";
+ private static final String CATEGORY_PREFIXING_PARAM = "CategoryPrefixing";
+ private static final String CONTEXT_PRINTING_PARAM = "ContextPrinting";
+ private static final String DATE_FORMAT_PARAM = "DateFormat";
+ private static final String TIMEZONE_FORMAT = "TimeZone";
+
+ public TTCCLayoutBuilder() {}
+
+ public TTCCLayoutBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Layout parse(final Element layoutElement, final XmlConfiguration config) {
+ final AtomicBoolean threadPrinting = new AtomicBoolean(Boolean.TRUE);
+ final AtomicBoolean categoryPrefixing = new AtomicBoolean(Boolean.TRUE);
+ final AtomicBoolean contextPrinting = new AtomicBoolean(Boolean.TRUE);
+ final AtomicReference dateFormat = new AtomicReference<>(RELATIVE);
+ final AtomicReference timezone = new AtomicReference<>();
+ forEachElement(layoutElement.getElementsByTagName("param"), currentElement -> {
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ switch (getNameAttributeKey(currentElement)) {
+ case THREAD_PRINTING_PARAM:
+ threadPrinting.set(getBooleanValueAttribute(currentElement));
+ break;
+ case CATEGORY_PREFIXING_PARAM:
+ categoryPrefixing.set(getBooleanValueAttribute(currentElement));
+ break;
+ case CONTEXT_PRINTING_PARAM:
+ contextPrinting.set(getBooleanValueAttribute(currentElement));
+ break;
+ case DATE_FORMAT_PARAM:
+ dateFormat.set(getValueAttribute(currentElement));
+ break;
+ case TIMEZONE_FORMAT:
+ timezone.set(getValueAttribute(currentElement));
+ break;
+ }
+ }
+ });
+ return createLayout(
+ threadPrinting.get(),
+ categoryPrefixing.get(),
+ contextPrinting.get(),
+ dateFormat.get(),
+ timezone.get(),
+ config);
+ }
+
+ @Override
+ public Layout parse(final PropertiesConfiguration config) {
+ final boolean threadPrinting = getBooleanProperty(THREAD_PRINTING_PARAM, true);
+ final boolean categoryPrefixing = getBooleanProperty(CATEGORY_PREFIXING_PARAM, true);
+ final boolean contextPrinting = getBooleanProperty(CONTEXT_PRINTING_PARAM, true);
+ final String dateFormat = getProperty(DATE_FORMAT_PARAM, RELATIVE);
+ final String timezone = getProperty(TIMEZONE_FORMAT);
+
+ return createLayout(threadPrinting, categoryPrefixing, contextPrinting, dateFormat, timezone, config);
+ }
+
+ private Layout createLayout(
+ final boolean threadPrinting,
+ final boolean categoryPrefixing,
+ final boolean contextPrinting,
+ final String dateFormat,
+ final String timezone,
+ final Log4j1Configuration config) {
+ final StringBuilder sb = new StringBuilder();
+ if (dateFormat != null) {
+ if (RELATIVE.equalsIgnoreCase(dateFormat)) {
+ sb.append("%r ");
+ } else if (!NULL.equalsIgnoreCase(dateFormat)) {
+ sb.append("%d{").append(dateFormat).append("}");
+ if (timezone != null) {
+ sb.append("{").append(timezone).append("}");
+ }
+ sb.append(" ");
+ }
+ }
+ if (threadPrinting) {
+ sb.append("[%t] ");
+ }
+ sb.append("%p ");
+ if (categoryPrefixing) {
+ sb.append("%c ");
+ }
+ if (contextPrinting) {
+ sb.append("%notEmpty{%ndc }");
+ }
+ sb.append("- %m%n");
+ return LayoutWrapper.adapt(PatternLayout.newBuilder()
+ .setPattern(sb.toString())
+ .setConfiguration(config)
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
new file mode 100644
index 00000000000..358c2d59322
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/XmlLayoutBuilder.java
@@ -0,0 +1,73 @@
+/*
+ * 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.log4j.builders.layout;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.layout.Log4j1XmlLayout;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.w3c.dom.Element;
+
+/**
+ * Build an XML Layout
+ */
+@Plugin(name = "org.apache.log4j.xml.XMLLayout", category = CATEGORY)
+public class XmlLayoutBuilder extends AbstractBuilder implements LayoutBuilder {
+
+ private static final String LOCATION_INFO = "LocationInfo";
+ private static final String PROPERTIES = "Properties";
+
+ public XmlLayoutBuilder() {}
+
+ public XmlLayoutBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Layout parse(final Element layoutElement, final XmlConfiguration config) {
+ final AtomicBoolean properties = new AtomicBoolean();
+ final AtomicBoolean locationInfo = new AtomicBoolean();
+ forEachElement(layoutElement.getElementsByTagName(PARAM_TAG), currentElement -> {
+ if (PROPERTIES.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ properties.set(getBooleanValueAttribute(currentElement));
+ } else if (LOCATION_INFO.equalsIgnoreCase(currentElement.getAttribute("name"))) {
+ locationInfo.set(getBooleanValueAttribute(currentElement));
+ }
+ });
+ return createLayout(properties.get(), locationInfo.get());
+ }
+
+ @Override
+ public Layout parse(final PropertiesConfiguration config) {
+ final boolean properties = getBooleanProperty(PROPERTIES);
+ final boolean locationInfo = getBooleanProperty(LOCATION_INFO);
+ return createLayout(properties, locationInfo);
+ }
+
+ private Layout createLayout(final boolean properties, final boolean locationInfo) {
+ return LayoutWrapper.adapt(Log4j1XmlLayout.createLayout(locationInfo, properties));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/package-info.java
new file mode 100644
index 00000000000..d87f91dc2e1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/layout/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@Open("org.apache.logging.log4j.core")
+package org.apache.log4j.builders.layout;
+
+import aQute.bnd.annotation.jpms.Open;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/package-info.java
new file mode 100644
index 00000000000..c20555c3b34
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@Export
+@Open("org.apache.logging.log4j.core")
+@Version("2.20.1")
+package org.apache.log4j.builders;
+
+import aQute.bnd.annotation.jpms.Open;
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java
new file mode 100644
index 00000000000..b88581f2e26
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.log4j.builders.rewrite;
+
+import org.apache.log4j.builders.Parser;
+import org.apache.log4j.rewrite.RewritePolicy;
+
+/**
+ * Define a RewritePolicy Builder.
+ */
+public interface RewritePolicyBuilder extends Parser {
+ // empty
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/package-info.java
new file mode 100644
index 00000000000..f8eb75121fe
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@Open("org.apache.logging.log4j.core")
+package org.apache.log4j.builders.rewrite;
+
+import aQute.bnd.annotation.jpms.Open;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/CompositeTriggeringPolicyBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/CompositeTriggeringPolicyBuilder.java
new file mode 100644
index 00000000000..2b96ee319bd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/CompositeTriggeringPolicyBuilder.java
@@ -0,0 +1,73 @@
+/*
+ * 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.log4j.builders.rolling;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.w3c.dom.Element;
+
+@Plugin(name = "org.apache.log4j.rolling.CompositeTriggeringPolicy", category = CATEGORY)
+public class CompositeTriggeringPolicyBuilder extends AbstractBuilder
+ implements TriggeringPolicyBuilder {
+
+ private static final TriggeringPolicy[] EMPTY_TRIGGERING_POLICIES = new TriggeringPolicy[0];
+ private static final String POLICY_TAG = "triggeringPolicy";
+
+ public CompositeTriggeringPolicyBuilder() {
+ super();
+ }
+
+ public CompositeTriggeringPolicyBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public CompositeTriggeringPolicy parse(final Element element, final XmlConfiguration configuration) {
+ final List policies = new ArrayList<>();
+ forEachElement(element.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case POLICY_TAG:
+ final TriggeringPolicy policy = configuration.parseTriggeringPolicy(currentElement);
+ if (policy != null) {
+ policies.add(policy);
+ }
+ break;
+ }
+ });
+ return createTriggeringPolicy(policies);
+ }
+
+ @Override
+ public CompositeTriggeringPolicy parse(final PropertiesConfiguration configuration) {
+ return createTriggeringPolicy(Collections.emptyList());
+ }
+
+ private CompositeTriggeringPolicy createTriggeringPolicy(final List policies) {
+ return CompositeTriggeringPolicy.createPolicy(policies.toArray(EMPTY_TRIGGERING_POLICIES));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/SizeBasedTriggeringPolicyBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/SizeBasedTriggeringPolicyBuilder.java
new file mode 100644
index 00000000000..0d82ff23112
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/SizeBasedTriggeringPolicyBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j.builders.rolling;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.w3c.dom.Element;
+
+@Plugin(name = "org.apache.log4j.rolling.SizeBasedTriggeringPolicy", category = CATEGORY)
+public class SizeBasedTriggeringPolicyBuilder extends AbstractBuilder
+ implements TriggeringPolicyBuilder {
+
+ private static final String MAX_SIZE_PARAM = "MaxFileSize";
+ private static final long DEFAULT_MAX_SIZE = 10 * 1024 * 1024;
+
+ public SizeBasedTriggeringPolicyBuilder() {
+ super();
+ }
+
+ public SizeBasedTriggeringPolicyBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public SizeBasedTriggeringPolicy parse(final Element element, final XmlConfiguration configuration) {
+ final AtomicLong maxSize = new AtomicLong(DEFAULT_MAX_SIZE);
+ forEachElement(element.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case PARAM_TAG:
+ switch (getNameAttributeKey(currentElement)) {
+ case MAX_SIZE_PARAM:
+ set(MAX_SIZE_PARAM, currentElement, maxSize);
+ break;
+ }
+ break;
+ }
+ });
+ return createTriggeringPolicy(maxSize.get());
+ }
+
+ @Override
+ public SizeBasedTriggeringPolicy parse(final PropertiesConfiguration configuration) {
+ final long maxSize = getLongProperty(MAX_SIZE_PARAM, DEFAULT_MAX_SIZE);
+ return createTriggeringPolicy(maxSize);
+ }
+
+ private SizeBasedTriggeringPolicy createTriggeringPolicy(final long maxSize) {
+ return SizeBasedTriggeringPolicy.createPolicy(Long.toString(maxSize));
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/TimeBasedRollingPolicyBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/TimeBasedRollingPolicyBuilder.java
new file mode 100644
index 00000000000..bebfd0260e3
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/TimeBasedRollingPolicyBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * 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.log4j.builders.rolling;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+
+import java.util.Properties;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.w3c.dom.Element;
+
+@Plugin(name = "org.apache.log4j.rolling.TimeBasedRollingPolicy", category = CATEGORY)
+public class TimeBasedRollingPolicyBuilder extends AbstractBuilder
+ implements TriggeringPolicyBuilder {
+
+ public TimeBasedRollingPolicyBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ public TimeBasedRollingPolicyBuilder() {
+ super();
+ }
+
+ @Override
+ public TimeBasedTriggeringPolicy parse(final Element element, final XmlConfiguration configuration) {
+ return createTriggeringPolicy();
+ }
+
+ @Override
+ public TimeBasedTriggeringPolicy parse(final PropertiesConfiguration configuration) {
+ return createTriggeringPolicy();
+ }
+
+ private TimeBasedTriggeringPolicy createTriggeringPolicy() {
+ return TimeBasedTriggeringPolicy.newBuilder().build();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/TriggeringPolicyBuilder.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/TriggeringPolicyBuilder.java
new file mode 100644
index 00000000000..931ad08b405
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/TriggeringPolicyBuilder.java
@@ -0,0 +1,24 @@
+/*
+ * 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.log4j.builders.rolling;
+
+import org.apache.log4j.builders.Parser;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+
+public interface TriggeringPolicyBuilder extends Parser {
+ // NOP
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/package-info.java
new file mode 100644
index 00000000000..d1ada5ae8c5
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rolling/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@Open("org.apache.logging.log4j.core")
+package org.apache.log4j.builders.rolling;
+
+import aQute.bnd.annotation.jpms.Open;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/component/helpers/Constants.java b/log4j-1.2-api/src/main/java/org/apache/log4j/component/helpers/Constants.java
new file mode 100644
index 00000000000..8e0f0a8934a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/component/helpers/Constants.java
@@ -0,0 +1,122 @@
+/*
+ * 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.log4j.component.helpers;
+
+/**
+ * Constants used internally throughout log4j.
+ *
+ */
+public interface Constants {
+
+ /**
+ * log4j package name string literal.
+ */
+ String LOG4J_PACKAGE_NAME = "org.apache.log4j";
+
+ /**
+ * The name of the default repository is "default" (without the quotes).
+ */
+ String DEFAULT_REPOSITORY_NAME = "default";
+
+ /**
+ * application string literal.
+ */
+ String APPLICATION_KEY = "application";
+ /**
+ * hostname string literal.
+ */
+ String HOSTNAME_KEY = "hostname";
+ /**
+ * receiver string literal.
+ */
+ String RECEIVER_NAME_KEY = "receiver";
+ /**
+ * log4jid string literal.
+ */
+ String LOG4J_ID_KEY = "log4jid";
+ /**
+ * time stamp pattern string literal.
+ */
+ String TIMESTAMP_RULE_FORMAT = "yyyy/MM/dd HH:mm:ss";
+
+ /**
+ * The default property file name for automatic configuration.
+ */
+ String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
+ /**
+ * The default XML configuration file name for automatic configuration.
+ */
+ String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
+ /**
+ * log4j.configuration string literal.
+ */
+ String DEFAULT_CONFIGURATION_KEY = "log4j.configuration";
+ /**
+ * log4j.configuratorClass string literal.
+ */
+ String CONFIGURATOR_CLASS_KEY = "log4j.configuratorClass";
+
+ /**
+ * JNDI context name string literal.
+ */
+ String JNDI_CONTEXT_NAME = "java:comp/env/log4j/context-name";
+
+ /**
+ * TEMP_LIST_APPENDER string literal.
+ */
+ String TEMP_LIST_APPENDER_NAME = "TEMP_LIST_APPENDER";
+ /**
+ * TEMP_CONSOLE_APPENDER string literal.
+ */
+ String TEMP_CONSOLE_APPENDER_NAME = "TEMP_CONSOLE_APPENDER";
+ /**
+ * Codes URL string literal.
+ */
+ String CODES_HREF = "http://logging.apache.org/log4j/docs/codes.html";
+
+ /**
+ * ABSOLUTE string literal.
+ */
+ String ABSOLUTE_FORMAT = "ABSOLUTE";
+ /**
+ * SimpleTimePattern for ABSOLUTE.
+ */
+ String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
+
+ /**
+ * SimpleTimePattern for ABSOLUTE.
+ */
+ String SIMPLE_TIME_PATTERN = "HH:mm:ss";
+
+ /**
+ * DATE string literal.
+ */
+ String DATE_AND_TIME_FORMAT = "DATE";
+ /**
+ * SimpleTimePattern for DATE.
+ */
+ String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS";
+
+ /**
+ * ISO8601 string literal.
+ */
+ String ISO8601_FORMAT = "ISO8601";
+ /**
+ * SimpleTimePattern for ISO8601.
+ */
+ String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/InputStreamWrapper.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/InputStreamWrapper.java
index 19bf9a9a319..49eaea9187c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/InputStreamWrapper.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/InputStreamWrapper.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.config;
@@ -88,5 +88,4 @@ public long skip(final long n) throws IOException {
public String toString() {
return getClass().getSimpleName() + " [description=" + description + ", input=" + input + "]";
}
-
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
new file mode 100644
index 00000000000..de3a3a53487
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1Configuration.java
@@ -0,0 +1,77 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.AbstractConfiguration;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+
+/**
+ * Base Configuration for Log4j 1.
+ */
+public class Log4j1Configuration extends AbstractConfiguration implements Reconfigurable {
+
+ public static final String MONITOR_INTERVAL = "log4j1.monitorInterval";
+ public static final String APPENDER_REF_TAG = "appender-ref";
+ public static final String THRESHOLD_PARAM = "Threshold";
+
+ public static final String INHERITED = "inherited";
+
+ public static final String NULL = "null";
+
+ /**
+ * The effective level used, when the configuration uses a non-existent custom
+ * level.
+ */
+ public static final Level DEFAULT_LEVEL = Level.DEBUG;
+
+ protected final BuilderManager manager = new BuilderManager();
+
+ public Log4j1Configuration(
+ final LoggerContext loggerContext,
+ final ConfigurationSource configurationSource,
+ final int monitorIntervalSeconds) {
+ super(loggerContext, configurationSource);
+ initializeWatchers(this, configurationSource, monitorIntervalSeconds);
+ }
+
+ public BuilderManager getBuilderManager() {
+ return manager;
+ }
+
+ /**
+ * Initialize the configuration.
+ */
+ @Override
+ public void initialize() {
+ getStrSubstitutor().setConfiguration(this);
+ getConfigurationStrSubstitutor().setConfiguration(this);
+ super.getScheduler().start();
+ doConfigure();
+ setState(State.INITIALIZED);
+ LOGGER.debug("Configuration {} initialized", this);
+ }
+
+ @Override
+ public Configuration reconfigure() {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
index 4475f6aba7d..c0f8e73fcb7 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java
@@ -1,21 +1,23 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.config;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -25,10 +27,13 @@
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.atomic.AtomicInteger;
-
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
import org.apache.logging.log4j.core.config.ConfigurationException;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
+import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder;
import org.apache.logging.log4j.core.tools.BasicCommandLineArguments;
import org.apache.logging.log4j.core.tools.picocli.CommandLine;
import org.apache.logging.log4j.core.tools.picocli.CommandLine.Command;
@@ -55,19 +60,29 @@ public final class Log4j1ConfigurationConverter {
@Command(name = "Log4j1ConfigurationConverter")
public static class CommandLineArguments extends BasicCommandLineArguments implements Runnable {
- @Option(names = { "--failfast", "-f" }, description = "Fails on the first failure in recurse mode.")
+ @Option(
+ names = {"--failfast", "-f"},
+ description = "Fails on the first failure in recurse mode.")
private boolean failFast;
- @Option(names = { "--in", "-i" }, description = "Specifies the input file.")
+ @Option(
+ names = {"--in", "-i"},
+ description = "Specifies the input file.")
private Path pathIn;
- @Option(names = { "--out", "-o" }, description = "Specifies the output file.")
+ @Option(
+ names = {"--out", "-o"},
+ description = "Specifies the output file.")
private Path pathOut;
- @Option(names = { "--recurse", "-r" }, description = "Recurses into this folder looking for the input file")
+ @Option(
+ names = {"--recurse", "-r"},
+ description = "Recurses into this folder looking for the input file")
private Path recurseIntoPath;
- @Option(names = { "--verbose", "-v" }, description = "Be verbose.")
+ @Option(
+ names = {"--verbose", "-v"},
+ description = "Be verbose.")
private boolean verbose;
public Path getPathIn() {
@@ -145,8 +160,8 @@ private Log4j1ConfigurationConverter(final CommandLineArguments cla) {
}
protected void convert(final InputStream input, final OutputStream output) throws IOException {
- final ConfigurationBuilder builder = new Log4j1ConfigurationParser()
- .buildConfigurationBuilder(input);
+ final ConfigurationBuilder builder =
+ new Log4j1ConfigurationParser().buildConfigurationBuilder(input);
builder.writeXmlConfiguration(output);
}
@@ -173,13 +188,20 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
verbose("Reading %s", file);
String newFile = file.getFileName().toString();
final int lastIndex = newFile.lastIndexOf(".");
- newFile = lastIndex < 0 ? newFile + FILE_EXT_XML
+ newFile = lastIndex < 0
+ ? newFile + FILE_EXT_XML
: newFile.substring(0, lastIndex) + FILE_EXT_XML;
- final Path resolved = file.resolveSibling(newFile);
- try (final InputStream input = new InputStreamWrapper(Files.newInputStream(file), file.toString());
- final OutputStream output = Files.newOutputStream(resolved)) {
+ final Path resolvedPath = file.resolveSibling(newFile);
+ try (final InputStream input =
+ new InputStreamWrapper(Files.newInputStream(file), file.toString());
+ final OutputStream output = Files.newOutputStream(resolvedPath)) {
try {
- convert(input, output);
+ final ByteArrayOutputStream tmpOutput = new ByteArrayOutputStream();
+ convert(input, tmpOutput);
+ tmpOutput.close();
+ DefaultConfigurationBuilder.formatXml(
+ new StreamSource(new ByteArrayInputStream(tmpOutput.toByteArray())),
+ new StreamResult(output));
countOKs.incrementAndGet();
} catch (ConfigurationException | IOException e) {
countFails.incrementAndGet();
@@ -187,8 +209,14 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
throw e;
}
e.printStackTrace();
+ } catch (TransformerException e) {
+ countFails.incrementAndGet();
+ if (cla.isFailFast()) {
+ throw new IOException(e);
+ }
+ e.printStackTrace();
}
- verbose("Wrote %s", resolved);
+ verbose("Wrote %s", resolvedPath);
}
}
return FileVisitResult.CONTINUE;
@@ -197,12 +225,14 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
} catch (final IOException e) {
throw new ConfigurationException(e);
} finally {
- verbose("OK = %,d, Failures = %,d, Total = %,d", countOKs.get(), countFails.get(),
- countOKs.get() + countFails.get());
+ verbose(
+ "OK = %,d, Failures = %,d, Total = %,d",
+ countOKs.get(), countFails.get(), countOKs.get() + countFails.get());
}
} else {
verbose("Reading %s", cla.getPathIn());
- try (final InputStream input = getInputStream(); final OutputStream output = getOutputStream()) {
+ try (final InputStream input = getInputStream();
+ final OutputStream output = getOutputStream()) {
convert(input, output);
} catch (final IOException e) {
throw new ConfigurationException(e);
@@ -216,5 +246,4 @@ private void verbose(final String template, final Object... args) {
System.err.println(String.format(template, args));
}
}
-
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java
index 83f66753d93..c75524811c6 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java
@@ -1,24 +1,23 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.config;
import java.io.IOException;
import java.io.InputStream;
-
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationException;
@@ -54,5 +53,4 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C
protected String[] getSupportedTypes() {
return SUFFIXES;
}
-
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
index 112ab4247fb..4e8ea68650c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.config;
@@ -24,8 +24,9 @@
import java.util.Objects;
import java.util.Properties;
import java.util.TreeMap;
-
+import org.apache.log4j.helpers.OptionConverter;
import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter.Result;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.appender.NullAppender;
@@ -39,7 +40,6 @@
import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
-import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;
@@ -47,7 +47,7 @@
* Experimental parser for Log4j 1.2 properties configuration files.
*
* This class is not thread-safe.
- *
+ *
*
* From the Log4j 1.2 Javadocs:
*
@@ -66,20 +66,20 @@ public class Log4j1ConfigurationParser {
private static final String ROOTCATEGORY = "rootCategory";
private static final String TRUE = "true";
private static final String FALSE = "false";
+ private static final String RELATIVE = "RELATIVE";
+ private static final String NULL = "NULL";
private final Properties properties = new Properties();
- private StrSubstitutor strSubstitutorProperties;
- private StrSubstitutor strSubstitutorSystem;
- private final ConfigurationBuilder builder = ConfigurationBuilderFactory
- .newConfigurationBuilder();
+ private final ConfigurationBuilder builder =
+ ConfigurationBuilderFactory.newConfigurationBuilder();
/**
* Parses a Log4j 1.2 properties configuration file in ISO 8859-1 encoding into a ConfigurationBuilder.
*
* @param input
* InputStream to read from is assumed to be ISO 8859-1, and will not be closed.
- * @return the populated ConfigurationBuilder, never {@literal null}
+ * @return the populated ConfigurationBuilder, never {@code null}
* @throws IOException
* if unable to read the input
* @throws ConfigurationException
@@ -89,8 +89,6 @@ public ConfigurationBuilder buildConfigurationBuilder(final
throws IOException {
try {
properties.load(input);
- strSubstitutorProperties = new StrSubstitutor(properties);
- strSubstitutorSystem = new StrSubstitutor(System.getProperties());
final String rootCategoryValue = getLog4jValue(ROOTCATEGORY);
final String rootLoggerValue = getLog4jValue(ROOTLOGGER);
if (rootCategoryValue == null && rootLoggerValue == null) {
@@ -102,9 +100,16 @@ public ConfigurationBuilder buildConfigurationBuilder(final
builder.setConfigurationName("Log4j1");
// DEBUG
final String debugValue = getLog4jValue("debug");
- if (Boolean.valueOf(debugValue)) {
+ if (Boolean.parseBoolean(debugValue)) {
builder.setStatusLevel(Level.DEBUG);
}
+ // global threshold
+ final String threshold = OptionConverter.findAndSubst(PropertiesConfiguration.THRESHOLD_KEY, properties);
+ if (threshold != null) {
+ final Level level = OptionConverter.convertLevel(threshold.trim(), Level.ALL);
+ builder.add(builder.newFilter("ThresholdFilter", Result.NEUTRAL, Result.DENY)
+ .addAttribute("level", level));
+ }
// Root
buildRootLogger(getLog4jValue(ROOTCATEGORY));
buildRootLogger(getLog4jValue(ROOTLOGGER));
@@ -145,13 +150,13 @@ private Map buildClassToPropertyPrefixMap() {
for (final Map.Entry entry : properties.entrySet()) {
final Object keyObj = entry.getKey();
if (keyObj != null) {
- final String key = keyObj.toString();
+ final String key = keyObj.toString().trim();
if (key.startsWith(prefix)) {
if (key.indexOf('.', preLength) < 0) {
final String name = key.substring(preLength);
final Object value = entry.getValue();
if (value != null) {
- map.put(name, value.toString());
+ map.put(name, value.toString().trim());
}
}
}
@@ -162,23 +167,23 @@ private Map buildClassToPropertyPrefixMap() {
private void buildAppender(final String appenderName, final String appenderClass) {
switch (appenderClass) {
- case "org.apache.log4j.ConsoleAppender":
- buildConsoleAppender(appenderName);
- break;
- case "org.apache.log4j.FileAppender":
- buildFileAppender(appenderName);
- break;
- case "org.apache.log4j.DailyRollingFileAppender":
- buildDailyRollingFileAppender(appenderName);
- break;
- case "org.apache.log4j.RollingFileAppender":
- buildRollingFileAppender(appenderName);
- break;
- case "org.apache.log4j.varia.NullAppender":
- buildNullAppender(appenderName);
- break;
- default:
- reportWarning("Unknown appender class: " + appenderClass + "; ignoring appender: " + appenderName);
+ case "org.apache.log4j.ConsoleAppender":
+ buildConsoleAppender(appenderName);
+ break;
+ case "org.apache.log4j.FileAppender":
+ buildFileAppender(appenderName);
+ break;
+ case "org.apache.log4j.DailyRollingFileAppender":
+ buildDailyRollingFileAppender(appenderName);
+ break;
+ case "org.apache.log4j.RollingFileAppender":
+ buildRollingFileAppender(appenderName);
+ break;
+ case "org.apache.log4j.varia.NullAppender":
+ buildNullAppender(appenderName);
+ break;
+ default:
+ reportWarning("Unknown appender class: " + appenderClass + "; ignoring appender: " + appenderName);
}
}
@@ -188,15 +193,15 @@ private void buildConsoleAppender(final String appenderName) {
if (targetValue != null) {
final ConsoleAppender.Target target;
switch (targetValue) {
- case "System.out":
- target = ConsoleAppender.Target.SYSTEM_OUT;
- break;
- case "System.err":
- target = ConsoleAppender.Target.SYSTEM_ERR;
- break;
- default:
- reportWarning("Unknown value for console Target: " + targetValue);
- target = null;
+ case "System.out":
+ target = ConsoleAppender.Target.SYSTEM_OUT;
+ break;
+ case "System.err":
+ target = ConsoleAppender.Target.SYSTEM_ERR;
+ break;
+ default:
+ reportWarning("Unknown value for console Target: " + targetValue);
+ target = null;
}
if (target != null) {
appenderBuilder.addAttribute("target", target);
@@ -226,52 +231,55 @@ private void buildFileAppender(final String appenderName, final AppenderComponen
}
private void buildDailyRollingFileAppender(final String appenderName) {
- final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName,
- RollingFileAppender.PLUGIN_NAME);
+ final AppenderComponentBuilder appenderBuilder =
+ builder.newAppender(appenderName, RollingFileAppender.PLUGIN_NAME);
buildFileAppender(appenderName, appenderBuilder);
final String fileName = getLog4jAppenderValue(appenderName, "File");
- final String datePattern = getLog4jAppenderValue(appenderName, "DatePattern", fileName + "'.'yyyy-MM-dd");
+ final String datePattern = getLog4jAppenderValue(appenderName, "DatePattern", ".yyyy-MM-dd");
appenderBuilder.addAttribute("filePattern", fileName + "%d{" + datePattern + "}");
final ComponentBuilder> triggeringPolicy = builder.newComponent("Policies")
.addComponent(builder.newComponent("TimeBasedTriggeringPolicy").addAttribute("modulate", true));
appenderBuilder.addComponent(triggeringPolicy);
- appenderBuilder
- .addComponent(builder.newComponent("DefaultRolloverStrategy").addAttribute("max", Integer.MAX_VALUE));
+ appenderBuilder.addComponent(builder.newComponent("DefaultRolloverStrategy")
+ .addAttribute("max", Integer.MAX_VALUE)
+ .addAttribute("fileIndex", "min"));
builder.add(appenderBuilder);
}
private void buildRollingFileAppender(final String appenderName) {
- final AppenderComponentBuilder appenderBuilder = builder.newAppender(appenderName,
- RollingFileAppender.PLUGIN_NAME);
+ final AppenderComponentBuilder appenderBuilder =
+ builder.newAppender(appenderName, RollingFileAppender.PLUGIN_NAME);
buildFileAppender(appenderName, appenderBuilder);
final String fileName = getLog4jAppenderValue(appenderName, "File");
appenderBuilder.addAttribute("filePattern", fileName + ".%i");
final String maxFileSizeString = getLog4jAppenderValue(appenderName, "MaxFileSize", "10485760");
final String maxBackupIndexString = getLog4jAppenderValue(appenderName, "MaxBackupIndex", "1");
- final ComponentBuilder> triggeringPolicy = builder.newComponent("Policies").addComponent(
- builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", maxFileSizeString));
+ final ComponentBuilder> triggeringPolicy = builder.newComponent("Policies")
+ .addComponent(
+ builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", maxFileSizeString));
appenderBuilder.addComponent(triggeringPolicy);
- appenderBuilder.addComponent(
- builder.newComponent("DefaultRolloverStrategy").addAttribute("max", maxBackupIndexString));
+ appenderBuilder.addComponent(builder.newComponent("DefaultRolloverStrategy")
+ .addAttribute("max", maxBackupIndexString)
+ .addAttribute("fileIndex", "min"));
builder.add(appenderBuilder);
}
- private void buildAttribute(final String componentName, final ComponentBuilder componentBuilder,
- final String sourceAttributeName, final String targetAttributeName) {
+ private void buildAttribute(
+ final String componentName,
+ final ComponentBuilder> componentBuilder,
+ final String sourceAttributeName,
+ final String targetAttributeName) {
final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName);
if (attributeValue != null) {
componentBuilder.addAttribute(targetAttributeName, attributeValue);
}
}
- private void buildAttributeWithDefault(final String componentName, final ComponentBuilder componentBuilder,
- final String sourceAttributeName, final String targetAttributeName, final String defaultValue) {
- final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName, defaultValue);
- componentBuilder.addAttribute(targetAttributeName, attributeValue);
- }
-
- private void buildMandatoryAttribute(final String componentName, final ComponentBuilder componentBuilder,
- final String sourceAttributeName, final String targetAttributeName) {
+ private void buildMandatoryAttribute(
+ final String componentName,
+ final ComponentBuilder> componentBuilder,
+ final String sourceAttributeName,
+ final String targetAttributeName) {
final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName);
if (attributeValue != null) {
componentBuilder.addAttribute(targetAttributeName, attributeValue);
@@ -289,66 +297,87 @@ private void buildAppenderLayout(final String name, final AppenderComponentBuild
final String layoutClass = getLog4jAppenderValue(name, "layout", null);
if (layoutClass != null) {
switch (layoutClass) {
- case "org.apache.log4j.PatternLayout":
- case "org.apache.log4j.EnhancedPatternLayout": {
- final String pattern = getLog4jAppenderValue(name, "layout.ConversionPattern", null)
-
- // Log4j 2's %x (NDC) is not compatible with Log4j 1's
- // %x
- // Log4j 1: "foo bar baz"
- // Log4j 2: "[foo, bar, baz]"
- // Use %ndc to get the Log4j 1 format
- .replace("%x", "%ndc")
-
- // Log4j 2's %X (MDC) is not compatible with Log4j 1's
- // %X
- // Log4j 1: "{{foo,bar}{hoo,boo}}"
- // Log4j 2: "{foo=bar,hoo=boo}"
- // Use %properties to get the Log4j 1 format
- .replace("%X", "%properties");
-
- appenderBuilder.add(newPatternLayout(pattern));
- break;
- }
- case "org.apache.log4j.SimpleLayout": {
- appenderBuilder.add(newPatternLayout("%level - %m%n"));
- break;
- }
- case "org.apache.log4j.TTCCLayout": {
- String pattern = "%r ";
- if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ThreadPrinting", TRUE))) {
- pattern += "[%t] ";
+ case "org.apache.log4j.PatternLayout":
+ case "org.apache.log4j.EnhancedPatternLayout": {
+ String pattern = getLog4jAppenderValue(name, "layout.ConversionPattern", null);
+ if (pattern != null) {
+ pattern = pattern
+ // Log4j 2 and Log4j 1 level names differ for custom levels
+ .replaceAll("%([-\\.\\d]*)p(?!\\w)", "%$1v1Level")
+ // Log4j 2's %x (NDC) is not compatible with Log4j 1's
+ // %x
+ // Log4j 1: "foo bar baz"
+ // Log4j 2: "[foo, bar, baz]"
+ // Use %ndc to get the Log4j 1 format
+ .replaceAll("%([-\\.\\d]*)x(?!\\w)", "%$1ndc")
+
+ // Log4j 2's %X (MDC) is not compatible with Log4j 1's
+ // %X
+ // Log4j 1: "{{foo,bar}{hoo,boo}}"
+ // Log4j 2: "{foo=bar,hoo=boo}"
+ // Use %properties to get the Log4j 1 format
+ .replaceAll("%([-\\.\\d]*)X(?!\\w)", "%$1properties");
+ } else {
+ pattern = "%m%n";
+ }
+ appenderBuilder.add(newPatternLayout(pattern));
+ break;
}
- pattern += "%p ";
- if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.CategoryPrefixing", TRUE))) {
- pattern += "%c ";
+ case "org.apache.log4j.SimpleLayout": {
+ appenderBuilder.add(newPatternLayout("%v1Level - %m%n"));
+ break;
}
- if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ContextPrinting", TRUE))) {
- pattern += "%notEmpty{%ndc }";
+ case "org.apache.log4j.TTCCLayout": {
+ String pattern = "";
+ final String dateFormat = getLog4jAppenderValue(name, "layout.DateFormat", RELATIVE);
+ final String timezone = getLog4jAppenderValue(name, "layout.TimeZone", null);
+ if (dateFormat != null) {
+ if (RELATIVE.equalsIgnoreCase(dateFormat)) {
+ pattern += "%r ";
+ } else if (!NULL.equalsIgnoreCase(dateFormat)) {
+ pattern += "%d{" + dateFormat + "}";
+ if (timezone != null) {
+ pattern += "{" + timezone + "}";
+ }
+ pattern += " ";
+ }
+ }
+ if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ThreadPrinting", TRUE))) {
+ pattern += "[%t] ";
+ }
+ pattern += "%p ";
+ if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.CategoryPrefixing", TRUE))) {
+ pattern += "%c ";
+ }
+ if (Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.ContextPrinting", TRUE))) {
+ pattern += "%notEmpty{%ndc }";
+ }
+ pattern += "- %m%n";
+ appenderBuilder.add(newPatternLayout(pattern));
+ break;
}
- pattern += "- %m%n";
- appenderBuilder.add(newPatternLayout(pattern));
- break;
- }
- case "org.apache.log4j.HTMLLayout": {
- final LayoutComponentBuilder htmlLayout = builder.newLayout("HtmlLayout");
- htmlLayout.addAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages"));
- htmlLayout.addAttribute("locationInfo",
- Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
- appenderBuilder.add(htmlLayout);
- break;
- }
- case "org.apache.log4j.xml.XMLLayout": {
- final LayoutComponentBuilder xmlLayout = builder.newLayout("Log4j1XmlLayout");
- xmlLayout.addAttribute("locationInfo",
- Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
- xmlLayout.addAttribute("properties",
- Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.Properties", FALSE)));
- appenderBuilder.add(xmlLayout);
- break;
- }
- default:
- reportWarning("Unknown layout class: " + layoutClass);
+ case "org.apache.log4j.HTMLLayout": {
+ final LayoutComponentBuilder htmlLayout = builder.newLayout("HtmlLayout");
+ htmlLayout.addAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages"));
+ htmlLayout.addAttribute(
+ "locationInfo",
+ Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
+ appenderBuilder.add(htmlLayout);
+ break;
+ }
+ case "org.apache.log4j.xml.XMLLayout": {
+ final LayoutComponentBuilder xmlLayout = builder.newLayout("Log4j1XmlLayout");
+ xmlLayout.addAttribute(
+ "locationInfo",
+ Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE)));
+ xmlLayout.addAttribute(
+ "properties",
+ Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.Properties", FALSE)));
+ appenderBuilder.add(xmlLayout);
+ break;
+ }
+ default:
+ reportWarning("Unknown layout class: " + layoutClass);
}
}
}
@@ -386,13 +415,13 @@ private void buildLoggers(final String prefix) {
for (final Map.Entry entry : properties.entrySet()) {
final Object keyObj = entry.getKey();
if (keyObj != null) {
- final String key = keyObj.toString();
+ final String key = keyObj.toString().trim();
if (key.startsWith(prefix)) {
final String name = key.substring(preLength);
final Object value = entry.getValue();
if (value != null) {
// a Level may be followed by a list of Appender refs.
- final String valueStr = value.toString();
+ final String valueStr = value.toString().trim();
final String[] split = valueStr.split(COMMA_DELIMITED_RE);
final String level = getLevelString(split, null);
if (level == null) {
@@ -421,8 +450,8 @@ private String getLog4jAppenderValue(final String appenderName, final String att
private String getProperty(final String key) {
final String value = properties.getProperty(key);
- final String sysValue = strSubstitutorSystem.replace(value);
- return strSubstitutorProperties.replace(sysValue);
+ final String substVars = OptionConverter.substVars(value, properties);
+ return substVars == null ? null : substVars.trim();
}
private String getProperty(final String key, final String defaultValue) {
@@ -430,8 +459,8 @@ private String getProperty(final String key, final String defaultValue) {
return value == null ? defaultValue : value;
}
- private String getLog4jAppenderValue(final String appenderName, final String attributeName,
- final String defaultValue) {
+ private String getLog4jAppenderValue(
+ final String appenderName, final String attributeName, final String defaultValue) {
return getProperty("log4j.appender." + appenderName + "." + attributeName, defaultValue);
}
@@ -442,5 +471,4 @@ private String getLog4jValue(final String key) {
private void reportWarning(final String msg) {
StatusLogger.getLogger().warn("Log4j 1 configuration parser: " + msg);
}
-
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
new file mode 100644
index 00000000000..3fc3d88ffec
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfiguration.java
@@ -0,0 +1,652 @@
+/*
+ * 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.log4j.config;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.builders.BuilderManager;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter.Result;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+/**
+ * Constructs a configuration based on Log4j 1 properties.
+ */
+public class PropertiesConfiguration extends Log4j1Configuration {
+
+ private static final String CATEGORY_PREFIX = "log4j.category.";
+ private static final String LOGGER_PREFIX = "log4j.logger.";
+ private static final String ADDITIVITY_PREFIX = "log4j.additivity.";
+ private static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
+ private static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ private static final String APPENDER_PREFIX = "log4j.appender.";
+ private static final String LOGGER_REF = "logger-ref";
+ private static final String ROOT_REF = "root-ref";
+ private static final String APPENDER_REF_TAG = "appender-ref";
+
+ /**
+ * If property set to true, then hierarchy will be reset before configuration.
+ */
+ private static final String RESET_KEY = "log4j.reset";
+
+ public static final String THRESHOLD_KEY = "log4j.threshold";
+ public static final String DEBUG_KEY = "log4j.debug";
+
+ private static final String INTERNAL_ROOT_NAME = "root";
+
+ private final Map registry = new HashMap<>();
+ private Properties properties;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param loggerContext The LoggerContext.
+ * @param source The ConfigurationSource.
+ * @param monitorIntervalSeconds The monitoring interval in seconds.
+ */
+ public PropertiesConfiguration(
+ final LoggerContext loggerContext, final ConfigurationSource source, final int monitorIntervalSeconds) {
+ super(loggerContext, source, monitorIntervalSeconds);
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param loggerContext The LoggerContext.
+ * @param properties The ConfigurationSource, may be null.
+ */
+ public PropertiesConfiguration(final LoggerContext loggerContext, final Properties properties) {
+ super(loggerContext, ConfigurationSource.NULL_SOURCE, 0);
+ this.properties = properties;
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param loggerContext The LoggerContext.
+ * @param properties The ConfigurationSource.
+ */
+ public PropertiesConfiguration(org.apache.logging.log4j.spi.LoggerContext loggerContext, Properties properties) {
+ this((LoggerContext) loggerContext, properties);
+ }
+
+ @Override
+ public void doConfigure() {
+ if (properties == null) {
+ properties = new Properties();
+ final InputStream inputStream = getConfigurationSource().getInputStream();
+ if (inputStream != null) {
+ try {
+ properties.load(inputStream);
+ } catch (final Exception e) {
+ LOGGER.error(
+ "Could not read configuration file [{}].",
+ getConfigurationSource().toString(),
+ e);
+ return;
+ }
+ }
+ }
+ // If we reach here, then the config file is alright.
+ doConfigure(properties);
+ }
+
+ @Override
+ public Configuration reconfigure() {
+ try {
+ final ConfigurationSource source = getConfigurationSource().resetInputStream();
+ if (source == null) {
+ return null;
+ }
+ final Configuration config =
+ new PropertiesConfigurationFactory().getConfiguration(getLoggerContext(), source);
+ return config == null || config.getState() != State.INITIALIZING ? null : config;
+ } catch (final IOException ex) {
+ LOGGER.error("Cannot locate file {}: {}", getConfigurationSource(), ex);
+ }
+ return null;
+ }
+
+ /**
+ * Reads a configuration from a file. The existing configuration is not cleared nor reset. If you require a
+ * different behavior, then call {@link LogManager#resetConfiguration resetConfiguration} method before calling
+ * doConfigure
.
+ *
+ * The configuration file consists of statements in the format key=value
. The syntax of different
+ * configuration elements are discussed below.
+ *
+ *
+ * The level value can consist of the string values OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL or a custom level
+ * value. A custom level value can be specified in the form level#classname. By default the repository-wide threshold is
+ * set to the lowest possible value, namely the level ALL
.
+ *
+ *
+ * Appender configuration
+ *
+ * Appender configuration syntax is:
+ *
+ *
+ * # For appender named appenderName , set its class.
+ * # Note: The appender name can contain dots.
+ * log4j.appender.appenderName=fully.qualified.name.of.appender.class
+ *
+ * # Set appender specific options.
+ * log4j.appender.appenderName.option1=value1
+ * ...
+ * log4j.appender.appenderName.optionN=valueN
+ *
+ *
+ * For each named appender you can configure its {@link Layout}. The syntax for configuring an appender's layout is:
+ *
+ *
+ * log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
+ * log4j.appender.appenderName.layout.option1=value1
+ * ....
+ * log4j.appender.appenderName.layout.optionN=valueN
+ *
+ *
+ * The syntax for adding {@link Filter}s to an appender is:
+ *
+ *
+ * log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
+ * log4j.appender.appenderName.filter.ID.option1=value1
+ * ...
+ * log4j.appender.appenderName.filter.ID.optionN=valueN
+ *
+ *
+ * The first line defines the class name of the filter identified by ID; subsequent lines with the same ID specify
+ * filter option - value pairs. Multiple filters are added to the appender in the lexicographic order of IDs.
+ *
+ *
+ * The syntax for adding an {@link ErrorHandler} to an appender is:
+ *
+ *
+ * log4j.appender.appenderName.errorhandler=fully.qualified.name.of.errorhandler.class
+ * log4j.appender.appenderName.errorhandler.appender-ref=appenderName
+ * log4j.appender.appenderName.errorhandler.option1=value1
+ * ...
+ * log4j.appender.appenderName.errorhandler.optionN=valueN
+ *
+ *
+ * Configuring loggers
+ *
+ * The syntax for configuring the root logger is:
+ *
+ *
+ * log4j.rootLogger=[level], appenderName, appenderName, ...
+ *
+ *
+ * This syntax means that an optional level can be supplied followed by appender names separated by commas.
+ *
+ *
+ * The level value can consist of the string values OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL or a custom level
+ * value. A custom level value can be specified in the form level#classname
.
+ *
+ *
+ * If a level value is specified, then the root level is set to the corresponding level. If no level value is specified,
+ * then the root level remains untouched.
+ *
+ *
+ * The root logger can be assigned multiple appenders.
+ *
+ *
+ * Each appenderName (separated by commas) will be added to the root logger. The named appender is defined using
+ * the appender syntax defined above.
+ *
+ *
+ * For non-root categories the syntax is almost the same:
+ *
+ *
+ * log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
+ *
+ *
+ * The meaning of the optional level value is discussed above in relation to the root logger. In addition however, the
+ * value INHERITED can be specified meaning that the named logger should inherit its level from the logger hierarchy.
+ *
+ *
+ * If no level value is supplied, then the level of the named logger remains untouched.
+ *
+ *
+ * By default categories inherit their level from the hierarchy. However, if you set the level of a logger and later
+ * decide that that logger should inherit its level, then you should specify INHERITED as the value for the level value.
+ * NULL is a synonym for INHERITED.
+ *
+ *
+ * Similar to the root logger syntax, each appenderName (separated by commas) will be attached to the named
+ * logger.
+ *
+ *
+ * See the appender additivity rule in the user manual for the meaning
+ * of the additivity
flag.
+ *
+ *
+ * # Set options for appender named "A1". # Appender "A1" will be a SyslogAppender
+ * log4j.appender.A1=org.apache.log4j.net.SyslogAppender
+ *
+ * # The syslog daemon resides on www.abc.net log4j.appender.A1.SyslogHost=www.abc.net
+ *
+ * # A1's layout is a PatternLayout, using the conversion pattern # %r %-5p %c{2} %M.%L %x - %m\n . Thus, the log
+ * output will # include # the relative time since the start of the application in # milliseconds, followed by the level
+ * of the log request, # followed by the two rightmost components of the logger name, # followed by the callers method
+ * name, followed by the line number, # the nested diagnostic context and finally the message itself. # Refer to the
+ * documentation of {@link PatternLayout} for further information # on the syntax of the ConversionPattern key.
+ * log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2}
+ * %M.%L %x - %m\n
+ *
+ * # Set options for appender named "A2" # A2 should be a RollingFileAppender, with maximum file size of 10 MB # using
+ * at most one backup file. A2's layout is TTCC, using the # ISO8061 date format with context printing enabled.
+ * log4j.appender.A2=org.apache.log4j.RollingFileAppender log4j.appender.A2.MaxFileSize=10MB
+ * log4j.appender.A2.MaxBackupIndex=1 log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+ * log4j.appender.A2.layout.ContextPrinting=enabled log4j.appender.A2.layout.DateFormat=ISO8601
+ *
+ * # Root logger set to DEBUG using the A2 appender defined above. log4j.rootLogger=DEBUG, A2
+ *
+ * # Logger definitions: # The SECURITY logger inherits is level from root. However, it's output # will go to A1
+ * appender defined above. It's additivity is non-cumulative. log4j.logger.SECURITY=INHERIT, A1
+ * log4j.additivity.SECURITY=false
+ *
+ * # Only warnings or above will be logged for the logger "SECURITY.access". # Output will go to A1.
+ * log4j.logger.SECURITY.access=WARN
+ *
+ *
+ * # The logger "class.of.the.day" inherits its level from the # logger hierarchy. Output will go to the appender's of
+ * the root # logger, A2 in this case. log4j.logger.class.of.the.day=INHERIT
+ *
+ *
+ * Refer to the setOption method in each Appender and Layout for class specific options.
+ *
+ *
+ * Use the #
or !
characters at the beginning of a line for comments.
+ *
+ */
+ private void doConfigure(final Properties properties) {
+ String status = "error";
+ String value = properties.getProperty(DEBUG_KEY);
+ if (value == null) {
+ value = properties.getProperty("log4j.configDebug");
+ if (value != null) {
+ LOGGER.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
+ }
+ }
+
+ if (value != null) {
+ status = OptionConverter.toBoolean(value, false) ? "debug" : "error";
+ }
+
+ final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(status);
+ statusConfig.initialize();
+
+ // if log4j.reset=true then reset hierarchy
+ final String reset = properties.getProperty(RESET_KEY);
+ if (reset != null && OptionConverter.toBoolean(reset, false)) {
+ LogManager.resetConfiguration();
+ }
+
+ final String threshold = OptionConverter.findAndSubst(THRESHOLD_KEY, properties);
+ if (threshold != null) {
+ final Level level = OptionConverter.convertLevel(threshold.trim(), Level.ALL);
+ addFilter(ThresholdFilter.createFilter(level, Result.NEUTRAL, Result.DENY));
+ }
+
+ configureRoot(properties);
+ parseLoggers(properties);
+
+ LOGGER.debug("Finished configuring.");
+ }
+
+ // --------------------------------------------------------------------------
+ // Internal stuff
+ // --------------------------------------------------------------------------
+
+ private void configureRoot(final Properties props) {
+ String effectiveFrefix = ROOT_LOGGER_PREFIX;
+ String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
+
+ if (value == null) {
+ value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
+ effectiveFrefix = ROOT_CATEGORY_PREFIX;
+ }
+
+ if (value == null) {
+ LOGGER.debug("Could not find root logger information. Is this OK?");
+ } else {
+ final LoggerConfig root = getRootLogger();
+ parseLogger(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
+ }
+ }
+
+ /**
+ * Parses non-root elements, such non-root categories and renderers.
+ */
+ private void parseLoggers(final Properties props) {
+ final Enumeration> enumeration = props.propertyNames();
+ while (enumeration.hasMoreElements()) {
+ final String key = Objects.toString(enumeration.nextElement(), null);
+ if (key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
+ String loggerName = null;
+ if (key.startsWith(CATEGORY_PREFIX)) {
+ loggerName = key.substring(CATEGORY_PREFIX.length());
+ } else if (key.startsWith(LOGGER_PREFIX)) {
+ loggerName = key.substring(LOGGER_PREFIX.length());
+ }
+ final String value = OptionConverter.findAndSubst(key, props);
+ LoggerConfig loggerConfig = getLogger(loggerName);
+ if (loggerConfig == null) {
+ final boolean additivity = getAdditivityForLogger(props, loggerName);
+ loggerConfig = new LoggerConfig(loggerName, org.apache.logging.log4j.Level.ERROR, additivity);
+ addLogger(loggerName, loggerConfig);
+ }
+ parseLogger(props, loggerConfig, key, loggerName, value);
+ }
+ }
+ }
+
+ /**
+ * Parses the additivity option for a non-root category.
+ */
+ private boolean getAdditivityForLogger(final Properties props, final String loggerName) {
+ boolean additivity = true;
+ final String key = ADDITIVITY_PREFIX + loggerName;
+ final String value = OptionConverter.findAndSubst(key, props);
+ LOGGER.debug("Handling {}=[{}]", key, value);
+ // touch additivity only if necessary
+ if (value != null && !value.isEmpty()) {
+ additivity = OptionConverter.toBoolean(value, true);
+ }
+ return additivity;
+ }
+
+ /**
+ * This method must work for the root category as well.
+ */
+ private void parseLogger(
+ final Properties props,
+ final LoggerConfig loggerConfig,
+ final String optionKey,
+ final String loggerName,
+ final String value) {
+
+ LOGGER.debug("Parsing for [{}] with value=[{}].", loggerName, value);
+ // We must skip over ',' but not white space
+ final StringTokenizer st = new StringTokenizer(value, ",");
+
+ // If value is not in the form ", appender.." or "", then we should set the level of the logger.
+
+ if (!(value.startsWith(",") || value.isEmpty())) {
+
+ // just to be on the safe side...
+ if (!st.hasMoreTokens()) {
+ return;
+ }
+
+ final String levelStr = st.nextToken();
+ LOGGER.debug("Level token is [{}].", levelStr);
+
+ final org.apache.logging.log4j.Level level = levelStr == null
+ ? org.apache.logging.log4j.Level.ERROR
+ : OptionConverter.convertLevel(levelStr, org.apache.logging.log4j.Level.DEBUG);
+ loggerConfig.setLevel(level);
+ LOGGER.debug("Logger {} level set to {}", loggerName, level);
+ }
+
+ Appender appender;
+ String appenderName;
+ while (st.hasMoreTokens()) {
+ appenderName = st.nextToken().trim();
+ if (appenderName == null || appenderName.equals(",")) {
+ continue;
+ }
+ LOGGER.debug("Parsing appender named \"{}\".", appenderName);
+ appender = parseAppender(props, appenderName);
+ if (appender != null) {
+ LOGGER.debug("Adding appender named [{}] to loggerConfig [{}].", appenderName, loggerConfig.getName());
+ loggerConfig.addAppender(getAppender(appenderName), null, null);
+ } else {
+ LOGGER.debug("Appender named [{}] not found.", appenderName);
+ }
+ }
+ }
+
+ public Appender parseAppender(final Properties props, final String appenderName) {
+ Appender appender = registry.get(appenderName);
+ if ((appender != null)) {
+ LOGGER.debug("Appender \"" + appenderName + "\" was already parsed.");
+ return appender;
+ }
+ // Appender was not previously initialized.
+ final String prefix = APPENDER_PREFIX + appenderName;
+ final String layoutPrefix = prefix + ".layout";
+ final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
+ final String className = OptionConverter.findAndSubst(prefix, props);
+ if (className == null) {
+ LOGGER.debug("Appender \"" + appenderName + "\" does not exist.");
+ return null;
+ }
+ appender = manager.parseAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props, this);
+ if (appender == null) {
+ appender = buildAppender(appenderName, className, prefix, layoutPrefix, filterPrefix, props);
+ } else {
+ registry.put(appenderName, appender);
+ addAppender(AppenderAdapter.adapt(appender));
+ }
+ return appender;
+ }
+
+ private Appender buildAppender(
+ final String appenderName,
+ final String className,
+ final String prefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props) {
+ final Appender appender = newInstanceOf(className, "Appender");
+ if (appender == null) {
+ return null;
+ }
+ appender.setName(appenderName);
+ appender.setLayout(parseLayout(layoutPrefix, appenderName, props));
+ final String errorHandlerPrefix = prefix + ".errorhandler";
+ final String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
+ if (errorHandlerClass != null) {
+ final ErrorHandler eh = parseErrorHandler(props, errorHandlerPrefix, errorHandlerClass, appender);
+ if (eh != null) {
+ appender.setErrorHandler(eh);
+ }
+ }
+ appender.addFilter(parseAppenderFilters(props, filterPrefix, appenderName));
+ final String[] keys = new String[] {layoutPrefix};
+ addProperties(appender, keys, props, prefix);
+ addAppender(AppenderAdapter.adapt(appender));
+ registry.put(appenderName, appender);
+ return appender;
+ }
+
+ public Layout parseLayout(final String layoutPrefix, final String appenderName, final Properties props) {
+ final String layoutClass = OptionConverter.findAndSubst(layoutPrefix, props);
+ if (layoutClass == null) {
+ return null;
+ }
+ Layout layout = manager.parse(layoutClass, layoutPrefix, props, this, BuilderManager.INVALID_LAYOUT);
+ if (layout == null) {
+ layout = buildLayout(layoutPrefix, layoutClass, appenderName, props);
+ }
+ return layout;
+ }
+
+ private Layout buildLayout(
+ final String layoutPrefix, final String className, final String appenderName, final Properties props) {
+ final Layout layout = newInstanceOf(className, "Layout");
+ if (layout == null) {
+ return null;
+ }
+ LOGGER.debug("Parsing layout options for \"{}\".", appenderName);
+ PropertySetter.setProperties(layout, props, layoutPrefix + ".");
+ LOGGER.debug("End of parsing for \"{}\".", appenderName);
+ return layout;
+ }
+
+ public ErrorHandler parseErrorHandler(
+ final Properties props,
+ final String errorHandlerPrefix,
+ final String errorHandlerClass,
+ final Appender appender) {
+ final ErrorHandler eh = newInstanceOf(errorHandlerClass, "ErrorHandler");
+ final String[] keys = new String[] {
+ // @formatter:off
+ errorHandlerPrefix + "." + ROOT_REF,
+ errorHandlerPrefix + "." + LOGGER_REF,
+ errorHandlerPrefix + "." + APPENDER_REF_TAG
+ };
+ // @formatter:on
+ addProperties(eh, keys, props, errorHandlerPrefix);
+ return eh;
+ }
+
+ public void addProperties(final Object obj, final String[] keys, final Properties props, final String prefix) {
+ final Properties edited = new Properties();
+ props.stringPropertyNames().stream()
+ .filter(name -> {
+ if (name.startsWith(prefix)) {
+ for (final String key : keys) {
+ if (name.equals(key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ })
+ .forEach(name -> edited.put(name, props.getProperty(name)));
+ PropertySetter.setProperties(obj, edited, prefix + ".");
+ }
+
+ public Filter parseAppenderFilters(final Properties props, final String filterPrefix, final String appenderName) {
+ // extract filters and filter options from props into a hashtable mapping
+ // the property name defining the filter class to a list of pre-parsed
+ // name-value pairs associated to that filter
+ final int fIdx = filterPrefix.length();
+ final SortedMap> filters = new TreeMap<>();
+ final Enumeration> e = props.keys();
+ String name = "";
+ while (e.hasMoreElements()) {
+ final String key = (String) e.nextElement();
+ if (key.startsWith(filterPrefix)) {
+ final int dotIdx = key.indexOf('.', fIdx);
+ String filterKey = key;
+ if (dotIdx != -1) {
+ filterKey = key.substring(0, dotIdx);
+ name = key.substring(dotIdx + 1);
+ }
+ final List filterOpts = filters.computeIfAbsent(filterKey, k -> new ArrayList<>());
+ if (dotIdx != -1) {
+ final String value = OptionConverter.findAndSubst(key, props);
+ filterOpts.add(new NameValue(name, value));
+ }
+ }
+ }
+
+ Filter head = null;
+ for (final Map.Entry> entry : filters.entrySet()) {
+ final String clazz = props.getProperty(entry.getKey());
+ Filter filter = null;
+ if (clazz != null) {
+ filter = manager.parse(clazz, entry.getKey(), props, this, BuilderManager.INVALID_FILTER);
+ if (filter == null) {
+ LOGGER.debug("Filter key: [{}] class: [{}] props: {}", entry.getKey(), clazz, entry.getValue());
+ filter = buildFilter(clazz, appenderName, entry.getValue());
+ }
+ }
+ head = FilterAdapter.addFilter(head, filter);
+ }
+ return head;
+ }
+
+ private Filter buildFilter(final String className, final String appenderName, final List props) {
+ final Filter filter = newInstanceOf(className, "Filter");
+ if (filter != null) {
+ final PropertySetter propSetter = new PropertySetter(filter);
+ for (final NameValue property : props) {
+ propSetter.setProperty(property.key, property.value);
+ }
+ propSetter.activate();
+ }
+ return filter;
+ }
+
+ public TriggeringPolicy parseTriggeringPolicy(final Properties props, final String policyPrefix) {
+ final String policyClass = OptionConverter.findAndSubst(policyPrefix, props);
+ if (policyClass == null) {
+ return null;
+ }
+ return manager.parse(policyClass, policyPrefix, props, this, null);
+ }
+
+ private static T newInstanceOf(final String className, final String type) {
+ try {
+ return LoaderUtil.newInstanceOf(className);
+ } catch (ReflectiveOperationException ex) {
+ LOGGER.error(
+ "Unable to create {} {} due to {}:{}",
+ type,
+ className,
+ ex.getClass().getSimpleName(),
+ ex.getMessage(),
+ ex);
+ return null;
+ }
+ }
+
+ private static class NameValue {
+ String key, value;
+
+ NameValue(final String key, final String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return key + "=" + value;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java
new file mode 100644
index 00000000000..fb6f1383be5
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertiesConfigurationFactory.java
@@ -0,0 +1,75 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Order;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * Configures Log4j from a log4j 1 format properties file.
+ */
+@Plugin(name = "Log4j1PropertiesConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+@Order(2)
+public class PropertiesConfigurationFactory extends ConfigurationFactory {
+
+ static final String FILE_EXTENSION = ".properties";
+
+ /**
+ * File name prefix for test configurations.
+ */
+ protected static final String TEST_PREFIX = "log4j-test";
+
+ /**
+ * File name prefix for standard configurations.
+ */
+ protected static final String DEFAULT_PREFIX = "log4j";
+
+ @Override
+ protected String[] getSupportedTypes() {
+ if (!PropertiesUtil.getProperties()
+ .getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, Boolean.FALSE)) {
+ return null;
+ }
+ return new String[] {FILE_EXTENSION};
+ }
+
+ @Override
+ public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
+ final int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0);
+ return new PropertiesConfiguration(loggerContext, source, interval);
+ }
+
+ @Override
+ protected String getTestPrefix() {
+ return TEST_PREFIX;
+ }
+
+ @Override
+ protected String getDefaultPrefix() {
+ return DEFAULT_PREFIX;
+ }
+
+ @Override
+ protected String getVersion() {
+ return LOG4J1_VERSION;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java
index 342024ad9f2..47dd263d814 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetter.java
@@ -1,47 +1,151 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.config;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Properties;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Priority;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.util.OptionConverter;
+import org.apache.logging.log4j.status.StatusLogger;
/**
+ * General purpose Object property setter. Clients repeatedly invokes
+ * {@link #setProperty setProperty(name,value)} in order to invoke setters
+ * on the Object specified in the constructor. This class relies on the
+ * JavaBeans {@link Introspector} to analyze the given Object Class using
+ * reflection.
*
- * @since 1.1
+ * Usage:
+ *
+ * PropertySetter ps = new PropertySetter(anObject);
+ * ps.set("name", "Joe");
+ * ps.set("age", "32");
+ * ps.set("isMale", "true");
+ *
+ * will cause the invocations anObject.setName("Joe"), anObject.setAge(32),
+ * and setMale(true) if such methods exist with those signatures.
+ * Otherwise an {@link IntrospectionException} are thrown.
*/
public class PropertySetter {
+ private static final PropertyDescriptor[] EMPTY_PROPERTY_DESCRIPTOR_ARRAY = {};
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ protected Object obj;
+ protected PropertyDescriptor[] props;
/**
* Create a new PropertySetter for the specified Object. This is done
* in preparation for invoking {@link #setProperty} one or more times.
*
- * @param obj the object for which to set properties
+ * @param obj the object for which to set properties
*/
public PropertySetter(final Object obj) {
+ this.obj = obj;
}
-
/**
- * Set the properties for the object that match the prefix
passed as parameter.
+ * Set the properties of an object passed as a parameter in one
+ * go. The properties
are parsed relative to a
+ * prefix
.
*
- * @param properties The properties
- * @param prefix The prefix
+ * @param obj The object to configure.
+ * @param properties A java.util.Properties containing keys and values.
+ * @param prefix Only keys having the specified prefix will be set.
+ */
+ public static void setProperties(final Object obj, final Properties properties, final String prefix) {
+ new PropertySetter(obj).setProperties(properties, prefix);
+ }
+
+ /**
+ * Uses JavaBeans {@link Introspector} to computer setters of object to be
+ * configured.
+ */
+ protected void introspect() {
+ try {
+ final BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
+ props = bi.getPropertyDescriptors();
+ } catch (IntrospectionException ex) {
+ LOGGER.error("Failed to introspect {}: {}", obj, ex.getMessage());
+ props = EMPTY_PROPERTY_DESCRIPTOR_ARRAY;
+ }
+ }
+
+ /**
+ * Set the properties for the object that match the
+ * prefix
passed as parameter.
+ * @param properties The properties.
+ * @param prefix The prefix of the properties to use.
*/
public void setProperties(final Properties properties, final String prefix) {
+ final int len = prefix.length();
+
+ for (String key : properties.stringPropertyNames()) {
+
+ // handle only properties that start with the desired prefix.
+ if (key.startsWith(prefix)) {
+
+ // ignore key if it contains dots after the prefix
+ if (key.indexOf('.', len + 1) > 0) {
+ continue;
+ }
+
+ final String value = OptionConverter.findAndSubst(key, properties);
+ key = key.substring(len);
+ if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) {
+ continue;
+ }
+ //
+ // if the property type is an OptionHandler
+ // (for example, triggeringPolicy of org.apache.log4j.rolling.RollingFileAppender)
+ final PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key));
+ if (prop != null
+ && OptionHandler.class.isAssignableFrom(prop.getPropertyType())
+ && prop.getWriteMethod() != null) {
+ final OptionHandler opt = (OptionHandler)
+ OptionConverter.instantiateByKey(properties, prefix + key, prop.getPropertyType(), null);
+ final PropertySetter setter = new PropertySetter(opt);
+ setter.setProperties(properties, prefix + key + ".");
+ try {
+ prop.getWriteMethod().invoke(this.obj, opt);
+ } catch (InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof InterruptedException
+ || ex.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.warn("Failed to set property [{}] to value \"{}\".", key, value, ex);
+ } catch (IllegalAccessException | RuntimeException ex) {
+ LOGGER.warn("Failed to set property [{}] to value \"{}\".", key, value, ex);
+ }
+ continue;
+ }
+
+ setProperty(key, value);
+ }
+ }
+ activate();
}
/**
@@ -56,33 +160,120 @@ public void setProperties(final Properties properties, final String prefix) {
* to an int using new Integer(value). If the setter expects a boolean,
* the conversion is by new Boolean(value).
*
- * @param name name of the property
- * @param value String value of the property
+ * @param name name of the property
+ * @param value String value of the property
*/
- public void setProperty(final String name, final String value) {
+ public void setProperty(String name, String value) {
+ if (value == null) {
+ return;
+ }
+
+ name = Introspector.decapitalize(name);
+ final PropertyDescriptor prop = getPropertyDescriptor(name);
+
+ // LOGGER.debug("---------Key: "+name+", type="+prop.getPropertyType());
+
+ if (prop == null) {
+ LOGGER.warn("No such property [" + name + "] in " + obj.getClass().getName() + ".");
+ } else {
+ try {
+ setProperty(prop, name, value);
+ } catch (PropertySetterException ex) {
+ LOGGER.warn("Failed to set property [{}] to value \"{}\".", name, value, ex.rootCause);
+ }
+ }
}
/**
* Set the named property given a {@link PropertyDescriptor}.
*
- * @param prop A PropertyDescriptor describing the characteristics of the property to set.
- * @param name The named of the property to set.
+ * @param prop A PropertyDescriptor describing the characteristics
+ * of the property to set.
+ * @param name The named of the property to set.
* @param value The value of the property.
- * @throws PropertySetterException (Never actually throws this exception. Kept for historical purposes.)
+ * @throws PropertySetterException if no setter is available.
*/
- public void setProperty(final PropertyDescriptor prop, final String name, final String value)
- throws PropertySetterException {
+ public void setProperty(PropertyDescriptor prop, String name, String value) throws PropertySetterException {
+ final Method setter = prop.getWriteMethod();
+ if (setter == null) {
+ throw new PropertySetterException("No setter for property [" + name + "].");
+ }
+ final Class>[] paramTypes = setter.getParameterTypes();
+ if (paramTypes.length != 1) {
+ throw new PropertySetterException("#params for setter != 1");
+ }
+
+ Object arg;
+ try {
+ arg = convertArg(value, paramTypes[0]);
+ } catch (Throwable t) {
+ throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed. Reason: " + t);
+ }
+ if (arg == null) {
+ throw new PropertySetterException("Conversion to type [" + paramTypes[0] + "] failed.");
+ }
+ LOGGER.debug("Setting property [" + name + "] to [" + arg + "].");
+ try {
+ setter.invoke(obj, arg);
+ } catch (InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof InterruptedException
+ || ex.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ throw new PropertySetterException(ex);
+ } catch (IllegalAccessException | RuntimeException ex) {
+ throw new PropertySetterException(ex);
+ }
}
/**
- * Set the properties of an object passed as a parameter in one
- * go. The properties
are parsed relative to a
- * prefix
.
- *
- * @param obj The object to configure.
- * @param properties A java.util.Properties containing keys and values.
- * @param prefix Only keys having the specified prefix will be set.
+ * Convert val
a String parameter to an object of a
+ * given type.
+ * @param val The value to convert.
+ * @param type The type of the value to convert to.
+ * @return The result of the conversion.
*/
- public static void setProperties(final Object obj, final Properties properties, final String prefix) {
+ protected Object convertArg(final String val, final Class> type) {
+ if (val == null) {
+ return null;
+ }
+
+ final String v = val.trim();
+ if (String.class.isAssignableFrom(type)) {
+ return val;
+ } else if (Integer.TYPE.isAssignableFrom(type)) {
+ return Integer.parseInt(v);
+ } else if (Long.TYPE.isAssignableFrom(type)) {
+ return Long.parseLong(v);
+ } else if (Boolean.TYPE.isAssignableFrom(type)) {
+ if ("true".equalsIgnoreCase(v)) {
+ return Boolean.TRUE;
+ } else if ("false".equalsIgnoreCase(v)) {
+ return Boolean.FALSE;
+ }
+ } else if (Priority.class.isAssignableFrom(type)) {
+ return org.apache.log4j.helpers.OptionConverter.toLevel(v, Log4j1Configuration.DEFAULT_LEVEL);
+ } else if (ErrorHandler.class.isAssignableFrom(type)) {
+ return OptionConverter.instantiateByClassName(v, ErrorHandler.class, null);
+ }
+ return null;
+ }
+
+ protected PropertyDescriptor getPropertyDescriptor(String name) {
+ if (props == null) {
+ introspect();
+ }
+ for (PropertyDescriptor prop : props) {
+ if (name.equals(prop.getName())) {
+ return prop;
+ }
+ }
+ return null;
+ }
+
+ public void activate() {
+ if (obj instanceof OptionHandler) {
+ ((OptionHandler) obj).activateOptions();
+ }
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetterException.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetterException.java
index c9dc4cfb579..be1a3df0cd2 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetterException.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/PropertySetterException.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.config;
@@ -45,7 +45,6 @@ public PropertySetterException(final String msg) {
* @param rootCause The root cause
*/
public PropertySetterException(final Throwable rootCause) {
- super();
this.rootCause = rootCause;
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java
index 7f96630203d..396cf9bbd32 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/package-info.java
@@ -17,4 +17,11 @@
/**
* Log4j 1.x compatibility layer.
*/
+@Export
+@Version("2.20.1")
+@Open("org.apache.logging.log4j.core")
package org.apache.log4j.config;
+
+import aQute.bnd.annotation.jpms.Open;
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
new file mode 100644
index 00000000000..f8ffdb7053d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
@@ -0,0 +1,134 @@
+/*
+ * 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.log4j.helpers;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Formats a {@link Date} in the format "HH:mm:ss,SSS" for example, "15:49:37,459".
+ *
+ * @since 0.7.5
+ */
+public class AbsoluteTimeDateFormat extends DateFormat {
+
+ private static final long serialVersionUID = -388856345976723342L;
+
+ /**
+ * String constant used to specify {@link org.apache.log4j.helpers.AbsoluteTimeDateFormat} in layouts. Current value is
+ * ABSOLUTE .
+ */
+ public static final String ABS_TIME_DATE_FORMAT = "ABSOLUTE";
+
+ /**
+ * String constant used to specify {@link org.apache.log4j.helpers.DateTimeDateFormat} in layouts. Current value is
+ * DATE .
+ */
+ public static final String DATE_AND_TIME_DATE_FORMAT = "DATE";
+
+ /**
+ * String constant used to specify {@link org.apache.log4j.helpers.ISO8601DateFormat} in layouts. Current value is
+ * ISO8601 .
+ */
+ public static final String ISO8601_DATE_FORMAT = "ISO8601";
+
+ private static long previousTime;
+
+ private static char[] previousTimeWithoutMillis = new char[9]; // "HH:mm:ss."
+
+ public AbsoluteTimeDateFormat() {
+ setCalendar(Calendar.getInstance());
+ }
+
+ public AbsoluteTimeDateFormat(final TimeZone timeZone) {
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ /**
+ * Appends to sbuf
the time in the format "HH:mm:ss,SSS" for example, "15:49:37,459"
+ *
+ * @param date the date to format
+ * @param sbuf the string buffer to write to
+ * @param fieldPosition remains untouched
+ */
+ @Override
+ public StringBuffer format(final Date date, final StringBuffer sbuf, final FieldPosition fieldPosition) {
+
+ final long now = date.getTime();
+ final int millis = (int) (now % 1000);
+
+ if ((now - millis) != previousTime || previousTimeWithoutMillis[0] == 0) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ final int start = sbuf.length();
+
+ final int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if (hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ final int mins = calendar.get(Calendar.MINUTE);
+ if (mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ final int secs = calendar.get(Calendar.SECOND);
+ if (secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0);
+
+ previousTime = now - millis;
+ } else {
+ sbuf.append(previousTimeWithoutMillis);
+ }
+
+ if (millis < 100) {
+ sbuf.append('0');
+ }
+ if (millis < 10) {
+ sbuf.append('0');
+ }
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ * Always returns null.
+ */
+ @Override
+ public Date parse(final String s, final ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
new file mode 100644
index 00000000000..6b3090559a2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
@@ -0,0 +1,106 @@
+/*
+ * 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.log4j.helpers;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Objects;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import org.apache.log4j.Appender;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Allows Classes to attach Appenders.
+ */
+public class AppenderAttachableImpl implements AppenderAttachable {
+
+ private final ConcurrentMap appenders = new ConcurrentHashMap<>();
+
+ /** Array of appenders. TODO */
+ protected Vector appenderList;
+
+ @Override
+ public void addAppender(final Appender appender) {
+ if (appender != null) {
+ // NullAppender name is null.
+ appenders.put(Objects.toString(appender.getName()), appender);
+ }
+ }
+
+ /**
+ * Calls the doAppend
method on all attached appenders.
+ *
+ * @param event The event to log.
+ * @return The number of appenders.
+ */
+ public int appendLoopOnAppenders(final LoggingEvent event) {
+ for (final Appender appender : appenders.values()) {
+ appender.doAppend(event);
+ }
+ return appenders.size();
+ }
+
+ /**
+ * Closes all appenders.
+ */
+ public void close() {
+ for (final Appender appender : appenders.values()) {
+ appender.close();
+ }
+ }
+
+ @Override
+ public Enumeration getAllAppenders() {
+ return Collections.enumeration(appenders.values());
+ }
+
+ @Override
+ public Appender getAppender(final String name) {
+ // No null keys allowed in a CHM.
+ return name == null ? null : appenders.get(name);
+ }
+
+ @Override
+ public boolean isAttached(final Appender appender) {
+ return appender != null ? appenders.containsValue(appender) : false;
+ }
+
+ @Override
+ public void removeAllAppenders() {
+ appenders.clear();
+ }
+
+ @Override
+ public void removeAppender(final Appender appender) {
+ if (appender != null) {
+ final String name = appender.getName();
+ if (name != null) {
+ appenders.remove(name, appender);
+ }
+ }
+ }
+
+ @Override
+ public void removeAppender(final String name) {
+ if (name != null) {
+ appenders.remove(name);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java
new file mode 100644
index 00000000000..404a58a57ed
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java
@@ -0,0 +1,162 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Bounded first-in-first-out buffer.
+ *
+ * @since version 0.9.1
+ */
+public class BoundedFIFO {
+
+ LoggingEvent[] buf;
+ int numElements = 0;
+ int first = 0;
+ int next = 0;
+ int maxSize;
+
+ /**
+ * Constructs a new instance with a maximum size passed as argument.
+ */
+ public BoundedFIFO(final int maxSize) {
+ if (maxSize < 1) {
+ throw new IllegalArgumentException("The maxSize argument (" + maxSize + ") is not a positive integer.");
+ }
+ this.maxSize = maxSize;
+ buf = new LoggingEvent[maxSize];
+ }
+
+ /**
+ * Gets the first element in the buffer. Returns null
if there are no elements in the buffer.
+ */
+ public LoggingEvent get() {
+ if (numElements == 0) {
+ return null;
+ }
+
+ final LoggingEvent r = buf[first];
+ buf[first] = null; // help garbage collection
+
+ if (++first == maxSize) {
+ first = 0;
+ }
+ numElements--;
+ return r;
+ }
+
+ /**
+ * Gets the maximum size of the buffer.
+ */
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ * Returns true
if the buffer is full, that is, whether the number of elements in the buffer equals the
+ * buffer size.
+ */
+ public boolean isFull() {
+ return numElements == maxSize;
+ }
+
+ /**
+ * Gets the number of elements in the buffer. This number is guaranteed to be in the range 0 to maxSize
+ * (inclusive).
+ */
+ public int length() {
+ return numElements;
+ }
+
+ int min(final int a, final int b) {
+ return a < b ? a : b;
+ }
+
+ /**
+ * Puts a {@link LoggingEvent} in the buffer. If the buffer is full then the event is silently dropped . It is the
+ * caller's responsability to make sure that the buffer has free space.
+ */
+ public void put(final LoggingEvent o) {
+ if (numElements != maxSize) {
+ buf[next] = o;
+ if (++next == maxSize) {
+ next = 0;
+ }
+ numElements++;
+ }
+ }
+
+ /**
+ * Resizes the buffer to a new size. If the new size is smaller than the old size events might be lost.
+ *
+ * @since 1.1
+ */
+ public synchronized void resize(final int newSize) {
+ if (newSize == maxSize) {
+ return;
+ }
+
+ final LoggingEvent[] tmp = new LoggingEvent[newSize];
+
+ // we should not copy beyond the buf array
+ int len1 = maxSize - first;
+
+ // we should not copy beyond the tmp array
+ len1 = min(len1, newSize);
+
+ // er.. how much do we actually need to copy?
+ // We should not copy more than the actual number of elements.
+ len1 = min(len1, numElements);
+
+ // Copy from buf starting a first, to tmp, starting at position 0, len1 elements.
+ System.arraycopy(buf, first, tmp, 0, len1);
+
+ // Are there any uncopied elements and is there still space in the new array?
+ int len2 = 0;
+ if ((len1 < numElements) && (len1 < newSize)) {
+ len2 = numElements - len1;
+ len2 = min(len2, newSize - len1);
+ System.arraycopy(buf, 0, tmp, len1, len2);
+ }
+
+ this.buf = tmp;
+ this.maxSize = newSize;
+ this.first = 0;
+ this.numElements = len1 + len2;
+ this.next = this.numElements;
+ if (this.next == this.maxSize) {
+ this.next = 0;
+ }
+ }
+
+ /**
+ * Returns true
if there is just one element in the buffer. In other words, if there were no elements
+ * before the last {@link #put} operation completed.
+ */
+ public boolean wasEmpty() {
+ return numElements == 1;
+ }
+
+ /**
+ * Returns true
if the number of elements in the buffer plus 1 equals the maximum buffer size, returns
+ * false
otherwise.
+ */
+ public boolean wasFull() {
+ return numElements + 1 == maxSize;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java
new file mode 100644
index 00000000000..d806cc5628c
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.log4j.helpers;
+
+import java.io.IOException;
+import java.io.Writer;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.ErrorHandler;
+
+/**
+ * Counts the number of bytes written.
+ *
+ * @since 0.8.1
+ */
+public class CountingQuietWriter extends QuietWriter {
+
+ protected long count;
+
+ public CountingQuietWriter(final Writer writer, final ErrorHandler eh) {
+ super(writer, eh);
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ public void setCount(final long count) {
+ this.count = count;
+ }
+
+ @Override
+ public void write(final String string) {
+ try {
+ out.write(string);
+ count += string.length();
+ } catch (final IOException e) {
+ errorHandler.error("Write failure.", e, ErrorCode.WRITE_FAILURE);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java
new file mode 100644
index 00000000000..882f495ef7b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java
@@ -0,0 +1,146 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Holds {@link LoggingEvent LoggingEvents} for immediate or differed display.
+ *
+ *
+ * This buffer gives read access to any element in the buffer not just the first or last element.
+ *
+ *
+ * @since 0.9.0
+ */
+public class CyclicBuffer {
+
+ LoggingEvent[] ea;
+ int first;
+ int last;
+ int numElems;
+ int maxSize;
+
+ /**
+ * Constructs a new instance of at most maxSize
events.
+ *
+ * The maxSize
argument must a positive integer.
+ *
+ * @param maxSize The maximum number of elements in the buffer.
+ */
+ public CyclicBuffer(final int maxSize) throws IllegalArgumentException {
+ if (maxSize < 1) {
+ throw new IllegalArgumentException("The maxSize argument (" + maxSize + ") is not a positive integer.");
+ }
+ this.maxSize = maxSize;
+ ea = new LoggingEvent[maxSize];
+ first = 0;
+ last = 0;
+ numElems = 0;
+ }
+
+ /**
+ * Adds an event
as the last event in the buffer.
+ */
+ public void add(final LoggingEvent event) {
+ ea[last] = event;
+ if (++last == maxSize) {
+ last = 0;
+ }
+
+ if (numElems < maxSize) {
+ numElems++;
+ } else if (++first == maxSize) {
+ first = 0;
+ }
+ }
+
+ /**
+ * Gets the oldest (first) element in the buffer. The oldest element is removed from the buffer.
+ */
+ public LoggingEvent get() {
+ LoggingEvent r = null;
+ if (numElems > 0) {
+ numElems--;
+ r = ea[first];
+ ea[first] = null;
+ if (++first == maxSize) {
+ first = 0;
+ }
+ }
+ return r;
+ }
+
+ /**
+ * Gets the i th oldest event currently in the buffer. If i is outside the range 0 to the number of
+ * elements currently in the buffer, then null
is returned.
+ */
+ public LoggingEvent get(final int i) {
+ if (i < 0 || i >= numElems) {
+ return null;
+ }
+
+ return ea[(first + i) % maxSize];
+ }
+
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ * Gets the number of elements in the buffer. This number is guaranteed to be in the range 0 to maxSize
+ * (inclusive).
+ */
+ public int length() {
+ return numElems;
+ }
+
+ /**
+ * Resizes the cyclic buffer to newSize
.
+ *
+ * @throws IllegalArgumentException if newSize
is negative.
+ */
+ public void resize(final int newSize) {
+ if (newSize < 0) {
+ throw new IllegalArgumentException("Negative array size [" + newSize + "] not allowed.");
+ }
+ if (newSize == numElems) {
+ return; // nothing to do
+ }
+
+ final LoggingEvent[] temp = new LoggingEvent[newSize];
+
+ final int loopLen = newSize < numElems ? newSize : numElems;
+
+ for (int i = 0; i < loopLen; i++) {
+ temp[i] = ea[first];
+ ea[first] = null;
+ if (++first == numElems) {
+ first = 0;
+ }
+ }
+ ea = temp;
+ first = 0;
+ numElems = loopLen;
+ maxSize = newSize;
+ if (loopLen == newSize) {
+ last = 0;
+ } else {
+ last = loopLen;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/DateLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/DateLayout.java
new file mode 100644
index 00000000000..5412b1b4f7e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/DateLayout.java
@@ -0,0 +1,173 @@
+/*
+ * 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.log4j.helpers;
+
+import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This abstract layout takes care of all the date related options and formatting work.
+ */
+public abstract class DateLayout extends Layout {
+
+ /**
+ * String constant designating no time information. Current value of this constant is NULL .
+ *
+ */
+ public static final String NULL_DATE_FORMAT = "NULL";
+
+ /**
+ * String constant designating relative time. Current value of this constant is RELATIVE .
+ */
+ public static final String RELATIVE_TIME_DATE_FORMAT = "RELATIVE";
+
+ /**
+ * @deprecated Options are now handled using the JavaBeans paradigm. This constant is not longer needed and will be
+ * removed in the near term.
+ */
+ @Deprecated
+ public static final String DATE_FORMAT_OPTION = "DateFormat";
+
+ /**
+ * @deprecated Options are now handled using the JavaBeans paradigm. This constant is not longer needed and will be
+ * removed in the near term.
+ */
+ @Deprecated
+ public static final String TIMEZONE_OPTION = "TimeZone";
+
+ protected FieldPosition pos = new FieldPosition(0);
+
+ private String timeZoneID;
+ private String dateFormatOption;
+
+ protected DateFormat dateFormat;
+ protected Date date = new Date();
+
+ public void activateOptions() {
+ setDateFormat(dateFormatOption);
+ if (timeZoneID != null && dateFormat != null) {
+ dateFormat.setTimeZone(TimeZone.getTimeZone(timeZoneID));
+ }
+ }
+
+ public void dateFormat(final StringBuffer buf, final LoggingEvent event) {
+ if (dateFormat != null) {
+ date.setTime(event.timeStamp);
+ dateFormat.format(date, buf, this.pos);
+ buf.append(' ');
+ }
+ }
+
+ /**
+ * Returns value of the DateFormat option.
+ */
+ public String getDateFormat() {
+ return dateFormatOption;
+ }
+
+ /**
+ * @deprecated Use the setter method for the option directly instead of the generic setOption
method.
+ */
+ @Deprecated
+ public String[] getOptionStrings() {
+ return new String[] {DATE_FORMAT_OPTION, TIMEZONE_OPTION};
+ }
+
+ /**
+ * Returns value of the TimeZone option.
+ */
+ public String getTimeZone() {
+ return timeZoneID;
+ }
+
+ /**
+ * Sets the {@link DateFormat} used to format time and date in the zone determined by timeZone
.
+ */
+ public void setDateFormat(final DateFormat dateFormat, final TimeZone timeZone) {
+ this.dateFormat = dateFormat;
+ this.dateFormat.setTimeZone(timeZone);
+ }
+
+ /**
+ * The value of the DateFormat option should be either an argument to the constructor of {@link SimpleDateFormat}
+ * or one of the srings "NULL", "RELATIVE", "ABSOLUTE", "DATE" or "ISO8601.
+ */
+ public void setDateFormat(final String dateFormat) {
+ if (dateFormat != null) {
+ dateFormatOption = dateFormat;
+ }
+ setDateFormat(dateFormatOption, TimeZone.getDefault());
+ }
+
+ /**
+ * Sets the DateFormat used to format date and time in the time zone determined by timeZone
parameter. The
+ * {@link DateFormat} used will depend on the dateFormatType
.
+ *
+ *
+ * The recognized types are {@link #NULL_DATE_FORMAT}, {@link #RELATIVE_TIME_DATE_FORMAT}
+ * {@link AbsoluteTimeDateFormat#ABS_TIME_DATE_FORMAT}, {@link AbsoluteTimeDateFormat#DATE_AND_TIME_DATE_FORMAT} and
+ * {@link AbsoluteTimeDateFormat#ISO8601_DATE_FORMAT}. If the dateFormatType
is not one of the above, then
+ * the argument is assumed to be a date pattern for {@link SimpleDateFormat}.
+ */
+ public void setDateFormat(final String dateFormatType, final TimeZone timeZone) {
+ if (dateFormatType == null) {
+ this.dateFormat = null;
+ return;
+ }
+ if (dateFormatType.equalsIgnoreCase(NULL_DATE_FORMAT)) {
+ this.dateFormat = null;
+ } else if (dateFormatType.equalsIgnoreCase(RELATIVE_TIME_DATE_FORMAT)) {
+ this.dateFormat = new RelativeTimeDateFormat();
+ } else if (dateFormatType.equalsIgnoreCase(AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT)) {
+ this.dateFormat = new AbsoluteTimeDateFormat(timeZone);
+ } else if (dateFormatType.equalsIgnoreCase(AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT)) {
+ this.dateFormat = new DateTimeDateFormat(timeZone);
+ } else if (dateFormatType.equalsIgnoreCase(AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT)) {
+ this.dateFormat = new ISO8601DateFormat(timeZone);
+ } else {
+ this.dateFormat = new SimpleDateFormat(dateFormatType);
+ this.dateFormat.setTimeZone(timeZone);
+ }
+ }
+
+ /**
+ * @deprecated Use the setter method for the option directly instead of the generic setOption
method.
+ */
+ @Deprecated
+ public void setOption(final String option, final String value) {
+ if (option.equalsIgnoreCase(DATE_FORMAT_OPTION)) {
+ dateFormatOption = toRootUpperCase(value);
+ } else if (option.equalsIgnoreCase(TIMEZONE_OPTION)) {
+ timeZoneID = value;
+ }
+ }
+
+ /**
+ * The TimeZoneID option is a time zone ID string in the format expected by the {@link TimeZone#getTimeZone}
+ * method.
+ */
+ public void setTimeZone(final String timeZone) {
+ this.timeZoneID = timeZone;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java
new file mode 100644
index 00000000000..aa117c8feea
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java
@@ -0,0 +1,80 @@
+/*
+ * 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.log4j.helpers;
+
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Formats a {@link Date} in the format "dd MMM yyyy HH:mm:ss,SSS" for example, "06 Nov 1994 15:49:37,459".
+ *
+ * @since 0.7.5
+ */
+public class DateTimeDateFormat extends AbsoluteTimeDateFormat {
+ private static final long serialVersionUID = 5547637772208514971L;
+
+ String[] shortMonths;
+
+ public DateTimeDateFormat() {
+ super();
+ shortMonths = new DateFormatSymbols().getShortMonths();
+ }
+
+ public DateTimeDateFormat(final TimeZone timeZone) {
+ this();
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ /**
+ * Appends to sbuf
the date in the format "dd MMM yyyy HH:mm:ss,SSS" for example, "06 Nov 1994
+ * 08:49:37,459".
+ *
+ * @param sbuf the string buffer to write to
+ */
+ @Override
+ public StringBuffer format(final Date date, final StringBuffer sbuf, final FieldPosition fieldPosition) {
+
+ calendar.setTime(date);
+
+ final int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if (day < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(day);
+ sbuf.append(' ');
+ sbuf.append(shortMonths[calendar.get(Calendar.MONTH)]);
+ sbuf.append(' ');
+
+ final int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+ sbuf.append(' ');
+
+ return super.format(date, sbuf, fieldPosition);
+ }
+
+ /**
+ * Always returns null.
+ */
+ @Override
+ public Date parse(final String s, final ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FileWatchdog.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FileWatchdog.java
new file mode 100644
index 00000000000..a43776bed76
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FileWatchdog.java
@@ -0,0 +1,105 @@
+/*
+ * 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.log4j.helpers;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.File;
+
+/**
+ * Checks every now and then that a certain file has not changed. If it has, then call the {@link #doOnChange} method.
+ *
+ * @since version 0.9.1
+ */
+public abstract class FileWatchdog extends Thread {
+
+ /**
+ * The default delay between every file modification check, set to 60 seconds.
+ */
+ public static final long DEFAULT_DELAY = 60_000;
+
+ /**
+ * The name of the file to observe for changes.
+ */
+ protected String filename;
+
+ /**
+ * The delay to observe between every check. By default set {@link #DEFAULT_DELAY}.
+ */
+ protected long delay = DEFAULT_DELAY;
+
+ File file;
+ long lastModified;
+ boolean warnedAlready;
+ boolean interrupted;
+
+ @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "The filename comes from a system property.")
+ protected FileWatchdog(final String fileName) {
+ super("FileWatchdog");
+ this.filename = fileName;
+ this.file = new File(fileName);
+ setDaemon(true);
+ checkAndConfigure();
+ }
+
+ protected void checkAndConfigure() {
+ boolean fileExists;
+ try {
+ fileExists = file.exists();
+ } catch (final SecurityException e) {
+ LogLog.warn("Was not allowed to read check file existance, file:[" + filename + "].");
+ interrupted = true; // there is no point in continuing
+ return;
+ }
+
+ if (fileExists) {
+ final long fileLastMod = file.lastModified(); // this can also throw a SecurityException
+ if (fileLastMod > lastModified) { // however, if we reached this point this
+ lastModified = fileLastMod; // is very unlikely.
+ doOnChange();
+ warnedAlready = false;
+ }
+ } else {
+ if (!warnedAlready) {
+ LogLog.debug("[" + filename + "] does not exist.");
+ warnedAlready = true;
+ }
+ }
+ }
+
+ protected abstract void doOnChange();
+
+ @Override
+ public void run() {
+ while (!interrupted) {
+ try {
+ Thread.sleep(delay);
+ } catch (final InterruptedException e) {
+ // no interruption expected
+ }
+ checkAndConfigure();
+ }
+ }
+
+ /**
+ * Sets the delay in milliseconds to observe between each check of the file changes.
+ *
+ * @param delayMillis the delay in milliseconds
+ */
+ public void setDelay(final long delayMillis) {
+ this.delay = delayMillis;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
new file mode 100644
index 00000000000..1d6a9082024
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
@@ -0,0 +1,38 @@
+/*
+ * 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.log4j.helpers;
+
+/**
+ * FormattingInfo instances contain the information obtained when parsing formatting modifiers in conversion modifiers.
+ *
+ * @since 0.8.2
+ */
+public class FormattingInfo {
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ void dump() {
+ LogLog.debug("min=" + min + ", max=" + max + ", leftAlign=" + leftAlign);
+ }
+
+ void reset() {
+ min = -1;
+ max = 0x7FFFFFFF;
+ leftAlign = false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java
new file mode 100644
index 00000000000..847bbb4ec0a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java
@@ -0,0 +1,171 @@
+/*
+ * 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.log4j.helpers;
+
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Formats a {@link Date} in the format "yyyy-MM-dd HH:mm:ss,SSS" for example "1999-11-27 15:49:37,459".
+ *
+ *
+ * Refer to the summary of the International Standard Date and Time
+ * Notation for more information on this format.
+ *
+ *
+ * @since 0.7.5
+ */
+public class ISO8601DateFormat extends AbsoluteTimeDateFormat {
+
+ private static final long serialVersionUID = -759840745298755296L;
+
+ private static long lastTime;
+
+ private static char[] lastTimeString = new char[20];
+
+ public ISO8601DateFormat() {}
+
+ public ISO8601DateFormat(final TimeZone timeZone) {
+ super(timeZone);
+ }
+
+ /**
+ * Appends a date in the format "YYYY-mm-dd HH:mm:ss,SSS" to sbuf
. For example: "1999-11-27 15:49:37,459".
+ *
+ * @param sbuf the StringBuffer
to write to
+ */
+ @Override
+ public StringBuffer format(final Date date, final StringBuffer sbuf, final FieldPosition fieldPosition) {
+
+ final long now = date.getTime();
+ final int millis = (int) (now % 1000);
+
+ if ((now - millis) != lastTime || lastTimeString[0] == 0) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ final int start = sbuf.length();
+
+ final int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+
+ String month;
+ switch (calendar.get(Calendar.MONTH)) {
+ case Calendar.JANUARY:
+ month = "-01-";
+ break;
+ case Calendar.FEBRUARY:
+ month = "-02-";
+ break;
+ case Calendar.MARCH:
+ month = "-03-";
+ break;
+ case Calendar.APRIL:
+ month = "-04-";
+ break;
+ case Calendar.MAY:
+ month = "-05-";
+ break;
+ case Calendar.JUNE:
+ month = "-06-";
+ break;
+ case Calendar.JULY:
+ month = "-07-";
+ break;
+ case Calendar.AUGUST:
+ month = "-08-";
+ break;
+ case Calendar.SEPTEMBER:
+ month = "-09-";
+ break;
+ case Calendar.OCTOBER:
+ month = "-10-";
+ break;
+ case Calendar.NOVEMBER:
+ month = "-11-";
+ break;
+ case Calendar.DECEMBER:
+ month = "-12-";
+ break;
+ default:
+ month = "-NA-";
+ break;
+ }
+ sbuf.append(month);
+
+ final int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if (day < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(day);
+
+ sbuf.append(' ');
+
+ final int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if (hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ final int mins = calendar.get(Calendar.MINUTE);
+ if (mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ final int secs = calendar.get(Calendar.SECOND);
+ if (secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
+ lastTime = now - millis;
+ } else {
+ sbuf.append(lastTimeString);
+ }
+
+ if (millis < 100) {
+ sbuf.append('0');
+ }
+ if (millis < 10) {
+ sbuf.append('0');
+ }
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ * Always returns null.
+ */
+ @Override
+ public Date parse(final String s, final ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/Loader.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/Loader.java
new file mode 100644
index 00000000000..bf5bb23ec38
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/Loader.java
@@ -0,0 +1,138 @@
+/*
+ * 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.log4j.helpers;
+
+import java.net.URL;
+
+/**
+ * Loads resources (or images) from various sources.
+ */
+public class Loader {
+
+ static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
+
+ private static boolean ignoreTCL;
+
+ static {
+ final String ignoreTCLProp = OptionConverter.getSystemProperty("log4j.ignoreTCL", null);
+ if (ignoreTCLProp != null) {
+ ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true);
+ }
+ }
+
+ /**
+ * This method will search for resource
in different places. The search order is as follows:
+ *
+ *
+ *
Search for resource
using the thread context class loader under Java2. If that fails, search for
+ * resource
using the class loader that loaded this class (Loader
).
+ *
+ *
+ *
Try one last time with ClassLoader.getSystemResource(resource)
.
+ *
+ *
+ */
+ public static URL getResource(final String resource) {
+ ClassLoader classLoader = null;
+ URL url = null;
+
+ try {
+ if (!ignoreTCL) {
+ classLoader = getTCL();
+ if (classLoader != null) {
+ LogLog.debug("Trying to find [" + resource + "] using context classloader " + classLoader + ".");
+ url = classLoader.getResource(resource);
+ if (url != null) {
+ return url;
+ }
+ }
+ }
+
+ // We could not find resource. Ler us now try with the
+ // ClassLoader that loaded this class.
+ classLoader = Loader.class.getClassLoader();
+ if (classLoader != null) {
+ LogLog.debug("Trying to find [" + resource + "] using " + classLoader + " class loader.");
+ url = classLoader.getResource(resource);
+ if (url != null) {
+ return url;
+ }
+ }
+ } catch (final Throwable t) {
+ // can't be InterruptedException or InterruptedIOException
+ // since not declared, must be error or RuntimeError.
+ LogLog.warn(TSTR, t);
+ }
+
+ // Last ditch attempt: get the resource from the class path. It
+ // may be the case that clazz was loaded by the Extentsion class
+ // loader which the parent of the system class loader. Hence the
+ // code below.
+ LogLog.debug("Trying to find [" + resource + "] using ClassLoader.getSystemResource().");
+ return ClassLoader.getSystemResource(resource);
+ }
+
+ /**
+ * Gets a resource by delegating to getResource(String).
+ *
+ * @param resource resource name
+ * @param clazz class, ignored.
+ * @return URL to resource or null.
+ * @deprecated as of 1.2.
+ */
+ @Deprecated
+ public static URL getResource(final String resource, final Class clazz) {
+ return getResource(resource);
+ }
+
+ /**
+ * Shorthand for {@code Thread.currentThread().getContextClassLoader()}.
+ */
+ private static ClassLoader getTCL() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ /**
+ * Always returns false since Java 1.x support is long gone.
+ *
+ * @return Always false.
+ */
+ public static boolean isJava1() {
+ return false;
+ }
+
+ /**
+ * Loads the specified class using the Thread
contextClassLoader
, if that fails try
+ * Class.forname.
+ *
+ * @param clazz The class to load.
+ * @return The Class.
+ * @throws ClassNotFoundException Never thrown, declared for compatibility.
+ */
+ public static Class loadClass(final String clazz) throws ClassNotFoundException {
+ // Just call Class.forName(clazz) if we are instructed to ignore the TCL.
+ if (ignoreTCL) {
+ return Class.forName(clazz);
+ }
+ try {
+ return getTCL().loadClass(clazz);
+ } catch (final Throwable t) {
+ // ignore
+ }
+ return Class.forName(clazz);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/LogLog.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/LogLog.java
new file mode 100644
index 00000000000..6c33b924987
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/LogLog.java
@@ -0,0 +1,172 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Logs statements from within Log4j.
+ *
+ *
+ * Log4j components cannot make Log4j logging calls. However, it is sometimes useful for the user to learn about what
+ * Log4j is doing. You can enable Log4j internal logging by defining the log4j.configDebug variable.
+ *
+ *
+ * All Log4j internal debug calls go to System.out
where as internal error messages are sent to
+ * System.err
. All internal messages are prepended with the string "log4j: ".
+ *
+ *
+ * @since 0.8.2
+ */
+public class LogLog {
+
+ private static final StatusLogger LOGGER = StatusLogger.getLogger();
+
+ /**
+ * Makes Log4j print log4j-internal debug statements to System.out
.
+ *
+ *
+ * The value of this string is {@value #DEBUG_KEY}
+ *
+ *
+ * Note that the search for all option names is case sensitive.
+ *
+ */
+ public static final String DEBUG_KEY = "log4j.debug";
+
+ /**
+ * Makes Log4j components print log4j-internal debug statements to System.out
.
+ *
+ *
+ * The value of this string is {@value #CONFIG_DEBUG_KEY}.
+ *
+ *
+ * Note that the search for all option names is case sensitive.
+ *
+ *
+ * @deprecated Use {@link #DEBUG_KEY} instead.
+ */
+ @Deprecated
+ public static final String CONFIG_DEBUG_KEY = "log4j.configDebug";
+
+ /**
+ * Debug enabled Enable or disable.
+ */
+ protected static boolean debugEnabled = false;
+
+ /**
+ * In quietMode not even errors generate any output.
+ */
+ private static boolean quietMode = false;
+
+ static {
+ String key = OptionConverter.getSystemProperty(DEBUG_KEY, null);
+ if (key == null) {
+ key = OptionConverter.getSystemProperty(CONFIG_DEBUG_KEY, null);
+ }
+ if (key != null) {
+ debugEnabled = OptionConverter.toBoolean(key, true);
+ }
+ }
+
+ /**
+ * Logs Log4j internal debug statements.
+ *
+ * @param message the message object to log.
+ */
+ public static void debug(final String message) {
+ if (debugEnabled && !quietMode) {
+ LOGGER.debug(message);
+ }
+ }
+
+ /**
+ * Logs Log4j internal debug statements.
+ *
+ * @param message the message object to log.
+ * @param throwable the {@code Throwable} to log, including its stack trace.
+ */
+ public static void debug(final String message, final Throwable throwable) {
+ if (debugEnabled && !quietMode) {
+ LOGGER.debug(message, throwable);
+ }
+ }
+
+ /**
+ * Logs Log4j internal error statements.
+ *
+ * @param message the message object to log.
+ */
+ public static void error(final String message) {
+ if (!quietMode) {
+ LOGGER.error(message);
+ }
+ }
+
+ /**
+ * Logs Log4j internal error statements.
+ *
+ * @param message the message object to log.
+ * @param throwable the {@code Throwable} to log, including its stack trace.
+ */
+ public static void error(final String message, final Throwable throwable) {
+ if (!quietMode) {
+ LOGGER.error(message, throwable);
+ }
+ }
+
+ /**
+ * Enables and disables Log4j internal logging.
+ *
+ * @param enabled Enable or disable.
+ */
+ public static void setInternalDebugging(final boolean enabled) {
+ debugEnabled = enabled;
+ }
+
+ /**
+ * In quite mode no LogLog generates strictly no output, not even for errors.
+ *
+ * @param quietMode A true for not
+ */
+ public static void setQuietMode(final boolean quietMode) {
+ LogLog.quietMode = quietMode;
+ }
+
+ /**
+ * Logs Log4j internal warning statements.
+ *
+ * @param message the message object to log.
+ */
+ public static void warn(final String message) {
+ if (!quietMode) {
+ LOGGER.warn(message);
+ }
+ }
+
+ /**
+ * Logs Log4j internal warnings.
+ *
+ * @param message the message object to log.
+ * @param throwable the {@code Throwable} to log, including its stack trace.
+ */
+ public static void warn(final String message, final Throwable throwable) {
+ if (!quietMode) {
+ LOGGER.warn(message, throwable);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/NullEnumeration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
index d0640044013..ff23eac8d92 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.helpers;
@@ -28,8 +28,7 @@
public final class NullEnumeration implements Enumeration {
private static final NullEnumeration INSTANCE = new NullEnumeration();
- private NullEnumeration() {
- }
+ private NullEnumeration() {}
public static NullEnumeration getInstance() {
return INSTANCE;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
new file mode 100644
index 00000000000..bc875512fa8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java
@@ -0,0 +1,711 @@
+/*
+ * 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.log4j.helpers;
+
+import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
+
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.spi.StandardLevel;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.apache.logging.log4j.util.PropertiesUtil;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * A convenience class to convert property values to specific types.
+ */
+public class OptionConverter {
+
+ private static class CharMap {
+ final char key;
+ final char replacement;
+
+ public CharMap(final char key, final char replacement) {
+ this.key = key;
+ this.replacement = replacement;
+ }
+ }
+
+ static String DELIM_START = "${";
+ static char DELIM_STOP = '}';
+ static int DELIM_START_LEN = 2;
+ static int DELIM_STOP_LEN = 1;
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ /**
+ * A Log4j 1.x level above or equal to this value is considered as OFF.
+ */
+ static final int MAX_CUTOFF_LEVEL =
+ Priority.FATAL_INT + 100 * (StandardLevel.FATAL.intLevel() - StandardLevel.OFF.intLevel() - 1) + 1;
+ /**
+ * A Log4j 1.x level below or equal to this value is considered as ALL.
+ *
+ * Log4j 2.x ALL to TRACE interval is shorter. This is {@link Priority#ALL_INT}
+ * plus the difference.
+ */
+ static final int MIN_CUTOFF_LEVEL = Priority.ALL_INT
+ + Level.TRACE_INT
+ - (Priority.ALL_INT + StandardLevel.ALL.intLevel())
+ + StandardLevel.TRACE.intLevel();
+ /**
+ * Cache of currently known levels.
+ */
+ static final ConcurrentMap LEVELS = new ConcurrentHashMap<>();
+ /**
+ * Postfix for all Log4j 2.x level names.
+ */
+ private static final String LOG4J2_LEVEL_CLASS = org.apache.logging.log4j.Level.class.getName();
+
+ private static final CharMap[] charMap = new CharMap[] {
+ new CharMap('n', '\n'),
+ new CharMap('r', '\r'),
+ new CharMap('t', '\t'),
+ new CharMap('f', '\f'),
+ new CharMap('\b', '\b'),
+ new CharMap('\"', '\"'),
+ new CharMap('\'', '\''),
+ new CharMap('\\', '\\')
+ };
+
+ public static String[] concatanateArrays(final String[] l, final String[] r) {
+ final int len = l.length + r.length;
+ final String[] a = new String[len];
+
+ System.arraycopy(l, 0, a, 0, l.length);
+ System.arraycopy(r, 0, a, l.length, r.length);
+
+ return a;
+ }
+
+ static int toLog4j2Level(final int v1Level) {
+ // I don't believe anyone uses values much bigger than FATAL
+ if (v1Level >= MAX_CUTOFF_LEVEL) {
+ return StandardLevel.OFF.intLevel();
+ }
+ // Linear transformation up to debug: CUTOFF_LEVEL -> OFF, DEBUG -> DEBUG
+ if (v1Level > Priority.DEBUG_INT) {
+ final int offset = Math.round((v1Level - Priority.DEBUG_INT) / 100.0f);
+ return StandardLevel.DEBUG.intLevel() - offset;
+ }
+ // Steeper linear transformation
+ if (v1Level > Level.TRACE_INT) {
+ final int offset = Math.round((v1Level - Level.TRACE_INT) / 50.0f);
+ return StandardLevel.TRACE.intLevel() - offset;
+ }
+ if (v1Level > MIN_CUTOFF_LEVEL) {
+ final int offset = Level.TRACE_INT - v1Level;
+ return StandardLevel.TRACE.intLevel() + offset;
+ }
+ return StandardLevel.ALL.intLevel();
+ }
+
+ static int toLog4j1Level(final int v2Level) {
+ if (v2Level == StandardLevel.ALL.intLevel()) {
+ return Priority.ALL_INT;
+ }
+ if (v2Level > StandardLevel.TRACE.intLevel()) {
+ return MIN_CUTOFF_LEVEL + (StandardLevel.ALL.intLevel() - v2Level);
+ }
+ // Inflating by 50
+ if (v2Level > StandardLevel.DEBUG.intLevel()) {
+ return Level.TRACE_INT + 50 * (StandardLevel.TRACE.intLevel() - v2Level);
+ }
+ // Inflating by 100
+ if (v2Level > StandardLevel.OFF.intLevel()) {
+ return Priority.DEBUG_INT + 100 * (StandardLevel.DEBUG.intLevel() - v2Level);
+ }
+ return Priority.OFF_INT;
+ }
+
+ static int toSyslogLevel(final int v2Level) {
+ if (v2Level <= StandardLevel.FATAL.intLevel()) {
+ return 0;
+ }
+ if (v2Level <= StandardLevel.ERROR.intLevel()) {
+ return 3
+ - (3 * (StandardLevel.ERROR.intLevel() - v2Level))
+ / (StandardLevel.ERROR.intLevel() - StandardLevel.FATAL.intLevel());
+ }
+ if (v2Level <= StandardLevel.WARN.intLevel()) {
+ return 4;
+ }
+ if (v2Level <= StandardLevel.INFO.intLevel()) {
+ return 6
+ - (2 * (StandardLevel.INFO.intLevel() - v2Level))
+ / (StandardLevel.INFO.intLevel() - StandardLevel.WARN.intLevel());
+ }
+ return 7;
+ }
+
+ public static org.apache.logging.log4j.Level createLevel(final Priority level) {
+ final String name =
+ toRootUpperCase(level.toString()) + "#" + level.getClass().getName();
+ return org.apache.logging.log4j.Level.forName(name, toLog4j2Level(level.toInt()));
+ }
+
+ public static org.apache.logging.log4j.Level convertLevel(final Priority level) {
+ return level != null ? level.getVersion2Level() : org.apache.logging.log4j.Level.ERROR;
+ }
+
+ /**
+ * @param level
+ * @return
+ */
+ public static Level convertLevel(final org.apache.logging.log4j.Level level) {
+ // level is standard or was created by Log4j 1.x custom level
+ Level actualLevel = toLevel(level.name(), null);
+ // level was created by Log4j 2.x
+ if (actualLevel == null) {
+ actualLevel = toLevel(LOG4J2_LEVEL_CLASS, level.name(), null);
+ }
+ return actualLevel != null ? actualLevel : Level.ERROR;
+ }
+
+ public static org.apache.logging.log4j.Level convertLevel(
+ final String level, final org.apache.logging.log4j.Level defaultLevel) {
+ final Level actualLevel = toLevel(level, null);
+ return actualLevel != null ? actualLevel.getVersion2Level() : defaultLevel;
+ }
+
+ public static String convertSpecialChars(final String s) {
+ char c;
+ final int len = s.length();
+ final StringBuilder sbuf = new StringBuilder(len);
+
+ int i = 0;
+ while (i < len) {
+ c = s.charAt(i++);
+ if (c == '\\') {
+ c = s.charAt(i++);
+ for (final CharMap entry : charMap) {
+ if (entry.key == c) {
+ c = entry.replacement;
+ }
+ }
+ }
+ sbuf.append(c);
+ }
+ return sbuf.toString();
+ }
+
+ /**
+ * Find the value corresponding to key
in
+ * props
. Then perform variable substitution on the
+ * found value.
+ * @param key The key used to locate the substitution string.
+ * @param props The properties to use in the substitution.
+ * @return The substituted string.
+ */
+ public static String findAndSubst(final String key, final Properties props) {
+ final String value = props.getProperty(key);
+ if (value == null) {
+ return null;
+ }
+
+ try {
+ return substVars(value, props);
+ } catch (final IllegalArgumentException e) {
+ LOGGER.error("Bad option value [{}].", value, e);
+ return value;
+ }
+ }
+
+ /**
+ * Very similar to System.getProperty
except
+ * that the {@link SecurityException} is hidden.
+ *
+ * @param key The key to search for.
+ * @param def The default value to return.
+ * @return the string value of the system property, or the default
+ * value if there is no property with that key.
+ * @since 1.1
+ */
+ public static String getSystemProperty(final String key, final String def) {
+ try {
+ return System.getProperty(key, def);
+ } catch (final Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
+ LOGGER.debug("Was not allowed to read system property \"{}\".", key);
+ return def;
+ }
+ }
+
+ /**
+ * Instantiate an object given a class name. Check that the
+ * className
is a subclass of
+ * superClass
. If that test fails or the object could
+ * not be instantiated, then defaultValue
is returned.
+ *
+ * @param className The fully qualified class name of the object to instantiate.
+ * @param superClass The class to which the new object should belong.
+ * @param defaultValue The object to return in case of non-fulfillment
+ * @return The created object.
+ */
+ public static Object instantiateByClassName(
+ final String className, final Class> superClass, final Object defaultValue) {
+ if (className != null) {
+ try {
+ final Object obj = LoaderUtil.newInstanceOf(className);
+ if (!superClass.isAssignableFrom(obj.getClass())) {
+ LOGGER.error(
+ "A \"{}\" object is not assignable to a \"{}\" variable", className, superClass.getName());
+ return defaultValue;
+ }
+ return obj;
+ } catch (final ReflectiveOperationException e) {
+ LOGGER.error("Could not instantiate class [" + className + "].", e);
+ }
+ }
+ return defaultValue;
+ }
+
+ public static Object instantiateByKey(
+ final Properties props, final String key, final Class superClass, final Object defaultValue) {
+
+ // Get the value of the property in string form
+ final String className = findAndSubst(key, props);
+ if (className == null) {
+ LogLog.error("Could not find value for key " + key);
+ return defaultValue;
+ }
+ // Trim className to avoid trailing spaces that cause problems.
+ return OptionConverter.instantiateByClassName(className.trim(), superClass, defaultValue);
+ }
+
+ /**
+ * Configure log4j given an {@link InputStream}.
+ *
+ * The InputStream will be interpreted by a new instance of a log4j configurator.
+ *
+ *
+ * All configurations steps are taken on the hierarchy
passed as a parameter.
+ *
+ *
+ * @param inputStream The configuration input stream.
+ * @param clazz The class name, of the log4j configurator which will parse the inputStream
. This must be a
+ * subclass of {@link Configurator}, or null. If this value is null then a default configurator of
+ * {@link PropertyConfigurator} is used.
+ * @param hierarchy The {@link LoggerRepository} to act on.
+ * @since 1.2.17
+ */
+ public static void selectAndConfigure(
+ final InputStream inputStream, final String clazz, final LoggerRepository hierarchy) {
+ Configurator configurator = null;
+
+ if (clazz != null) {
+ LOGGER.debug("Preferred configurator class: " + clazz);
+ configurator = (Configurator) instantiateByClassName(clazz, Configurator.class, null);
+ if (configurator == null) {
+ LOGGER.error("Could not instantiate configurator [" + clazz + "].");
+ return;
+ }
+ } else {
+ configurator = new PropertyConfigurator();
+ }
+
+ configurator.doConfigure(inputStream, hierarchy);
+ }
+
+ /**
+ * Configure log4j given a URL.
+ *
+ * The url must point to a file or resource which will be interpreted by a new instance of a log4j configurator.
+ *
+ *
+ * All configurations steps are taken on the hierarchy
passed as a parameter.
+ *
+ *
+ * @param url The location of the configuration file or resource.
+ * @param clazz The classname, of the log4j configurator which will parse the file or resource at url
. This
+ * must be a subclass of {@link Configurator}, or null. If this value is null then a default configurator of
+ * {@link PropertyConfigurator} is used, unless the filename pointed to by url
ends in '.xml', in
+ * which case {@link org.apache.log4j.xml.DOMConfigurator} is used.
+ * @param hierarchy The {@link LoggerRepository} to act on.
+ *
+ * @since 1.1.4
+ */
+ public static void selectAndConfigure(final URL url, String clazz, final LoggerRepository hierarchy) {
+ Configurator configurator = null;
+ final String filename = url.getFile();
+
+ if (clazz == null && filename != null && filename.endsWith(".xml")) {
+ clazz = "org.apache.log4j.xml.DOMConfigurator";
+ }
+
+ if (clazz != null) {
+ LOGGER.debug("Preferred configurator class: " + clazz);
+ configurator = (Configurator) instantiateByClassName(clazz, Configurator.class, null);
+ if (configurator == null) {
+ LOGGER.error("Could not instantiate configurator [" + clazz + "].");
+ return;
+ }
+ } else {
+ configurator = new PropertyConfigurator();
+ }
+
+ configurator.doConfigure(url, hierarchy);
+ }
+
+ /**
+ * Perform variable substitution in string val
from the
+ * values of keys found in the system propeties.
+ *
+ * The variable substitution delimeters are ${ and } .
+ *
+ *
For example, if the System properties contains "key=value", then
+ * the call
+ *
+ * String s = OptionConverter.substituteVars("Value of key is ${key}.");
+ *
+ *
+ * will set the variable s
to "Value of key is value.".
+ *
+ *
If no value could be found for the specified key, then the
+ * props
parameter is searched, if the value could not
+ * be found there, then substitution defaults to the empty string.
+ *
+ *
For example, if system propeties contains no value for the key
+ * "inexistentKey", then the call
+ *
+ *
+ * String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
+ *
+ * will set s
to "Value of inexistentKey is []"
+ *
+ * An {@link IllegalArgumentException} is thrown if
+ * val
contains a start delimeter "${" which is not
+ * balanced by a stop delimeter "}".
+ *
+ * Author Avy Sharell
+ *
+ * @param val The string on which variable substitution is performed.
+ * @param props The properties to use for the substitution.
+ * @return The substituted string.
+ * @throws IllegalArgumentException if val
is malformed.
+ */
+ public static String substVars(final String val, final Properties props) throws IllegalArgumentException {
+ return substVars(val, props, new ArrayList<>());
+ }
+
+ private static String substVars(final String val, final Properties props, final List keys)
+ throws IllegalArgumentException {
+ if (val == null) {
+ return null;
+ }
+ final StringBuilder sbuf = new StringBuilder();
+
+ int i = 0;
+ int j;
+ int k;
+
+ while (true) {
+ j = val.indexOf(DELIM_START, i);
+ if (j == -1) {
+ // no more variables
+ if (i == 0) { // this is a simple string
+ return val;
+ }
+ // add the tail string which contails no variables and return the result.
+ sbuf.append(val.substring(i));
+ return sbuf.toString();
+ }
+ sbuf.append(val.substring(i, j));
+ k = val.indexOf(DELIM_STOP, j);
+ if (k == -1) {
+ throw new IllegalArgumentException(
+ Strings.dquote(val) + " has no closing brace. Opening brace at position " + j + '.');
+ }
+ j += DELIM_START_LEN;
+ final String key = val.substring(j, k);
+ // first try in System properties
+ String replacement = PropertiesUtil.getProperties().getStringProperty(key, null);
+ // then try props parameter
+ if (replacement == null && props != null) {
+ replacement = props.getProperty(key);
+ }
+
+ if (replacement != null) {
+
+ // Do variable substitution on the replacement string
+ // such that we can solve "Hello ${x2}" as "Hello p1"
+ // the where the properties are
+ // x1=p1
+ // x2=${x1}
+ if (!keys.contains(key)) {
+ final List usedKeys = new ArrayList<>(keys);
+ usedKeys.add(key);
+ final String recursiveReplacement = substVars(replacement, props, usedKeys);
+ sbuf.append(recursiveReplacement);
+ } else {
+ sbuf.append(replacement);
+ }
+ }
+ i = k + DELIM_STOP_LEN;
+ }
+ }
+
+ /**
+ * If value
is "true", then true
is
+ * returned. If value
is "false", then
+ * true
is returned. Otherwise, default
is
+ * returned.
+ *
+ * Case of value is unimportant.
+ * @param value The value to convert.
+ * @param dEfault The default value.
+ * @return the value of the result.
+ */
+ public static boolean toBoolean(final String value, final boolean dEfault) {
+ if (value == null) {
+ return dEfault;
+ }
+ final String trimmedVal = value.trim();
+ if ("true".equalsIgnoreCase(trimmedVal)) {
+ return true;
+ }
+ if ("false".equalsIgnoreCase(trimmedVal)) {
+ return false;
+ }
+ return dEfault;
+ }
+
+ public static long toFileSize(final String value, final long defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+
+ String s = toRootUpperCase(value.trim());
+ long multiplier = 1;
+ int index;
+
+ if ((index = s.indexOf("KB")) != -1) {
+ multiplier = 1024;
+ s = s.substring(0, index);
+ } else if ((index = s.indexOf("MB")) != -1) {
+ multiplier = 1024 * 1024;
+ s = s.substring(0, index);
+ } else if ((index = s.indexOf("GB")) != -1) {
+ multiplier = 1024 * 1024 * 1024;
+ s = s.substring(0, index);
+ }
+ if (s != null) {
+ try {
+ return Long.valueOf(s).longValue() * multiplier;
+ } catch (final NumberFormatException e) {
+ LogLog.error("[" + s + "] is not in proper int form.");
+ LogLog.error("[" + value + "] not in expected format.", e);
+ }
+ }
+ return defaultValue;
+ }
+
+ public static int toInt(final String value, final int dEfault) {
+ if (value != null) {
+ final String s = value.trim();
+ try {
+ return Integer.valueOf(s).intValue();
+ } catch (final NumberFormatException e) {
+ LogLog.error("[" + s + "] is not in proper int form.");
+ e.printStackTrace();
+ }
+ }
+ return dEfault;
+ }
+
+ /**
+ * Converts a standard or custom priority level to a Level object.
+ *
+ * If value
is of form "level#classname", then the specified class'
+ * toLevel method is called to process the specified level string; if no '#'
+ * character is present, then the default {@link org.apache.log4j.Level} class
+ * is used to process the level value.
+ *
+ *
+ *
+ * As a special case, if the value
parameter is equal to the string
+ * "NULL", then the value null
will be returned.
+ *
+ *
+ *
+ * As a Log4j 2.x extension, a {@code value}
+ * "level#org.apache.logging.log4j.Level" retrieves the corresponding custom
+ * Log4j 2.x level.
+ *
+ *
+ *
+ * If any error occurs while converting the value to a level, the
+ * defaultValue
parameter, which may be null
, is
+ * returned.
+ *
+ *
+ *
+ * Case of value
is insignificant for the level, but is
+ * significant for the class name part, if present.
+ *
+ *
+ * @param value The value to convert.
+ * @param defaultValue The default value.
+ * @return the value of the result.
+ *
+ * @since 1.1
+ */
+ public static Level toLevel(String value, final Level defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+
+ value = value.trim();
+ final Level cached = LEVELS.get(value);
+ if (cached != null) {
+ return cached;
+ }
+
+ final int hashIndex = value.indexOf('#');
+ if (hashIndex == -1) {
+ if ("NULL".equalsIgnoreCase(value)) {
+ return null;
+ }
+ // no class name specified : use standard Level class
+ final Level standardLevel = Level.toLevel(value, defaultValue);
+ if (standardLevel != null && value.equals(standardLevel.toString())) {
+ LEVELS.putIfAbsent(value, standardLevel);
+ }
+ return standardLevel;
+ }
+
+ final String clazz = value.substring(hashIndex + 1);
+ final String levelName = value.substring(0, hashIndex);
+
+ final Level customLevel = toLevel(clazz, levelName, defaultValue);
+ if (customLevel != null
+ && levelName.equals(customLevel.toString())
+ && clazz.equals(customLevel.getClass().getName())) {
+ LEVELS.putIfAbsent(value, customLevel);
+ }
+ return customLevel;
+ }
+
+ /**
+ * Converts a custom priority level to a Level object.
+ *
+ *
+ * If {@code clazz} has the special value "org.apache.logging.log4j.Level" a
+ * wrapper of the corresponding Log4j 2.x custom level object is returned.
+ *
+ *
+ * @param clazz a custom level class,
+ * @param levelName the name of the level,
+ * @param defaultValue the value to return in case an error occurs,
+ * @return the value of the result.
+ */
+ public static Level toLevel(final String clazz, final String levelName, final Level defaultValue) {
+
+ // This is degenerate case but you never know.
+ if ("NULL".equalsIgnoreCase(levelName)) {
+ return null;
+ }
+
+ LOGGER.debug("toLevel:class=[{}]:pri=[{}]", clazz, levelName);
+
+ // Support for levels defined in Log4j2.
+ if (LOG4J2_LEVEL_CLASS.equals(clazz)) {
+ final org.apache.logging.log4j.Level v2Level =
+ org.apache.logging.log4j.Level.getLevel(toRootUpperCase(levelName));
+ if (v2Level != null) {
+ switch (v2Level.name()) {
+ case "ALL":
+ return Level.ALL;
+ case "DEBUG":
+ return Level.DEBUG;
+ case "ERROR":
+ return Level.ERROR;
+ case "FATAL":
+ return Level.FATAL;
+ case "INFO":
+ return Level.INFO;
+ case "OFF":
+ return Level.OFF;
+ case "WARN":
+ return Level.WARN;
+ case "TRACE":
+ return Level.TRACE;
+ default:
+ return new LevelWrapper(v2Level);
+ }
+ } else {
+ return defaultValue;
+ }
+ }
+ try {
+ final Class> customLevel = LoaderUtil.loadClass(clazz);
+
+ // get a ref to the specified class' static method
+ // toLevel(String, org.apache.log4j.Level)
+ final Class>[] paramTypes = new Class[] {String.class, org.apache.log4j.Level.class};
+ final java.lang.reflect.Method toLevelMethod = customLevel.getMethod("toLevel", paramTypes);
+
+ // now call the toLevel method, passing level string + default
+ final Object[] params = new Object[] {levelName, defaultValue};
+ final Object o = toLevelMethod.invoke(null, params);
+
+ return (Level) o;
+ } catch (final ClassNotFoundException e) {
+ LOGGER.warn("custom level class [" + clazz + "] not found.");
+ } catch (final NoSuchMethodException e) {
+ LOGGER.warn(
+ "custom level class [" + clazz + "]" + " does not have a class function toLevel(String, Level)", e);
+ } catch (final java.lang.reflect.InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.warn("custom level class [" + clazz + "]" + " could not be instantiated", e);
+ } catch (final ClassCastException e) {
+ LOGGER.warn("class [" + clazz + "] is not a subclass of org.apache.log4j.Level", e);
+ } catch (final IllegalAccessException e) {
+ LOGGER.warn("class [" + clazz + "] cannot be instantiated due to access restrictions", e);
+ } catch (final RuntimeException e) {
+ LOGGER.warn("class [" + clazz + "], level [" + levelName + "] conversion failed.", e);
+ }
+ return defaultValue;
+ }
+
+ /**
+ * OptionConverter is a static class.
+ */
+ private OptionConverter() {}
+
+ private static class LevelWrapper extends Level {
+
+ private static final long serialVersionUID = -7693936267612508528L;
+
+ protected LevelWrapper(final org.apache.logging.log4j.Level v2Level) {
+ super(toLog4j1Level(v2Level.intLevel()), v2Level.name(), toSyslogLevel(v2Level.intLevel()), v2Level);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
new file mode 100644
index 00000000000..be5be195d12
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternConverter.java
@@ -0,0 +1,105 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ *
+ * PatternConverter is an abtract class that provides the
+ * formatting functionality that derived classes need.
+ *
+ *
Conversion specifiers in a conversion patterns are parsed to
+ * individual PatternConverters. Each of which is responsible for
+ * converting a logging event in a converter specific manner.
+ *
+ * @author James P. Cakalic
+ * @author Ceki Gülcü
+ *
+ * @since 0.8.2
+ */
+public abstract class PatternConverter {
+ public PatternConverter next;
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ protected PatternConverter() {}
+
+ protected PatternConverter(final FormattingInfo fi) {
+ min = fi.min;
+ max = fi.max;
+ leftAlign = fi.leftAlign;
+ }
+
+ /**
+ * Derived pattern converters must override this method in order to
+ * convert conversion specifiers in the correct way.
+ */
+ protected abstract String convert(LoggingEvent event);
+
+ /**
+ * A template method for formatting in a converter specific way.
+ */
+ public void format(final StringBuffer sbuf, final LoggingEvent e) {
+ final String s = convert(e);
+
+ if (s == null) {
+ if (0 < min) spacePad(sbuf, min);
+ return;
+ }
+
+ final int len = s.length();
+
+ if (len > max) sbuf.append(s.substring(len - max));
+ else if (len < min) {
+ if (leftAlign) {
+ sbuf.append(s);
+ spacePad(sbuf, min - len);
+ } else {
+ spacePad(sbuf, min - len);
+ sbuf.append(s);
+ }
+ } else sbuf.append(s);
+ }
+
+ static String[] SPACES = {
+ " ",
+ " ",
+ " ",
+ " ", // 1,2,4,8 spaces
+ " ", // 16 spaces
+ " "
+ }; // 32 spaces
+
+ /**
+ * Fast space padding method.
+ */
+ public void spacePad(final StringBuffer sbuf, final int length) {
+ int l = length;
+ while (l >= 32) {
+ sbuf.append(SPACES[5]);
+ l -= 32;
+ }
+
+ for (int i = 4; i >= 0; i--) {
+ if ((l & (1 << i)) != 0) {
+ sbuf.append(SPACES[i]);
+ }
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java
new file mode 100644
index 00000000000..36c1c79dcbf
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/PatternParser.java
@@ -0,0 +1,517 @@
+/*
+ * 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.log4j.helpers;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+// Contributors: Nelson Minar <(nelson@monkey.org>
+// Igor E. Poteryaev
+// Reinhard Deschler
+
+/**
+ * Most of the work of the {@link org.apache.log4j.PatternLayout} class is delegated to the PatternParser class.
+ *
+ *
+ * It is this class that parses conversion patterns and creates a chained list of {@link OptionConverter
+ * OptionConverters}.
+ *
+ * @author James P. Cakalic
+ * @author Ceki Gülcü
+ * @author Anders Kristensen
+ *
+ * @since 0.8.2
+ */
+public class PatternParser {
+
+ private static final char ESCAPE_CHAR = '%';
+
+ private static final int LITERAL_STATE = 0;
+ private static final int CONVERTER_STATE = 1;
+ private static final int DOT_STATE = 3;
+ private static final int MIN_STATE = 4;
+ private static final int MAX_STATE = 5;
+
+ static final int FULL_LOCATION_CONVERTER = 1000;
+ static final int METHOD_LOCATION_CONVERTER = 1001;
+ static final int CLASS_LOCATION_CONVERTER = 1002;
+ static final int LINE_LOCATION_CONVERTER = 1003;
+ static final int FILE_LOCATION_CONVERTER = 1004;
+
+ static final int RELATIVE_TIME_CONVERTER = 2000;
+ static final int THREAD_CONVERTER = 2001;
+ static final int LEVEL_CONVERTER = 2002;
+ static final int NDC_CONVERTER = 2003;
+ static final int MESSAGE_CONVERTER = 2004;
+
+ int state;
+ protected StringBuffer currentLiteral = new StringBuffer(32);
+ protected int patternLength;
+ protected int i;
+ PatternConverter head;
+ PatternConverter tail;
+ protected FormattingInfo formattingInfo = new FormattingInfo();
+ protected String pattern;
+
+ public PatternParser(final String pattern) {
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ state = LITERAL_STATE;
+ }
+
+ private void addToList(PatternConverter pc) {
+ if (head == null) {
+ head = tail = pc;
+ } else {
+ tail.next = pc;
+ tail = pc;
+ }
+ }
+
+ protected String extractOption() {
+ if ((i < patternLength) && (pattern.charAt(i) == '{')) {
+ final int end = pattern.indexOf('}', i);
+ if (end > i) {
+ final String r = pattern.substring(i + 1, end);
+ i = end + 1;
+ return r;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The option is expected to be in decimal and positive. In case of error, zero is returned.
+ */
+ protected int extractPrecisionOption() {
+ final String opt = extractOption();
+ int r = 0;
+ if (opt != null) {
+ try {
+ r = Integer.parseInt(opt);
+ if (r <= 0) {
+ LogLog.error("Precision option (" + opt + ") isn't a positive integer.");
+ r = 0;
+ }
+ } catch (NumberFormatException e) {
+ LogLog.error("Category option \"" + opt + "\" not a decimal integer.", e);
+ }
+ }
+ return r;
+ }
+
+ public PatternConverter parse() {
+ char c;
+ i = 0;
+ while (i < patternLength) {
+ c = pattern.charAt(i++);
+ switch (state) {
+ case LITERAL_STATE:
+ // In literal state, the last char is always a literal.
+ if (i == patternLength) {
+ currentLiteral.append(c);
+ continue;
+ }
+ if (c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch (pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+ break;
+ case 'n':
+ currentLiteral.append(Layout.LINE_SEP);
+ i++; // move pointer
+ break;
+ default:
+ if (currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ // LogLog.debug("Parsed LITERAL converter: \""
+ // +currentLiteral+"\".");
+ }
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo.reset();
+ }
+ } else {
+ currentLiteral.append(c);
+ }
+ break;
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+ switch (c) {
+ case '-':
+ formattingInfo.leftAlign = true;
+ break;
+ case '.':
+ state = DOT_STATE;
+ break;
+ default:
+ if (c >= '0' && c <= '9') {
+ formattingInfo.min = c - '0';
+ state = MIN_STATE;
+ } else finalizeConverter(c);
+ } // switch
+ break;
+ case MIN_STATE:
+ currentLiteral.append(c);
+ if (c >= '0' && c <= '9') formattingInfo.min = formattingInfo.min * 10 + (c - '0');
+ else if (c == '.') state = DOT_STATE;
+ else {
+ finalizeConverter(c);
+ }
+ break;
+ case DOT_STATE:
+ currentLiteral.append(c);
+ if (c >= '0' && c <= '9') {
+ formattingInfo.max = c - '0';
+ state = MAX_STATE;
+ } else {
+ LogLog.error("Error occured in position " + i + ".\n Was expecting digit, instead got char \""
+ + c + "\".");
+ state = LITERAL_STATE;
+ }
+ break;
+ case MAX_STATE:
+ currentLiteral.append(c);
+ if (c >= '0' && c <= '9') formattingInfo.max = formattingInfo.max * 10 + (c - '0');
+ else {
+ finalizeConverter(c);
+ state = LITERAL_STATE;
+ }
+ break;
+ } // switch
+ } // while
+ if (currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ // LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+ }
+ return head;
+ }
+
+ protected void finalizeConverter(char c) {
+ PatternConverter pc = null;
+ switch (c) {
+ case 'c':
+ pc = new CategoryPatternConverter(formattingInfo, extractPrecisionOption());
+ // LogLog.debug("CATEGORY converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'C':
+ pc = new ClassNamePatternConverter(formattingInfo, extractPrecisionOption());
+ // LogLog.debug("CLASS_NAME converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'd':
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+ DateFormat df;
+ final String dOpt = extractOption();
+ if (dOpt != null) dateFormatStr = dOpt;
+
+ if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+ df = new ISO8601DateFormat();
+ else if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+ df = new AbsoluteTimeDateFormat();
+ else if (dateFormatStr.equalsIgnoreCase(AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+ df = new DateTimeDateFormat();
+ else {
+ try {
+ df = new SimpleDateFormat(dateFormatStr);
+ } catch (IllegalArgumentException e) {
+ LogLog.error("Could not instantiate SimpleDateFormat with " + dateFormatStr, e);
+ df = (DateFormat) OptionConverter.instantiateByClassName(
+ "org.apache.log4j.helpers.ISO8601DateFormat", DateFormat.class, null);
+ }
+ }
+ pc = new DatePatternConverter(formattingInfo, df);
+ // LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'F':
+ pc = new LocationPatternConverter(formattingInfo, FILE_LOCATION_CONVERTER);
+ // LogLog.debug("File name converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'l':
+ pc = new LocationPatternConverter(formattingInfo, FULL_LOCATION_CONVERTER);
+ // LogLog.debug("Location converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'L':
+ pc = new LocationPatternConverter(formattingInfo, LINE_LOCATION_CONVERTER);
+ // LogLog.debug("LINE NUMBER converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'm':
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+ // LogLog.debug("MESSAGE converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'M':
+ pc = new LocationPatternConverter(formattingInfo, METHOD_LOCATION_CONVERTER);
+ // LogLog.debug("METHOD converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'p':
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+ // LogLog.debug("LEVEL converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'r':
+ pc = new BasicPatternConverter(formattingInfo, RELATIVE_TIME_CONVERTER);
+ // LogLog.debug("RELATIVE time converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 't':
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+ // LogLog.debug("THREAD converter.");
+ // formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*
+ * case 'u': if(i < patternLength) { char cNext = pattern.charAt(i); if(cNext >= '0' && cNext <= '9') { pc = new
+ * UserFieldPatternConverter(formattingInfo, cNext - '0'); LogLog.debug("USER converter ["+cNext+"].");
+ * formattingInfo.dump(); currentLiteral.setLength(0); i++; } else LogLog.error("Unexpected char"
+ * +cNext+" at position "+i); } break;
+ */
+ case 'x':
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+ // LogLog.debug("NDC converter.");
+ currentLiteral.setLength(0);
+ break;
+ case 'X':
+ final String xOpt = extractOption();
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
+ currentLiteral.setLength(0);
+ break;
+ default:
+ LogLog.error("Unexpected char [" + c + "] at position " + i + " in conversion pattern.");
+ pc = new LiteralPatternConverter(currentLiteral.toString());
+ currentLiteral.setLength(0);
+ }
+
+ addConverter(pc);
+ }
+
+ protected void addConverter(PatternConverter pc) {
+ currentLiteral.setLength(0);
+ // Add the pattern converter to the list.
+ addToList(pc);
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ // Reset formatting info
+ formattingInfo.reset();
+ }
+
+ // ---------------------------------------------------------------------
+ // PatternConverters
+ // ---------------------------------------------------------------------
+
+ private static class BasicPatternConverter extends PatternConverter {
+ int type;
+
+ BasicPatternConverter(final FormattingInfo formattingInfo, final int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public String convert(final LoggingEvent event) {
+ switch (type) {
+ case RELATIVE_TIME_CONVERTER:
+ return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
+ case THREAD_CONVERTER:
+ return event.getThreadName();
+ case LEVEL_CONVERTER:
+ return event.getLevel().toString();
+ case NDC_CONVERTER:
+ return event.getNDC();
+ case MESSAGE_CONVERTER: {
+ return event.getRenderedMessage();
+ }
+ default:
+ return null;
+ }
+ }
+ }
+
+ private static class LiteralPatternConverter extends PatternConverter {
+ private String literal;
+
+ LiteralPatternConverter(final String value) {
+ literal = value;
+ }
+
+ public final void format(final StringBuffer sbuf, final LoggingEvent event) {
+ sbuf.append(literal);
+ }
+
+ public String convert(final LoggingEvent event) {
+ return literal;
+ }
+ }
+
+ private static class DatePatternConverter extends PatternConverter {
+ private DateFormat df;
+ private Date date;
+
+ DatePatternConverter(final FormattingInfo formattingInfo, final DateFormat df) {
+ super(formattingInfo);
+ date = new Date();
+ this.df = df;
+ }
+
+ public String convert(final LoggingEvent event) {
+ date.setTime(event.timeStamp);
+ String converted = null;
+ try {
+ converted = df.format(date);
+ } catch (Exception ex) {
+ LogLog.error("Error occured while converting date.", ex);
+ }
+ return converted;
+ }
+ }
+
+ private static class MDCPatternConverter extends PatternConverter {
+ private String key;
+
+ MDCPatternConverter(final FormattingInfo formattingInfo, final String key) {
+ super(formattingInfo);
+ this.key = key;
+ }
+
+ public String convert(final LoggingEvent event) {
+ if (key == null) {
+ final StringBuffer buf = new StringBuffer("{");
+ final Map properties = event.getProperties();
+ if (properties.size() > 0) {
+ final Object[] keys = properties.keySet().toArray();
+ Arrays.sort(keys);
+ for (int i = 0; i < keys.length; i++) {
+ buf.append('{');
+ buf.append(keys[i]);
+ buf.append(',');
+ buf.append(properties.get(keys[i]));
+ buf.append('}');
+ }
+ }
+ buf.append('}');
+ return buf.toString();
+ } else {
+ final Object val = event.getMDC(key);
+ if (val == null) {
+ return null;
+ } else {
+ return val.toString();
+ }
+ }
+ }
+ }
+
+ private class LocationPatternConverter extends PatternConverter {
+ int type;
+
+ LocationPatternConverter(final FormattingInfo formattingInfo, final int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public String convert(final LoggingEvent event) {
+ final LocationInfo locationInfo = event.getLocationInformation();
+ switch (type) {
+ case FULL_LOCATION_CONVERTER:
+ return locationInfo.fullInfo;
+ case METHOD_LOCATION_CONVERTER:
+ return locationInfo.getMethodName();
+ case LINE_LOCATION_CONVERTER:
+ return locationInfo.getLineNumber();
+ case FILE_LOCATION_CONVERTER:
+ return locationInfo.getFileName();
+ default:
+ return null;
+ }
+ }
+ }
+
+ private abstract static class NamedPatternConverter extends PatternConverter {
+ int precision;
+
+ NamedPatternConverter(final FormattingInfo formattingInfo, final int precision) {
+ super(formattingInfo);
+ this.precision = precision;
+ }
+
+ abstract String getFullyQualifiedName(LoggingEvent event);
+
+ public String convert(final LoggingEvent event) {
+ final String n = getFullyQualifiedName(event);
+ if (precision <= 0) return n;
+ else {
+ final int len = n.length();
+
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = len - 1;
+ for (int i = precision; i > 0; i--) {
+ end = n.lastIndexOf('.', end - 1);
+ if (end == -1) return n;
+ }
+ return n.substring(end + 1, len);
+ }
+ }
+ }
+
+ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+ ClassNamePatternConverter(final FormattingInfo formattingInfo, final int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(final LoggingEvent event) {
+ return event.getLocationInformation().getClassName();
+ }
+ }
+
+ private class CategoryPatternConverter extends NamedPatternConverter {
+
+ CategoryPatternConverter(final FormattingInfo formattingInfo, final int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(final LoggingEvent event) {
+ return event.getLoggerName();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java
new file mode 100644
index 00000000000..f9dc6fb7871
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/QuietWriter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.helpers;
+
+import java.io.FilterWriter;
+import java.io.Writer;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.ErrorHandler;
+
+/**
+ * QuietWriter does not throw exceptions when things go
+ * wrong. Instead, it delegates error handling to its {@link ErrorHandler}.
+ */
+public class QuietWriter extends FilterWriter {
+
+ protected ErrorHandler errorHandler;
+
+ public QuietWriter(final Writer writer, final ErrorHandler errorHandler) {
+ super(writer);
+ setErrorHandler(errorHandler);
+ }
+
+ @Override
+ public void write(final String string) {
+ if (string != null) {
+ try {
+ out.write(string);
+ } catch (Exception e) {
+ errorHandler.error("Failed to write [" + string + "].", e, ErrorCode.WRITE_FAILURE);
+ }
+ }
+ }
+
+ @Override
+ public void flush() {
+ try {
+ out.flush();
+ } catch (Exception e) {
+ errorHandler.error("Failed to flush writer,", e, ErrorCode.FLUSH_FAILURE);
+ }
+ }
+
+ public void setErrorHandler(final ErrorHandler eh) {
+ if (eh == null) {
+ // This is a programming error on the part of the enclosing appender.
+ throw new IllegalArgumentException("Attempted to set null ErrorHandler.");
+ }
+ this.errorHandler = eh;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
new file mode 100644
index 00000000000..498e014a2a4
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
@@ -0,0 +1,58 @@
+/*
+ * 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.log4j.helpers;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Date;
+
+/**
+ * Formats a {@link Date} by printing the number of milliseconds elapsed since construction of the format. This is the
+ * fastest printing DateFormat in the package.
+ *
+ * @since 0.7.5
+ */
+public class RelativeTimeDateFormat extends DateFormat {
+
+ private static final long serialVersionUID = 7055751607085611984L;
+
+ protected final long startTime;
+
+ public RelativeTimeDateFormat() {
+ this.startTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Appends to sbuf
the number of milliseconds elapsed since the start of the application.
+ *
+ * @since 0.7.5
+ */
+ @Override
+ public StringBuffer format(final Date date, final StringBuffer sbuf, final FieldPosition fieldPosition) {
+ // System.err.println(":"+ date.getTime() + " - " + startTime);
+ return sbuf.append((date.getTime() - startTime));
+ }
+
+ /**
+ * Always returns null.
+ */
+ @Override
+ public Date parse(final String s, final ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
new file mode 100644
index 00000000000..f99ad0b8c7c
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/UtilLoggingLevel.java
@@ -0,0 +1,239 @@
+/*
+ * 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.log4j.helpers;
+
+import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.log4j.Level;
+
+/**
+ * An extension of the Level class that provides support for java.util.logging Levels.
+ */
+public class UtilLoggingLevel extends Level {
+
+ /**
+ * Serialization version id.
+ */
+ private static final long serialVersionUID = 909301162611820211L;
+
+ /**
+ * Numerical value for SEVERE.
+ */
+ public static final int SEVERE_INT = 22000;
+ /**
+ * Numerical value for WARNING.
+ */
+ public static final int WARNING_INT = 21000;
+
+ // INFO level defined in parent as 20000..no need to redefine here
+
+ /**
+ * Numerical value for CONFIG.
+ */
+ public static final int CONFIG_INT = 14000;
+
+ /**
+ * Numerical value for FINE.
+ */
+ public static final int FINE_INT = 13000;
+
+ /**
+ * Numerical value for FINER.
+ */
+ public static final int FINER_INT = 12000;
+
+ /**
+ * Numerical value for FINEST.
+ */
+ public static final int FINEST_INT = 11000;
+
+ /**
+ * Numerical value for UNKNOWN.
+ */
+ public static final int UNKNOWN_INT = 10000;
+
+ /**
+ * SEVERE.
+ */
+ public static final UtilLoggingLevel SEVERE = new UtilLoggingLevel(SEVERE_INT, "SEVERE", 0);
+
+ /**
+ * WARNING.
+ */
+ public static final UtilLoggingLevel WARNING = new UtilLoggingLevel(WARNING_INT, "WARNING", 4);
+
+ /**
+ * INFO.
+ */
+ // note: we've aligned the int values of the java.util.logging INFO level with log4j's level
+ public static final UtilLoggingLevel INFO = new UtilLoggingLevel(INFO_INT, "INFO", 5);
+
+ /**
+ * CONFIG.
+ */
+ public static final UtilLoggingLevel CONFIG = new UtilLoggingLevel(CONFIG_INT, "CONFIG", 6);
+
+ /**
+ * FINE.
+ */
+ public static final UtilLoggingLevel FINE = new UtilLoggingLevel(FINE_INT, "FINE", 7);
+
+ /**
+ * FINER.
+ */
+ public static final UtilLoggingLevel FINER = new UtilLoggingLevel(FINER_INT, "FINER", 8);
+
+ /**
+ * FINEST.
+ */
+ public static final UtilLoggingLevel FINEST = new UtilLoggingLevel(FINEST_INT, "FINEST", 9);
+
+ /**
+ * Create new instance.
+ *
+ * @param level numeric value for level.
+ * @param levelStr symbolic name for level.
+ * @param syslogEquivalent Equivalent syslog severity.
+ */
+ protected UtilLoggingLevel(final int level, final String levelStr, final int syslogEquivalent) {
+ super(level, levelStr, syslogEquivalent);
+ }
+
+ /**
+ * Convert an integer passed as argument to a level. If the conversion fails, then this method returns the specified
+ * default.
+ *
+ * @param val numeric value.
+ * @param defaultLevel level to be returned if no level matches numeric value.
+ * @return matching level or default level.
+ */
+ public static UtilLoggingLevel toLevel(final int val, final UtilLoggingLevel defaultLevel) {
+ switch (val) {
+ case SEVERE_INT:
+ return SEVERE;
+
+ case WARNING_INT:
+ return WARNING;
+
+ case INFO_INT:
+ return INFO;
+
+ case CONFIG_INT:
+ return CONFIG;
+
+ case FINE_INT:
+ return FINE;
+
+ case FINER_INT:
+ return FINER;
+
+ case FINEST_INT:
+ return FINEST;
+
+ default:
+ return defaultLevel;
+ }
+ }
+
+ /**
+ * Gets level matching numeric value.
+ *
+ * @param val numeric value.
+ * @return matching level or UtilLoggerLevel.FINEST if no match.
+ */
+ @SuppressFBWarnings(value = "HSM_HIDING_METHOD", justification = "Legacy code")
+ public static Level toLevel(final int val) {
+ return toLevel(val, FINEST);
+ }
+
+ /**
+ * Gets list of supported levels.
+ *
+ * @return list of supported levels.
+ */
+ public static List getAllPossibleLevels() {
+ final ArrayList list = new ArrayList<>();
+ list.add(FINE);
+ list.add(FINER);
+ list.add(FINEST);
+ list.add(INFO);
+ list.add(CONFIG);
+ list.add(WARNING);
+ list.add(SEVERE);
+ return list;
+ }
+
+ /**
+ * Get level with specified symbolic name.
+ *
+ * @param s symbolic name.
+ * @return matching level or Level.DEBUG if no match.
+ */
+ @SuppressFBWarnings(value = "HSM_HIDING_METHOD", justification = "Legacy code")
+ public static Level toLevel(final String s) {
+ return toLevel(s, Level.DEBUG);
+ }
+
+ /**
+ * Get level with specified symbolic name.
+ *
+ * @param sArg symbolic name.
+ * @param defaultLevel level to return if no match.
+ * @return matching level or defaultLevel if no match.
+ */
+ @SuppressFBWarnings(value = "HSM_HIDING_METHOD", justification = "Legacy code")
+ public static Level toLevel(final String sArg, final Level defaultLevel) {
+ if (sArg == null) {
+ return defaultLevel;
+ }
+
+ final String s = toRootUpperCase(sArg);
+
+ if (s.equals("SEVERE")) {
+ return SEVERE;
+ }
+
+ // if(s.equals("FINE")) return Level.FINE;
+ if (s.equals("WARNING")) {
+ return WARNING;
+ }
+
+ if (s.equals("INFO")) {
+ return INFO;
+ }
+
+ if (s.equals("CONFIG")) {
+ return CONFIG;
+ }
+
+ if (s.equals("FINE")) {
+ return FINE;
+ }
+
+ if (s.equals("FINER")) {
+ return FINER;
+ }
+
+ if (s.equals("FINEST")) {
+ return FINEST;
+ }
+ return defaultLevel;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/package-info.java
index 00d0e1293ff..b94af3ba614 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/package-info.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/package-info.java
@@ -17,4 +17,9 @@
/**
* Log4j 1.x compatibility layer.
*/
+@Export
+@Version("2.20.3")
package org.apache.log4j.helpers;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java
new file mode 100644
index 00000000000..ee9c1805a3f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java
@@ -0,0 +1,177 @@
+/*
+ * 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.log4j.jmx;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+
+public abstract class AbstractDynamicMBean implements DynamicMBean, MBeanRegistration {
+
+ /**
+ * Get MBean name.
+ *
+ * @param appender appender, may not be null.
+ * @return name.
+ * @since 1.2.16
+ */
+ protected static String getAppenderName(final Appender appender) {
+ String name = appender.getName();
+ if (name == null || name.trim().length() == 0) {
+ // try to get some form of a name, because null is not allowed (exception), and empty string certainly isn't
+ // useful in
+ // JMX..
+ name = appender.toString();
+ }
+ return name;
+ }
+
+ String dClassName;
+ MBeanServer server;
+
+ private final Vector mbeanList = new Vector();
+
+ /**
+ * Enables the to get the values of several attributes of the Dynamic MBean.
+ */
+ @Override
+ public AttributeList getAttributes(final String[] attributeNames) {
+
+ // Check attributeNames is not null to avoid NullPointerException later on
+ if (attributeNames == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("attributeNames[] cannot be null"),
+ "Cannot invoke a getter of " + dClassName);
+ }
+
+ final AttributeList resultList = new AttributeList();
+
+ // if attributeNames is empty, return an empty result list
+ if (attributeNames.length == 0) {
+ return resultList;
+ }
+
+ // build the result attribute list
+ for (final String attributeName : attributeNames) {
+ try {
+ final Object value = getAttribute((String) attributeName);
+ resultList.add(new Attribute(attributeName, value));
+ } catch (final JMException e) {
+ e.printStackTrace();
+ } catch (final RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ return (resultList);
+ }
+
+ protected abstract Logger getLogger();
+
+ @Override
+ public void postDeregister() {
+ getLogger().debug("postDeregister is called.");
+ }
+
+ @Override
+ public void postRegister(final java.lang.Boolean registrationDone) {}
+
+ /**
+ * Performs cleanup for deregistering this MBean. Default implementation unregisters MBean instances which are
+ * registered using {@link #registerMBean(Object mbean, ObjectName objectName)}.
+ */
+ @Override
+ public void preDeregister() {
+ getLogger().debug("preDeregister called.");
+
+ final Enumeration iterator = mbeanList.elements();
+ while (iterator.hasMoreElements()) {
+ final ObjectName name = (ObjectName) iterator.nextElement();
+ try {
+ server.unregisterMBean(name);
+ } catch (final InstanceNotFoundException e) {
+ getLogger().warn("Missing MBean " + name.getCanonicalName());
+ } catch (final MBeanRegistrationException e) {
+ getLogger().warn("Failed unregistering " + name.getCanonicalName());
+ }
+ }
+ }
+
+ @Override
+ public ObjectName preRegister(final MBeanServer server, final ObjectName name) {
+ getLogger().debug("preRegister called. Server=" + server + ", name=" + name);
+ this.server = server;
+ return name;
+ }
+
+ /**
+ * Registers MBean instance in the attached server. Must NOT be called before registration of this instance.
+ */
+ protected void registerMBean(final Object mbean, final ObjectName objectName)
+ throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
+ server.registerMBean(mbean, objectName);
+ mbeanList.add(objectName);
+ }
+
+ /**
+ * Sets the values of several attributes of the Dynamic MBean, and returns the list of attributes that have been set.
+ */
+ @Override
+ public AttributeList setAttributes(final AttributeList attributes) {
+
+ // Check attributes is not null to avoid NullPointerException later on
+ if (attributes == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("AttributeList attributes cannot be null"),
+ "Cannot invoke a setter of " + dClassName);
+ }
+ final AttributeList resultList = new AttributeList();
+
+ // if attributeNames is empty, nothing more to do
+ if (attributes.isEmpty()) {
+ return resultList;
+ }
+
+ // for each attribute, try to set it and add to the result list if successfull
+ for (final Object attribute : attributes) {
+ final Attribute attr = (Attribute) attribute;
+ try {
+ setAttribute(attr);
+ final String name = attr.getName();
+ final Object value = getAttribute(name);
+ resultList.add(new Attribute(name, value));
+ } catch (final JMException e) {
+ e.printStackTrace();
+ } catch (final RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ return (resultList);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/Agent.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/Agent.java
new file mode 100644
index 00000000000..daf7add2b54
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/Agent.java
@@ -0,0 +1,124 @@
+/*
+ * 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.log4j.jmx;
+
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.util.LoaderUtil;
+
+/**
+ * Manages an instance of com.sun.jdmk.comm.HtmlAdapterServer which was provided for demonstration purposes in the Java
+ * Management Extensions Reference Implementation 1.2.1. This class is provided to maintain compatibility with earlier
+ * versions of log4j and use in new code is discouraged.
+ *
+ * @deprecated
+ */
+@Deprecated
+public class Agent {
+
+ /**
+ * Diagnostic logger.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ static Logger log = Logger.getLogger(Agent.class);
+
+ /**
+ * Creates a new instance of com.sun.jdmk.comm.HtmlAdapterServer using reflection.
+ *
+ * @since 1.2.16
+ * @return new instance.
+ */
+ private static Object createServer() {
+ Object newInstance = null;
+ try {
+ newInstance = LoaderUtil.newInstanceOf("com.sun.jdmk.comm.HtmlAdapterServer");
+ } catch (final ReflectiveOperationException ex) {
+ throw new RuntimeException(ex);
+ }
+ return newInstance;
+ }
+
+ /**
+ * Invokes HtmlAdapterServer.start() using reflection.
+ *
+ * @since 1.2.16
+ * @param server instance of com.sun.jdmk.comm.HtmlAdapterServer.
+ */
+ private static void startServer(final Object server) {
+ try {
+ server.getClass().getMethod("start", new Class[0]).invoke(server, new Object[0]);
+ } catch (final InvocationTargetException ex) {
+ final Throwable cause = ex.getTargetException();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause != null) {
+ if (cause instanceof InterruptedException || cause instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ throw new RuntimeException(cause.toString());
+ } else {
+ throw new RuntimeException();
+ }
+ } catch (final NoSuchMethodException ex) {
+ throw new RuntimeException(ex.toString());
+ } catch (final IllegalAccessException ex) {
+ throw new RuntimeException(ex.toString());
+ }
+ }
+
+ /**
+ * Create new instance.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public Agent() {}
+
+ /**
+ * Starts instance of HtmlAdapterServer.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public void start() {
+
+ final MBeanServer server = MBeanServerFactory.createMBeanServer();
+ final Object html = createServer();
+
+ try {
+ log.info("Registering HtmlAdaptorServer instance.");
+ server.registerMBean(html, new ObjectName("Adaptor:name=html,port=8082"));
+ log.info("Registering HierarchyDynamicMBean instance.");
+ final HierarchyDynamicMBean hdm = new HierarchyDynamicMBean();
+ server.registerMBean(hdm, new ObjectName("log4j:hiearchy=default"));
+ } catch (final JMException e) {
+ log.error("Problem while registering MBeans instances.", e);
+ return;
+ } catch (final RuntimeException e) {
+ log.error("Problem while registering MBeans instances.", e);
+ return;
+ }
+ startServer(html);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java
new file mode 100644
index 00000000000..2ee683240ad
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java
@@ -0,0 +1,296 @@
+/*
+ * 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.log4j.jmx;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.Vector;
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.OptionHandler;
+
+public class AppenderDynamicMBean extends AbstractDynamicMBean {
+
+ // This category instance is for logging.
+ private static final Logger cat = Logger.getLogger(AppenderDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+ private final Vector dAttributes = new Vector();
+
+ private final String dClassName = this.getClass().getName();
+ private final Hashtable dynamicProps = new Hashtable(5);
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[2];
+
+ private final String dDescription = "This MBean acts as a management facade for log4j appenders.";
+
+ // We wrap this appender instance.
+ private final Appender appender;
+
+ public AppenderDynamicMBean(final Appender appender) throws IntrospectionException {
+ this.appender = appender;
+ buildDynamicMBeanInfo();
+ }
+
+ private void buildDynamicMBeanInfo() throws IntrospectionException {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo(
+ "AppenderDynamicMBean(): Constructs a AppenderDynamicMBean instance", constructors[0]);
+
+ final BeanInfo bi = Introspector.getBeanInfo(appender.getClass());
+ final PropertyDescriptor[] pd = bi.getPropertyDescriptors();
+
+ final int size = pd.length;
+
+ for (int i = 0; i < size; i++) {
+ final String name = pd[i].getName();
+ final Method readMethod = pd[i].getReadMethod();
+ final Method writeMethod = pd[i].getWriteMethod();
+ if (readMethod != null) {
+ final Class returnClass = readMethod.getReturnType();
+ if (isSupportedType(returnClass)) {
+ String returnClassName;
+ if (returnClass.isAssignableFrom(Priority.class)) {
+ returnClassName = "java.lang.String";
+ } else {
+ returnClassName = returnClass.getName();
+ }
+
+ dAttributes.add(
+ new MBeanAttributeInfo(name, returnClassName, "Dynamic", true, writeMethod != null, false));
+ dynamicProps.put(name, new MethodUnion(readMethod, writeMethod));
+ }
+ }
+ }
+
+ MBeanParameterInfo[] params = new MBeanParameterInfo[0];
+
+ dOperations[0] = new MBeanOperationInfo(
+ "activateOptions", "activateOptions(): add an appender", params, "void", MBeanOperationInfo.ACTION);
+
+ params = new MBeanParameterInfo[1];
+ params[0] = new MBeanParameterInfo("layout class", "java.lang.String", "layout class");
+
+ dOperations[1] = new MBeanOperationInfo(
+ "setLayout", "setLayout(): add a layout", params, "void", MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName)
+ throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ cat.debug("getAttribute called with [" + attributeName + "].");
+ if (attributeName.startsWith("appender=" + appender.getName() + ",layout")) {
+ try {
+ return new ObjectName("log4j:" + attributeName);
+ } catch (final MalformedObjectNameException e) {
+ cat.error("attributeName", e);
+ } catch (final RuntimeException e) {
+ cat.error("attributeName", e);
+ }
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(attributeName);
+
+ // cat.debug("----name="+attributeName+", b="+b);
+
+ if (mu != null && mu.readMethod != null) {
+ try {
+ return mu.readMethod.invoke(appender, null);
+ } catch (final IllegalAccessException e) {
+ return null;
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ } catch (final RuntimeException e) {
+ return null;
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return cat;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ return new MBeanInfo(
+ dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[])
+ throws MBeanException, ReflectionException {
+
+ if (operationName.equals("activateOptions") && appender instanceof OptionHandler) {
+ final OptionHandler oh = (OptionHandler) appender;
+ oh.activateOptions();
+ return "Options activated.";
+ } else if (operationName.equals("setLayout")) {
+ final Layout layout =
+ (Layout) OptionConverter.instantiateByClassName((String) params[0], Layout.class, null);
+ appender.setLayout(layout);
+ registerLayoutMBean(layout);
+ }
+ return null;
+ }
+
+ private boolean isSupportedType(final Class clazz) {
+ if (clazz.isPrimitive() || (clazz == String.class) || clazz.isAssignableFrom(Priority.class)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public ObjectName preRegister(final MBeanServer server, final ObjectName name) {
+ cat.debug("preRegister called. Server=" + server + ", name=" + name);
+ this.server = server;
+ registerLayoutMBean(appender.getLayout());
+
+ return name;
+ }
+
+ void registerLayoutMBean(final Layout layout) {
+ if (layout == null) {
+ return;
+ }
+
+ final String name =
+ getAppenderName(appender) + ",layout=" + layout.getClass().getName();
+ cat.debug("Adding LayoutMBean:" + name);
+ ObjectName objectName = null;
+ try {
+ final LayoutDynamicMBean appenderMBean = new LayoutDynamicMBean(layout);
+ objectName = new ObjectName("log4j:appender=" + name);
+ if (!server.isRegistered(objectName)) {
+ registerMBean(appenderMBean, objectName);
+ dAttributes.add(new MBeanAttributeInfo(
+ "appender=" + name,
+ "javax.management.ObjectName",
+ "The " + name + " layout.",
+ true,
+ true,
+ false));
+ }
+
+ } catch (final JMException e) {
+ cat.error("Could not add DynamicLayoutMBean for [" + name + "].", e);
+ } catch (final java.beans.IntrospectionException e) {
+ cat.error("Could not add DynamicLayoutMBean for [" + name + "].", e);
+ } catch (final RuntimeException e) {
+ cat.error("Could not add DynamicLayoutMBean for [" + name + "].", e);
+ }
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute)
+ throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(name);
+
+ if (mu != null && mu.writeMethod != null) {
+ final Object[] o = new Object[1];
+
+ final Class[] params = mu.writeMethod.getParameterTypes();
+ if (params[0] == org.apache.log4j.Priority.class) {
+ value = OptionConverter.toLevel((String) value, (Level) getAttribute(name));
+ }
+ o[0] = value;
+
+ try {
+ mu.writeMethod.invoke(appender, o);
+
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ cat.error("FIXME", e);
+ } catch (final IllegalAccessException e) {
+ cat.error("FIXME", e);
+ } catch (final RuntimeException e) {
+ cat.error("FIXME", e);
+ }
+ } else if (name.endsWith(".layout")) {
+
+ } else {
+ throw (new AttributeNotFoundException(
+ "Attribute " + name + " not found in " + this.getClass().getName()));
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java
new file mode 100644
index 00000000000..7b3c20980fe
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java
@@ -0,0 +1,268 @@
+/*
+ * 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.log4j.jmx;
+
+import java.lang.reflect.Constructor;
+import java.util.Vector;
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.Notification;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationFilterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.LoggerRepository;
+
+public class HierarchyDynamicMBean extends AbstractDynamicMBean
+ implements HierarchyEventListener, NotificationBroadcaster {
+
+ static final String ADD_APPENDER = "addAppender.";
+ static final String THRESHOLD = "threshold";
+
+ private static final Logger log = Logger.getLogger(HierarchyDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+ private final Vector vAttributes = new Vector();
+ private final String dClassName = this.getClass().getName();
+
+ private final String dDescription = "This MBean acts as a management facade for org.apache.log4j.Hierarchy.";
+
+ private final NotificationBroadcasterSupport nbs = new NotificationBroadcasterSupport();
+
+ private final LoggerRepository hierarchy;
+
+ public HierarchyDynamicMBean() {
+ hierarchy = LogManager.getLoggerRepository();
+ buildDynamicMBeanInfo();
+ }
+
+ @Override
+ public void addAppenderEvent(final Category logger, final Appender appender) {
+ log.debug("addAppenderEvent called: logger=" + logger.getName() + ", appender=" + appender.getName());
+ final Notification n = new Notification(ADD_APPENDER + logger.getName(), this, 0);
+ n.setUserData(appender);
+ log.debug("sending notification.");
+ nbs.sendNotification(n);
+ }
+
+ ObjectName addLoggerMBean(final Logger logger) {
+ final String name = logger.getName();
+ ObjectName objectName = null;
+ try {
+ final LoggerDynamicMBean loggerMBean = new LoggerDynamicMBean(logger);
+ objectName = new ObjectName("log4j", "logger", name);
+
+ if (!server.isRegistered(objectName)) {
+ registerMBean(loggerMBean, objectName);
+ final NotificationFilterSupport nfs = new NotificationFilterSupport();
+ nfs.enableType(ADD_APPENDER + logger.getName());
+ log.debug("---Adding logger [" + name + "] as listener.");
+ nbs.addNotificationListener(loggerMBean, nfs, null);
+ vAttributes.add(new MBeanAttributeInfo(
+ "logger=" + name,
+ "javax.management.ObjectName",
+ "The " + name + " logger.",
+ true,
+ true, // this makes
+ // the object
+ // clickable
+ false));
+ }
+
+ } catch (final JMException e) {
+ log.error("Could not add loggerMBean for [" + name + "].", e);
+ } catch (final RuntimeException e) {
+ log.error("Could not add loggerMBean for [" + name + "].", e);
+ }
+ return objectName;
+ }
+
+ public ObjectName addLoggerMBean(final String name) {
+ final Logger cat = LogManager.exists(name);
+
+ if (cat != null) {
+ return addLoggerMBean(cat);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void addNotificationListener(
+ final NotificationListener listener, final NotificationFilter filter, final java.lang.Object handback) {
+ nbs.addNotificationListener(listener, filter, handback);
+ }
+
+ private void buildDynamicMBeanInfo() {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo(
+ "HierarchyDynamicMBean(): Constructs a HierarchyDynamicMBean instance", constructors[0]);
+
+ vAttributes.add(new MBeanAttributeInfo(
+ THRESHOLD, "java.lang.String", "The \"threshold\" state of the hiearchy.", true, true, false));
+
+ final MBeanParameterInfo[] params = new MBeanParameterInfo[1];
+ params[0] = new MBeanParameterInfo("name", "java.lang.String", "Create a logger MBean");
+ dOperations[0] = new MBeanOperationInfo(
+ "addLoggerMBean",
+ "addLoggerMBean(): add a loggerMBean",
+ params,
+ "javax.management.ObjectName",
+ MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName)
+ throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ log.debug("Called getAttribute with [" + attributeName + "].");
+
+ // Check for a recognized attributeName and call the corresponding getter
+ if (attributeName.equals(THRESHOLD)) {
+ return hierarchy.getThreshold();
+ } else if (attributeName.startsWith("logger")) {
+ final int k = attributeName.indexOf("%3D");
+ String val = attributeName;
+ if (k > 0) {
+ val = attributeName.substring(0, k) + '=' + attributeName.substring(k + 3);
+ }
+ try {
+ return new ObjectName("log4j:" + val);
+ } catch (final JMException e) {
+ log.error("Could not create ObjectName" + val);
+ } catch (final RuntimeException e) {
+ log.error("Could not create ObjectName" + val);
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return log;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ // cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[vAttributes.size()];
+ vAttributes.toArray(attribs);
+
+ return new MBeanInfo(
+ dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ }
+
+ @Override
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ return nbs.getNotificationInfo();
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[])
+ throws MBeanException, ReflectionException {
+
+ if (operationName == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Operation name cannot be null"),
+ "Cannot invoke a null operation in " + dClassName);
+ }
+ // Check for a recognized operation name and call the corresponding operation
+
+ if (operationName.equals("addLoggerMBean")) {
+ return addLoggerMBean((String) params[0]);
+ } else {
+ throw new ReflectionException(
+ new NoSuchMethodException(operationName),
+ "Cannot find the operation " + operationName + " in " + dClassName);
+ }
+ }
+
+ @Override
+ public void postRegister(final java.lang.Boolean registrationDone) {
+ log.debug("postRegister is called.");
+ hierarchy.addHierarchyEventListener(this);
+ final Logger root = hierarchy.getRootLogger();
+ addLoggerMBean(root);
+ }
+
+ @Override
+ public void removeAppenderEvent(final Category cat, final Appender appender) {
+ log.debug("removeAppenderCalled: logger=" + cat.getName() + ", appender=" + appender.getName());
+ }
+
+ @Override
+ public void removeNotificationListener(final NotificationListener listener) throws ListenerNotFoundException {
+ nbs.removeNotificationListener(listener);
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute)
+ throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ final Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ if (name.equals(THRESHOLD)) {
+ final Level l = OptionConverter.toLevel((String) value, hierarchy.getThreshold());
+ hierarchy.setThreshold(l);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java
new file mode 100644
index 00000000000..d31e63d7713
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java
@@ -0,0 +1,225 @@
+/*
+ * 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.log4j.jmx;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.Vector;
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.OptionHandler;
+
+public class LayoutDynamicMBean extends AbstractDynamicMBean {
+
+ // This category instance is for logging.
+ private static final Logger cat = Logger.getLogger(LayoutDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+ private final Vector dAttributes = new Vector();
+
+ private final String dClassName = this.getClass().getName();
+ private final Hashtable dynamicProps = new Hashtable(5);
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+
+ private final String dDescription = "This MBean acts as a management facade for log4j layouts.";
+
+ // We wrap this layout instance.
+ private final Layout layout;
+
+ public LayoutDynamicMBean(final Layout layout) throws IntrospectionException {
+ this.layout = layout;
+ buildDynamicMBeanInfo();
+ }
+
+ private void buildDynamicMBeanInfo() throws IntrospectionException {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo(
+ "LayoutDynamicMBean(): Constructs a LayoutDynamicMBean instance", constructors[0]);
+
+ final BeanInfo bi = Introspector.getBeanInfo(layout.getClass());
+ final PropertyDescriptor[] pd = bi.getPropertyDescriptors();
+
+ final int size = pd.length;
+
+ for (int i = 0; i < size; i++) {
+ final String name = pd[i].getName();
+ final Method readMethod = pd[i].getReadMethod();
+ final Method writeMethod = pd[i].getWriteMethod();
+ if (readMethod != null) {
+ final Class returnClass = readMethod.getReturnType();
+ if (isSupportedType(returnClass)) {
+ String returnClassName;
+ if (returnClass.isAssignableFrom(Level.class)) {
+ returnClassName = "java.lang.String";
+ } else {
+ returnClassName = returnClass.getName();
+ }
+
+ dAttributes.add(
+ new MBeanAttributeInfo(name, returnClassName, "Dynamic", true, writeMethod != null, false));
+ dynamicProps.put(name, new MethodUnion(readMethod, writeMethod));
+ }
+ }
+ }
+
+ final MBeanParameterInfo[] params = new MBeanParameterInfo[0];
+
+ dOperations[0] = new MBeanOperationInfo(
+ "activateOptions", "activateOptions(): add an layout", params, "void", MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName)
+ throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(attributeName);
+
+ cat.debug("----name=" + attributeName + ", mu=" + mu);
+
+ if (mu != null && mu.readMethod != null) {
+ try {
+ return mu.readMethod.invoke(layout, null);
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ } catch (final IllegalAccessException e) {
+ return null;
+ } catch (final RuntimeException e) {
+ return null;
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return cat;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ return new MBeanInfo(
+ dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[])
+ throws MBeanException, ReflectionException {
+
+ if (operationName.equals("activateOptions") && layout instanceof OptionHandler) {
+ final OptionHandler oh = (OptionHandler) layout;
+ oh.activateOptions();
+ return "Options activated.";
+ }
+ return null;
+ }
+
+ private boolean isSupportedType(final Class clazz) {
+ if (clazz.isPrimitive() || (clazz == String.class) || clazz.isAssignableFrom(Level.class)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute)
+ throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ final MethodUnion mu = (MethodUnion) dynamicProps.get(name);
+
+ if (mu != null && mu.writeMethod != null) {
+ final Object[] o = new Object[1];
+
+ final Class[] params = mu.writeMethod.getParameterTypes();
+ if (params[0] == org.apache.log4j.Priority.class) {
+ value = OptionConverter.toLevel((String) value, (Level) getAttribute(name));
+ }
+ o[0] = value;
+
+ try {
+ mu.writeMethod.invoke(layout, o);
+
+ } catch (final InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ cat.error("FIXME", e);
+ } catch (final IllegalAccessException e) {
+ cat.error("FIXME", e);
+ } catch (final RuntimeException e) {
+ cat.error("FIXME", e);
+ }
+ } else {
+ throw (new AttributeNotFoundException(
+ "Attribute " + name + " not found in " + this.getClass().getName()));
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java
new file mode 100644
index 00000000000..7be0f070f5f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java
@@ -0,0 +1,242 @@
+/*
+ * 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.log4j.jmx;
+
+import java.lang.reflect.Constructor;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.OptionConverter;
+
+public class LoggerDynamicMBean extends AbstractDynamicMBean implements NotificationListener {
+
+ // This Logger instance is for logging.
+ private static final Logger cat = Logger.getLogger(LoggerDynamicMBean.class);
+ private final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+
+ private final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+ private final Vector dAttributes = new Vector();
+
+ private final String dClassName = this.getClass().getName();
+
+ private final String dDescription =
+ "This MBean acts as a management facade for a org.apache.log4j.Logger instance.";
+
+ // We wrap this Logger instance.
+ private final Logger logger;
+
+ public LoggerDynamicMBean(final Logger logger) {
+ this.logger = logger;
+ buildDynamicMBeanInfo();
+ }
+
+ void addAppender(final String appenderClass, final String appenderName) {
+ cat.debug("addAppender called with " + appenderClass + ", " + appenderName);
+ final Appender appender =
+ (Appender) OptionConverter.instantiateByClassName(appenderClass, org.apache.log4j.Appender.class, null);
+ appender.setName(appenderName);
+ logger.addAppender(appender);
+
+ // appenderMBeanRegistration();
+
+ }
+
+ void appenderMBeanRegistration() {
+ final Enumeration enumeration = logger.getAllAppenders();
+ while (enumeration.hasMoreElements()) {
+ final Appender appender = (Appender) enumeration.nextElement();
+ registerAppenderMBean(appender);
+ }
+ }
+
+ private void buildDynamicMBeanInfo() {
+ final Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo(
+ "HierarchyDynamicMBean(): Constructs a HierarchyDynamicMBean instance", constructors[0]);
+
+ dAttributes.add(
+ new MBeanAttributeInfo("name", "java.lang.String", "The name of this Logger.", true, false, false));
+
+ dAttributes.add(new MBeanAttributeInfo(
+ "priority", "java.lang.String", "The priority of this logger.", true, true, false));
+
+ final MBeanParameterInfo[] params = new MBeanParameterInfo[2];
+ params[0] = new MBeanParameterInfo("class name", "java.lang.String", "add an appender to this logger");
+ params[1] = new MBeanParameterInfo("appender name", "java.lang.String", "name of the appender");
+
+ dOperations[0] = new MBeanOperationInfo(
+ "addAppender", "addAppender(): add an appender", params, "void", MBeanOperationInfo.ACTION);
+ }
+
+ @Override
+ public Object getAttribute(final String attributeName)
+ throws AttributeNotFoundException, MBeanException, ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ // Check for a recognized attributeName and call the corresponding getter
+ if (attributeName.equals("name")) {
+ return logger.getName();
+ } else if (attributeName.equals("priority")) {
+ final Level l = logger.getLevel();
+ if (l == null) {
+ return null;
+ } else {
+ return l.toString();
+ }
+ } else if (attributeName.startsWith("appender=")) {
+ try {
+ return new ObjectName("log4j:" + attributeName);
+ } catch (final MalformedObjectNameException e) {
+ cat.error("Could not create ObjectName" + attributeName);
+ } catch (final RuntimeException e) {
+ cat.error("Could not create ObjectName" + attributeName);
+ }
+ }
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw (new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " + dClassName));
+ }
+
+ @Override
+ protected Logger getLogger() {
+ return logger;
+ }
+
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ // cat.debug("getMBeanInfo called.");
+
+ final MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ final MBeanInfo mb = new MBeanInfo(
+ dClassName, dDescription, attribs, dConstructors, dOperations, new MBeanNotificationInfo[0]);
+ // cat.debug("getMBeanInfo exit.");
+ return mb;
+ }
+
+ @Override
+ public void handleNotification(final Notification notification, final Object handback) {
+ cat.debug("Received notification: " + notification.getType());
+ registerAppenderMBean((Appender) notification.getUserData());
+ }
+
+ @Override
+ public Object invoke(final String operationName, final Object params[], final String signature[])
+ throws MBeanException, ReflectionException {
+
+ if (operationName.equals("addAppender")) {
+ addAppender((String) params[0], (String) params[1]);
+ return "Hello world.";
+ }
+
+ return null;
+ }
+
+ @Override
+ public void postRegister(final java.lang.Boolean registrationDone) {
+ appenderMBeanRegistration();
+ }
+
+ void registerAppenderMBean(final Appender appender) {
+ final String name = getAppenderName(appender);
+ cat.debug("Adding AppenderMBean for appender named " + name);
+ ObjectName objectName = null;
+ try {
+ final AppenderDynamicMBean appenderMBean = new AppenderDynamicMBean(appender);
+ objectName = new ObjectName("log4j", "appender", name);
+ if (!server.isRegistered(objectName)) {
+ registerMBean(appenderMBean, objectName);
+ dAttributes.add(new MBeanAttributeInfo(
+ "appender=" + name,
+ "javax.management.ObjectName",
+ "The " + name + " appender.",
+ true,
+ true,
+ false));
+ }
+
+ } catch (final JMException e) {
+ cat.error("Could not add appenderMBean for [" + name + "].", e);
+ } catch (final java.beans.IntrospectionException e) {
+ cat.error("Could not add appenderMBean for [" + name + "].", e);
+ } catch (final RuntimeException e) {
+ cat.error("Could not add appenderMBean for [" + name + "].", e);
+ }
+ }
+
+ @Override
+ public void setAttribute(final Attribute attribute)
+ throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName + " with null attribute");
+ }
+ final String name = attribute.getName();
+ final Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + dClassName + " with null attribute name");
+ }
+
+ if (name.equals("priority")) {
+ if (value instanceof String) {
+ final String s = (String) value;
+ Level p = logger.getLevel();
+ if (s.equalsIgnoreCase("NULL")) {
+ p = null;
+ } else {
+ p = OptionConverter.toLevel(s, p);
+ }
+ logger.setLevel(p);
+ }
+ } else {
+ throw (new AttributeNotFoundException(
+ "Attribute " + name + " not found in " + this.getClass().getName()));
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/MethodUnion.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/MethodUnion.java
new file mode 100644
index 00000000000..f7a4a80e6b8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/MethodUnion.java
@@ -0,0 +1,30 @@
+/*
+ * 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.log4j.jmx;
+
+import java.lang.reflect.Method;
+
+class MethodUnion {
+
+ Method readMethod;
+ Method writeMethod;
+
+ MethodUnion(final Method readMethod, final Method writeMethod) {
+ this.readMethod = readMethod;
+ this.writeMethod = writeMethod;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/package-info.java
new file mode 100644
index 00000000000..b502a304c98
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/jmx/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+/**
+ * This package lets you manage log4j settings using JMX. It is unfortunately not of production quality.
+ */
+@Export
+@Version("2.20.1")
+package org.apache.log4j.jmx;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java
new file mode 100644
index 00000000000..4a3dcc11625
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1SyslogLayout.java
@@ -0,0 +1,233 @@
+/*
+ * 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.log4j.layout;
+
+import static org.apache.logging.log4j.util.Strings.toRootLowerCase;
+
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.config.Node;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
+import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
+import org.apache.logging.log4j.core.config.plugins.PluginElement;
+import org.apache.logging.log4j.core.layout.AbstractStringLayout;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Priority;
+import org.apache.logging.log4j.core.pattern.DatePatternConverter;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.util.NetUtils;
+import org.apache.logging.log4j.util.Chars;
+
+/**
+ * Port of the layout used by SyslogAppender in Log4j 1.x. Provided for
+ * compatibility with existing Log4j 1 configurations.
+ *
+ * Originally developed by Ceki Gülcü and Anders Kristensen.
+ */
+@Plugin(name = "Log4j1SyslogLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
+public final class Log4j1SyslogLayout extends AbstractStringLayout {
+
+ /**
+ * Builds a SyslogLayout.
+ * The main arguments are
+ *
+ * facility: The Facility is used to try to classify the message.
+ * includeNewLine: If true a newline will be appended to the result.
+ * escapeNL: Pattern to use for replacing newlines.
+ * charset: The character set.
+ *
+ * @param the builder type
+ */
+ public static class Builder> extends AbstractStringLayout.Builder
+ implements org.apache.logging.log4j.core.util.Builder {
+
+ public Builder() {
+ setCharset(StandardCharsets.UTF_8);
+ }
+
+ @PluginBuilderAttribute
+ private Facility facility = Facility.USER;
+
+ @PluginBuilderAttribute
+ private boolean facilityPrinting;
+
+ @PluginBuilderAttribute
+ private boolean header;
+
+ @PluginElement("Layout")
+ private Layout extends Serializable> messageLayout;
+
+ @Override
+ public Log4j1SyslogLayout build() {
+ if (!isValid()) {
+ return null;
+ }
+ if (messageLayout != null && !(messageLayout instanceof StringLayout)) {
+ LOGGER.error("Log4j1SyslogLayout: the message layout must be a StringLayout.");
+ return null;
+ }
+ return new Log4j1SyslogLayout(
+ facility, facilityPrinting, header, (StringLayout) messageLayout, getCharset());
+ }
+
+ public Facility getFacility() {
+ return facility;
+ }
+
+ public boolean isFacilityPrinting() {
+ return facilityPrinting;
+ }
+
+ public boolean isHeader() {
+ return header;
+ }
+
+ public Layout extends Serializable> getMessageLayout() {
+ return messageLayout;
+ }
+
+ public B setFacility(final Facility facility) {
+ this.facility = facility;
+ return asBuilder();
+ }
+
+ public B setFacilityPrinting(final boolean facilityPrinting) {
+ this.facilityPrinting = facilityPrinting;
+ return asBuilder();
+ }
+
+ public B setHeader(final boolean header) {
+ this.header = header;
+ return asBuilder();
+ }
+
+ public B setMessageLayout(final Layout extends Serializable> messageLayout) {
+ this.messageLayout = messageLayout;
+ return asBuilder();
+ }
+ }
+
+ @PluginBuilderFactory
+ public static > B newBuilder() {
+ return new Builder().asBuilder();
+ }
+
+ /**
+ * Host name used to identify messages from this appender.
+ */
+ private static final String localHostname = NetUtils.getLocalHostname();
+
+ private final Facility facility;
+ private final boolean facilityPrinting;
+ private final boolean header;
+ private final StringLayout messageLayout;
+
+ /**
+ * Date format used if header = true.
+ */
+ private static final String[] dateFormatOptions = {"MMM dd HH:mm:ss", null, "en"};
+
+ private final LogEventPatternConverter dateConverter = DatePatternConverter.newInstance(dateFormatOptions);
+
+ private Log4j1SyslogLayout(
+ final Facility facility,
+ final boolean facilityPrinting,
+ final boolean header,
+ final StringLayout messageLayout,
+ final Charset charset) {
+ super(charset);
+ this.facility = facility;
+ this.facilityPrinting = facilityPrinting;
+ this.header = header;
+ this.messageLayout = messageLayout;
+ }
+
+ /**
+ * Formats a {@link LogEvent} in conformance with the BSD Log record format.
+ *
+ * @param event The LogEvent
+ * @return the event formatted as a String.
+ */
+ @Override
+ public String toSerializable(final LogEvent event) {
+ // The messageLayout also uses the thread-bound StringBuilder,
+ // so we generate the message first
+ final String message = messageLayout != null
+ ? messageLayout.toSerializable(event)
+ : event.getMessage().getFormattedMessage();
+ final StringBuilder buf = getStringBuilder();
+
+ buf.append('<');
+ buf.append(Priority.getPriority(facility, event.getLevel()));
+ buf.append('>');
+
+ if (header) {
+ final int index = buf.length() + 4;
+ dateConverter.format(event, buf);
+ // RFC 3164 says leading space, not leading zero on days 1-9
+ if (buf.charAt(index) == '0') {
+ buf.setCharAt(index, Chars.SPACE);
+ }
+
+ buf.append(Chars.SPACE);
+ buf.append(localHostname);
+ buf.append(Chars.SPACE);
+ }
+
+ if (facilityPrinting) {
+ buf.append(facility != null ? toRootLowerCase(facility.name()) : "user")
+ .append(':');
+ }
+
+ buf.append(message);
+ // TODO: splitting message into 1024 byte chunks?
+ return buf.toString();
+ }
+
+ /**
+ * Gets this SyslogLayout's content format. Specified by:
+ *
+ * Key: "structured" Value: "false"
+ * Key: "dateFormat" Value: "MMM dd HH:mm:ss"
+ * Key: "format" Value: "<LEVEL>TIMESTAMP PROP(HOSTNAME) MESSAGE"
+ * Key: "formatType" Value: "logfilepatternreceiver" (format uses the keywords supported by
+ * LogFilePatternReceiver)
+ *
+ *
+ * @return Map of content format keys supporting SyslogLayout
+ */
+ @Override
+ public Map getContentFormat() {
+ final Map result = new HashMap<>();
+ result.put("structured", "false");
+ result.put("formatType", "logfilepatternreceiver");
+ result.put("dateFormat", dateFormatOptions[0]);
+ if (header) {
+ result.put("format", "TIMESTAMP PROP(HOSTNAME) MESSAGE");
+ } else {
+ result.put("format", "MESSAGE");
+ }
+ return result;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
index 9522b9e9e66..a508218b119 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/layout/Log4j1XmlLayout.java
@@ -1,26 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.layout;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
-
+import java.util.Objects;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Node;
@@ -30,7 +31,6 @@
import org.apache.logging.log4j.core.layout.AbstractStringLayout;
import org.apache.logging.log4j.core.layout.ByteBufferDestination;
import org.apache.logging.log4j.core.util.Transform;
-import org.apache.logging.log4j.util.BiConsumer;
import org.apache.logging.log4j.util.ReadOnlyStringMap;
import org.apache.logging.log4j.util.Strings;
@@ -42,6 +42,9 @@
@Plugin(name = "Log4j1XmlLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
public final class Log4j1XmlLayout extends AbstractStringLayout {
+ /** We yield to the \r\n heresy. */
+ private static final String EOL = "\r\n";
+
private final boolean locationInfo;
private final boolean properties;
@@ -51,7 +54,7 @@ public static Log4j1XmlLayout createLayout(
@PluginAttribute(value = "locationInfo") final boolean locationInfo,
@PluginAttribute(value = "properties") final boolean properties
// @formatter:on
- ) {
+ ) {
return new Log4j1XmlLayout(locationInfo, properties);
}
@@ -83,9 +86,10 @@ public String toSerializable(final LogEvent event) {
return text.toString();
}
+ @SuppressFBWarnings(
+ value = "INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE",
+ justification = "The throwable is formatted into a log file, which should be private.")
private void formatTo(final LogEvent event, final StringBuilder buf) {
- // We yield to the \r\n heresy.
-
buf.append("\r\n");
+ buf.append("\">");
+ buf.append(EOL);
buf.append(" \r\n");
+ buf.append("]]>");
+ buf.append(EOL);
final List ndc = event.getContextStack().asList();
if (!ndc.isEmpty()) {
buf.append(" \r\n");
+ buf.append("]]>");
+ buf.append(EOL);
}
@SuppressWarnings("ThrowableResultOfMethodCallIgnored")
- final Throwable thrown = event.getThrown();
+ final Throwable thrown = event.getThrown();
if (thrown != null) {
buf.append(" \r\n");
+ buf.append("]]>");
+ buf.append(EOL);
}
if (locationInfo) {
@@ -129,7 +137,8 @@ private void formatTo(final LogEvent event, final StringBuilder buf) {
buf.append(Transform.escapeHtmlTags(source.getFileName()));
buf.append("\" line=\"");
buf.append(source.getLineNumber());
- buf.append("\"/>\r\n");
+ buf.append("\"/>");
+ buf.append(EOL);
}
}
@@ -137,23 +146,23 @@ private void formatTo(final LogEvent event, final StringBuilder buf) {
final ReadOnlyStringMap contextMap = event.getContextData();
if (!contextMap.isEmpty()) {
buf.append("\r\n");
- contextMap.forEach(new BiConsumer() {
- @Override
- public void accept(final String key, final String val) {
- if (val != null) {
- buf.append(" \r\n");
- }
+ contextMap.forEach((key, val) -> {
+ if (val != null) {
+ buf.append(" ");
+ buf.append(EOL);
}
});
- buf.append(" \r\n");
+ buf.append("");
+ buf.append(EOL);
}
}
- buf.append(" \r\n\r\n");
+ buf.append("");
+ buf.append(EOL);
+ buf.append(EOL);
}
-
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
new file mode 100644
index 00000000000..d85c4c732ae
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/CategoryUtil.java
@@ -0,0 +1,177 @@
+/*
+ * 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.log4j.legacy.core;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.spi.LoggerContext;
+
+/**
+ * Delegates to {@code Logger} methods implemented by {@code log4j-core} if appropriate.
+ */
+public final class CategoryUtil {
+
+ private static org.apache.logging.log4j.core.Logger asCore(final Logger logger) {
+ return (org.apache.logging.log4j.core.Logger) logger;
+ }
+
+ private static T get(final Logger logger, final Supplier run, final T defaultValue) {
+ return isCore(logger) ? run.get() : defaultValue;
+ }
+
+ /**
+ * Gets the appenders attached directly to this logger.
+ *
+ * @param logger The target logger.
+ * @return A Map containing the Appender's name as the key and the Appender as the value.
+ */
+ public static Map getAppenders(final Logger logger) {
+ return get(logger, () -> getDirectAppenders(logger), Collections.emptyMap());
+ }
+
+ private static Map getDirectAppenders(final Logger logger) {
+ return CategoryUtil.getExactLoggerConfig(logger)
+ .map(LoggerConfig::getAppenders)
+ .orElse(Collections.emptyMap());
+ }
+
+ private static Optional getExactLoggerConfig(final Logger logger) {
+ return Optional.of(asCore(logger).get()).filter(lc -> logger.getName().equals(lc.getName()));
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#getFilters()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return An Iterator over all the Filters associated with the Logger.
+ */
+ public static Iterator getFilters(final Logger logger) {
+ return get(logger, asCore(logger)::getFilters, null);
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#getContext()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return the LoggerContext.
+ */
+ public static LoggerContext getLoggerContext(final Logger logger) {
+ return get(logger, asCore(logger)::getContext, null);
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#getParent()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return The parent Logger.
+ */
+ public static Logger getParent(final Logger logger) {
+ return get(logger, asCore(logger)::getParent, null);
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#isAdditive()} if appropriate.
+ *
+ * @param logger The target logger.
+ * @return true if the associated LoggerConfig is additive, false otherwise.
+ */
+ public static boolean isAdditive(final Logger logger) {
+ return get(logger, asCore(logger)::isAdditive, false);
+ }
+
+ private static boolean isCore(final Logger logger) {
+ return logger instanceof org.apache.logging.log4j.core.Logger;
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#setAdditive(boolean)} if appropriate.
+ *
+ * @param logger The target logger.
+ * @param additive Boolean value to indicate whether the Logger is additive or not.
+ */
+ public static void setAdditivity(final Logger logger, final boolean additive) {
+ if (isCore(logger)) {
+ asCore(logger).setAdditive(additive);
+ }
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.Logger#setLevel(Level)} if appropriate.
+ *
+ * @param logger The target logger.
+ * @param level The Level to use on this Logger, may be null.
+ */
+ public static void setLevel(final Logger logger, final Level level) {
+ if (isCore(logger)) {
+ Configurator.setLevel(asCore(logger), level);
+ }
+ }
+
+ /**
+ * Returns the level explicitly set on the logger.
+ *
+ * If the Log4j API implementation does not support it, returns the effective level instead.
+ *
+ */
+ public static Level getExplicitLevel(final Logger logger) {
+ return isCore(logger) ? getExplicitLevel(asCore(logger)) : logger.getLevel();
+ }
+
+ private static Level getExplicitLevel(final org.apache.logging.log4j.core.Logger logger) {
+ final LoggerConfig config = logger.get();
+ return config.getName().equals(logger.getName()) ? config.getExplicitLevel() : null;
+ }
+
+ /**
+ * Adds an appender to the logger. This method requires a check for the presence
+ * of Log4j Core or it will cause a {@code ClassNotFoundException}.
+ *
+ * @param logger The target logger.
+ * @param appender A Log4j2 appender.
+ */
+ public static void addAppender(final Logger logger, final Appender appender) {
+ if (appender instanceof AppenderAdapter.Adapter) {
+ appender.start();
+ }
+ asCore(logger).addAppender(appender);
+ }
+
+ /**
+ * Sends the event to all appenders directly connected with the logger. This
+ * method requires a check for the presence of Log4j Core or it will cause a
+ * {@code ClassNotFoundException}.
+ *
+ * @param logger The target logger.
+ * @param event The event to send.
+ */
+ public static void log(final Logger logger, final LogEvent event) {
+ getExactLoggerConfig(logger).ifPresent(lc -> lc.log(event));
+ }
+
+ private CategoryUtil() {}
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java
new file mode 100644
index 00000000000..bf94a8d4093
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/legacy/core/ContextUtil.java
@@ -0,0 +1,49 @@
+/*
+ * 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.log4j.legacy.core;
+
+import org.apache.logging.log4j.spi.LoggerContext;
+
+/**
+ * Delegates to {@code LoggerContext} methods implemented by {@code log4j-core} if appropriate.
+ */
+public final class ContextUtil {
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.LoggerContext#reconfigure()} if appropriate.
+ *
+ * @param loggerContext The target logger context.
+ */
+ public static void reconfigure(final LoggerContext loggerContext) {
+ if (loggerContext instanceof org.apache.logging.log4j.core.LoggerContext) {
+ ((org.apache.logging.log4j.core.LoggerContext) loggerContext).reconfigure();
+ }
+ }
+
+ /**
+ * Delegates to {@link org.apache.logging.log4j.core.LoggerContext#close()} if appropriate.
+ *
+ * @param loggerContext The target logger context.
+ */
+ public static void shutdown(final LoggerContext loggerContext) {
+ if (loggerContext instanceof org.apache.logging.log4j.core.LoggerContext) {
+ ((org.apache.logging.log4j.core.LoggerContext) loggerContext).close();
+ }
+ }
+
+ private ContextUtil() {}
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/DefaultRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/DefaultRenderer.java
new file mode 100644
index 00000000000..a677d13037a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/DefaultRenderer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.log4j.or;
+
+/**
+ * The default ObjectRenderer renders objects by calling their {@code toString()} method.
+ *
+ * @since 1.0
+ */
+class DefaultRenderer implements ObjectRenderer {
+
+ DefaultRenderer() {}
+
+ /**
+ * Render the object passed as parameter by calling its {@code toString()} method.
+ */
+ public String doRender(final Object o) {
+ try {
+ return o.toString();
+ } catch (Exception ex) {
+ return ex.toString();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java
new file mode 100644
index 00000000000..e114cf51d92
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ObjectRenderer.java
@@ -0,0 +1,29 @@
+/*
+ * 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.log4j.or;
+
+/**
+ * Converts objects to Strings.
+ */
+public interface ObjectRenderer {
+ /**
+ * Render the object passed as parameter as a String.
+ * @param o The object to render.
+ * @return The String representation of the object.
+ */
+ String doRender(Object o);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererMap.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererMap.java
new file mode 100644
index 00000000000..e78576193d1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/RendererMap.java
@@ -0,0 +1,174 @@
+/*
+ * 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.log4j.or;
+
+import java.util.Hashtable;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.logging.log4j.core.util.Loader;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Map class objects to an {@link ObjectRenderer}.
+ *
+ * @author Ceki Gülcü
+ * @since version 1.0
+ */
+public class RendererMap {
+
+ Hashtable map;
+
+ static ObjectRenderer defaultRenderer = new DefaultRenderer();
+
+ public RendererMap() {
+ map = new Hashtable();
+ }
+
+ /**
+ * Add a renderer to a hierarchy passed as parameter.
+ */
+ public static void addRenderer(
+ final RendererSupport repository, final String renderedClassName, final String renderingClassName) {
+ StatusLogger.getLogger()
+ .debug("Rendering class: [" + renderingClassName + "], Rendered class: [" + renderedClassName + "].");
+ final ObjectRenderer renderer =
+ (ObjectRenderer) OptionConverter.instantiateByClassName(renderingClassName, ObjectRenderer.class, null);
+ if (renderer == null) {
+ StatusLogger.getLogger().error("Could not instantiate renderer [" + renderingClassName + "].");
+ return;
+ }
+ try {
+ final Class renderedClass = Loader.loadClass(renderedClassName);
+ repository.setRenderer(renderedClass, renderer);
+ } catch (ClassNotFoundException e) {
+ StatusLogger.getLogger().error("Could not find class [" + renderedClassName + "].", e);
+ }
+ }
+
+ /**
+ * Find the appropriate renderer for the class type of the o
parameter. This is accomplished by calling the
+ * {@link #get(Class)} method. Once a renderer is found, it is applied on the object o
and the result is
+ * returned as a {@link String}.
+ */
+ public String findAndRender(final Object o) {
+ if (o == null) return null;
+ else return get(o.getClass()).doRender(o);
+ }
+
+ /**
+ * Syntactic sugar method that calls {@link #get(Class)} with the class of the object parameter.
+ */
+ public ObjectRenderer get(final Object o) {
+ if (o == null) return null;
+ else return get(o.getClass());
+ }
+
+ /**
+ * Search the parents of clazz
for a renderer. The renderer closest in the hierarchy will be returned. If
+ * no renderers could be found, then the default renderer is returned.
+ *
+ *
+ * The search first looks for a renderer configured for clazz
. If a renderer could not be found, then the
+ * search continues by looking at all the interfaces implemented by clazz
including the super-interfaces of
+ * each interface. If a renderer cannot be found, then the search looks for a renderer defined for the parent
+ * (superclass) of clazz
. If that fails, then all the interfaces implemented by the parent of
+ * clazz
are searched and so on.
+ *
+ *
+ * For example, if A0, A1, A2 are classes and X0, X1, X2, Y0, Y1 are interfaces where A2 extends A1 which in turn
+ * extends A0 and similarly X2 extends X1 which extends X0 and Y1 extends Y0. Let us also assume that A1 implements the
+ * Y0 interface and that A2 implements the X2 interface.
+ *
+ *
+ * The table below shows the results returned by the get(A2.class)
method depending on the renderers added
+ * to the map.
+ *
+ *
+ *
+ *
+ * Added renderers
+ * Value returned by get(A2.class)
+ *
+ *
+ * A0Renderer
+ * A0Renderer
+ *
+ *
+ * A0Renderer, A1Renderer
+ * A1Renderer
+ *
+ *
+ * X0Renderer
+ * X0Renderer
+ *
+ *
+ * A1Renderer, X0Renderer
+ * X0Renderer
+ *
+ *
+ *
+ *
+ * This search algorithm is not the most natural, although it is particularly easy to implement. Future log4j versions
+ * may implement a more intuitive search algorithm. However, the present algorithm should be acceptable in the
+ * vast majority of circumstances.
+ *
+ */
+ public ObjectRenderer get(final Class clazz) {
+ // System.out.println("\nget: "+clazz);
+ ObjectRenderer r = null;
+ for (Class c = clazz; c != null; c = c.getSuperclass()) {
+ // System.out.println("Searching for class: "+c);
+ r = (ObjectRenderer) map.get(c);
+ if (r != null) {
+ return r;
+ }
+ r = searchInterfaces(c);
+ if (r != null) return r;
+ }
+ return defaultRenderer;
+ }
+
+ ObjectRenderer searchInterfaces(Class c) {
+ // System.out.println("Searching interfaces of class: "+c);
+
+ ObjectRenderer r = (ObjectRenderer) map.get(c);
+ if (r != null) {
+ return r;
+ }
+ final Class[] ia = c.getInterfaces();
+ for (int i = 0; i < ia.length; i++) {
+ r = searchInterfaces(ia[i]);
+ if (r != null) return r;
+ }
+ return null;
+ }
+
+ public ObjectRenderer getDefaultRenderer() {
+ return defaultRenderer;
+ }
+
+ public void clear() {
+ map.clear();
+ }
+
+ /**
+ * Register an {@link ObjectRenderer} for clazz
.
+ */
+ public void put(final Class clazz, final ObjectRenderer or) {
+ map.put(clazz, or);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
new file mode 100644
index 00000000000..3da0dbedb49
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
@@ -0,0 +1,56 @@
+/*
+ * 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.log4j.or;
+
+import org.apache.log4j.Layout;
+
+/**
+ */
+public class ThreadGroupRenderer implements ObjectRenderer {
+
+ @Override
+ public String doRender(final Object obj) {
+ if (obj instanceof ThreadGroup) {
+ final StringBuilder sb = new StringBuilder();
+ final ThreadGroup threadGroup = (ThreadGroup) obj;
+ sb.append("java.lang.ThreadGroup[name=");
+ sb.append(threadGroup.getName());
+ sb.append(", maxpri=");
+ sb.append(threadGroup.getMaxPriority());
+ sb.append("]");
+ final Thread[] threads = new Thread[threadGroup.activeCount()];
+ threadGroup.enumerate(threads);
+ for (Thread thread : threads) {
+ sb.append(Layout.LINE_SEP);
+ sb.append(" Thread=[");
+ sb.append(thread.getName());
+ sb.append(",");
+ sb.append(thread.getPriority());
+ sb.append(",");
+ sb.append(thread.isDaemon());
+ sb.append("]");
+ }
+ return sb.toString();
+ }
+ try {
+ // this is the best we can do
+ return obj.toString();
+ } catch (Exception ex) {
+ return ex.toString();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
new file mode 100644
index 00000000000..ffc5e9385af
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
@@ -0,0 +1,86 @@
+/*
+ * 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.log4j.or.jms;
+
+import javax.jms.DeliveryMode;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+
+/**
+ * Log4j 1.x JMS Message Renderer
+ */
+public class MessageRenderer implements ObjectRenderer {
+ private static final Logger LOGGER = StatusLogger.getLogger();
+
+ /**
+ * Render a {@link javax.jms.Message}.
+ */
+ @Override
+ public String doRender(final Object obj) {
+ if (obj instanceof Message) {
+ final StringBuilder sb = new StringBuilder();
+ final Message message = (Message) obj;
+ try {
+ sb.append("DeliveryMode=");
+ switch (message.getJMSDeliveryMode()) {
+ case DeliveryMode.NON_PERSISTENT:
+ sb.append("NON_PERSISTENT");
+ break;
+ case DeliveryMode.PERSISTENT:
+ sb.append("PERSISTENT");
+ break;
+ default:
+ sb.append("UNKNOWN");
+ }
+ sb.append(", CorrelationID=");
+ sb.append(message.getJMSCorrelationID());
+
+ sb.append(", Destination=");
+ sb.append(message.getJMSDestination());
+
+ sb.append(", Expiration=");
+ sb.append(message.getJMSExpiration());
+
+ sb.append(", MessageID=");
+ sb.append(message.getJMSMessageID());
+
+ sb.append(", Priority=");
+ sb.append(message.getJMSPriority());
+
+ sb.append(", Redelivered=");
+ sb.append(message.getJMSRedelivered());
+
+ sb.append(", ReplyTo=");
+ sb.append(message.getJMSReplyTo());
+
+ sb.append(", Timestamp=");
+ sb.append(message.getJMSTimestamp());
+
+ sb.append(", Type=");
+ sb.append(message.getJMSType());
+
+ } catch (JMSException e) {
+ LOGGER.error("Could not parse Message.", e);
+ }
+ return sb.toString();
+ }
+ return obj.toString();
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/package-info.java
new file mode 100644
index 00000000000..85922fd2e41
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/jms/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.20.1")
+package org.apache.log4j.or.jms;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/or/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/or/package-info.java
new file mode 100644
index 00000000000..4cee049d061
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/or/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.20.1")
+package org.apache.log4j.or;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/package-info.java
index 714c20030b9..7462396fa7f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/package-info.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/package-info.java
@@ -17,4 +17,9 @@
/**
* Log4j 1.x compatibility layer.
*/
+@Export
+@Version("2.20.2")
package org.apache.log4j;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
new file mode 100644
index 00000000000..a51f9e2d3cd
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
@@ -0,0 +1,127 @@
+/*
+ * 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.log4j.pattern;
+
+/**
+ * Modifies the output of a pattern converter for a specified minimum and maximum width and alignment.
+ */
+public final class FormattingInfo {
+ /**
+ * Array of spaces.
+ */
+ private static final char[] SPACES = new char[] {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+
+ /**
+ * Default instance.
+ */
+ private static final FormattingInfo DEFAULT = new FormattingInfo(false, 0, Integer.MAX_VALUE);
+
+ /**
+ * Gets default instance.
+ *
+ * @return default instance.
+ */
+ public static FormattingInfo getDefault() {
+ return DEFAULT;
+ }
+
+ /**
+ * Minimum length.
+ */
+ private final int minLength;
+
+ /**
+ * Maximum length.
+ */
+ private final int maxLength;
+
+ /**
+ * Alignment.
+ */
+ private final boolean leftAlign;
+
+ /**
+ * Creates new instance.
+ *
+ * @param leftAlign left align if true.
+ * @param minLength minimum length.
+ * @param maxLength maximum length.
+ */
+ public FormattingInfo(final boolean leftAlign, final int minLength, final int maxLength) {
+ this.leftAlign = leftAlign;
+ this.minLength = minLength;
+ this.maxLength = maxLength;
+ }
+
+ /**
+ * Adjust the content of the buffer based on the specified lengths and alignment.
+ *
+ * @param fieldStart start of field in buffer.
+ * @param buffer buffer to be modified.
+ */
+ public void format(final int fieldStart, final StringBuffer buffer) {
+ final int rawLength = buffer.length() - fieldStart;
+
+ if (rawLength > maxLength) {
+ buffer.delete(fieldStart, buffer.length() - maxLength);
+ } else if (rawLength < minLength) {
+ if (leftAlign) {
+ final int fieldEnd = buffer.length();
+ buffer.setLength(fieldStart + minLength);
+
+ for (int i = fieldEnd; i < buffer.length(); i++) {
+ buffer.setCharAt(i, ' ');
+ }
+ } else {
+ int padLength = minLength - rawLength;
+
+ for (; padLength > 8; padLength -= 8) {
+ buffer.insert(fieldStart, SPACES);
+ }
+
+ buffer.insert(fieldStart, SPACES, 0, padLength);
+ }
+ }
+ }
+
+ /**
+ * Get maximum length.
+ *
+ * @return maximum length.
+ */
+ public int getMaxLength() {
+ return maxLength;
+ }
+
+ /**
+ * Get minimum length.
+ *
+ * @return minimum length.
+ */
+ public int getMinLength() {
+ return minLength;
+ }
+
+ /**
+ * Determine if left aligned.
+ *
+ * @return true if left aligned.
+ */
+ public boolean isLeftAligned() {
+ return leftAlign;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java
new file mode 100644
index 00000000000..4b0cb5ae814
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1LevelPatternConverter.java
@@ -0,0 +1,47 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
+import org.apache.logging.log4j.core.pattern.PatternConverter;
+
+/**
+ * Outputs the Log4j 1.x level name.
+ */
+@Plugin(name = "Log4j1LevelPatternConverter", category = PatternConverter.CATEGORY)
+@ConverterKeys({"v1Level"})
+public class Log4j1LevelPatternConverter extends LogEventPatternConverter {
+
+ private static final Log4j1LevelPatternConverter INSTANCE = new Log4j1LevelPatternConverter();
+
+ public static Log4j1LevelPatternConverter newInstance(final String[] options) {
+ return INSTANCE;
+ }
+
+ private Log4j1LevelPatternConverter() {
+ super("Log4j1Level", "v1Level");
+ }
+
+ @Override
+ public void format(final LogEvent event, final StringBuilder toAppendTo) {
+ toAppendTo.append(OptionConverter.convertLevel(event.getLevel()).toString());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
index b4ae0c5e93b..17fbc3568fe 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1MdcPatternConverter.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.pattern;
@@ -29,7 +29,7 @@
* within the property bundle when this pattern converter has the option set.
*/
@Plugin(name = "Log4j1MdcPatternConverter", category = PatternConverter.CATEGORY)
-@ConverterKeys({ "properties" })
+@ConverterKeys({"properties"})
public final class Log4j1MdcPatternConverter extends LogEventPatternConverter {
/**
* Name of property to output.
@@ -79,10 +79,6 @@ public void format(final LogEvent event, final StringBuilder toAppendTo) {
}
}
- private static TriConsumer APPEND_EACH = new TriConsumer() {
- @Override
- public void accept(final String key, final Object value, final StringBuilder toAppendTo) {
+ private static TriConsumer APPEND_EACH = (key, value, toAppendTo) ->
toAppendTo.append('{').append(key).append(',').append(value).append('}');
- }
- };
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
index 405db0093c8..5a37e79008f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/Log4j1NdcPatternConverter.java
@@ -1,21 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.pattern;
+import java.util.List;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
@@ -23,20 +24,16 @@
import org.apache.logging.log4j.core.pattern.PatternConverter;
import org.apache.logging.log4j.util.Strings;
-import java.util.List;
-
-
/**
* Returns the event's NDC in a StringBuilder.
*/
@Plugin(name = "Log4j1NdcPatternConverter", category = PatternConverter.CATEGORY)
-@ConverterKeys({ "ndc" })
+@ConverterKeys({"ndc"})
public final class Log4j1NdcPatternConverter extends LogEventPatternConverter {
/**
* Singleton.
*/
- private static final Log4j1NdcPatternConverter INSTANCE =
- new Log4j1NdcPatternConverter();
+ private static final Log4j1NdcPatternConverter INSTANCE = new Log4j1NdcPatternConverter();
/**
* Private constructor.
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
new file mode 100644
index 00000000000..fc7488fa57a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
@@ -0,0 +1,341 @@
+/*
+ * 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.log4j.pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * NameAbbreviator generates abbreviated logger and class names.
+ */
+public abstract class NameAbbreviator {
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class DropElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ *
+ * @param count maximum number of path elements to output.
+ */
+ public DropElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ *
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ int i = count;
+ for (int pos = buf.indexOf(".", nameStart); pos != -1; pos = buf.indexOf(".", pos + 1)) {
+ if (--i == 0) {
+ buf.delete(nameStart, pos + 1);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class MaxElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ *
+ * @param count maximum number of path elements to output.
+ */
+ public MaxElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ *
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = buf.length() - 1;
+
+ final String bufString = buf.toString();
+ for (int i = count; i > 0; i--) {
+ end = bufString.lastIndexOf(".", end - 1);
+
+ if ((end == -1) || (end < nameStart)) {
+ return;
+ }
+ }
+
+ buf.delete(nameStart, end + 1);
+ }
+ }
+
+ /**
+ * Abbreviator that simply appends full name to buffer.
+ */
+ private static class NOPAbbreviator extends NameAbbreviator {
+ /**
+ * Constructor.
+ */
+ public NOPAbbreviator() {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {}
+ }
+
+ /**
+ * Pattern abbreviator.
+ *
+ *
+ */
+ private static class PatternAbbreviator extends NameAbbreviator {
+ /**
+ * Element abbreviation patterns.
+ */
+ private final PatternAbbreviatorFragment[] fragments;
+
+ /**
+ * Create PatternAbbreviator.
+ *
+ * @param fragments element abbreviation patterns.
+ */
+ public PatternAbbreviator(final List fragments) {
+ if (fragments.size() == 0) {
+ throw new IllegalArgumentException("fragments must have at least one element");
+ }
+
+ this.fragments = new PatternAbbreviatorFragment[fragments.size()];
+ fragments.toArray(this.fragments);
+ }
+
+ /**
+ * Abbreviate name.
+ *
+ * @param buf buffer that abbreviated name is appended.
+ * @param nameStart start of name.
+ */
+ @Override
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ //
+ // all non-terminal patterns are executed once
+ //
+ int pos = nameStart;
+
+ for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length()); i++) {
+ pos = fragments[i].abbreviate(buf, pos);
+ }
+
+ //
+ // last pattern in executed repeatedly
+ //
+ final PatternAbbreviatorFragment terminalFragment = fragments[fragments.length - 1];
+
+ while ((pos < buf.length()) && (pos >= 0)) {
+ pos = terminalFragment.abbreviate(buf, pos);
+ }
+ }
+ }
+
+ /**
+ * Fragment of an pattern abbreviator.
+ *
+ */
+ private static class PatternAbbreviatorFragment {
+ /**
+ * Count of initial characters of element to output.
+ */
+ private final int charCount;
+
+ /**
+ * Character used to represent dropped characters. '\0' indicates no representation of dropped characters.
+ */
+ private final char ellipsis;
+
+ /**
+ * Creates a PatternAbbreviatorFragment.
+ *
+ * @param charCount number of initial characters to preserve.
+ * @param ellipsis character to represent elimination of characters, '\0' if no ellipsis is desired.
+ */
+ public PatternAbbreviatorFragment(final int charCount, final char ellipsis) {
+ this.charCount = charCount;
+ this.ellipsis = ellipsis;
+ }
+
+ /**
+ * Abbreviate element of name.
+ *
+ * @param buf buffer to receive element.
+ * @param startPos starting index of name element.
+ * @return starting index of next element.
+ */
+ public int abbreviate(final StringBuffer buf, final int startPos) {
+ int nextDot = buf.toString().indexOf(".", startPos);
+
+ if (nextDot != -1) {
+ if ((nextDot - startPos) > charCount) {
+ buf.delete(startPos + charCount, nextDot);
+ nextDot = startPos + charCount;
+
+ if (ellipsis != '\0') {
+ buf.insert(nextDot, ellipsis);
+ nextDot++;
+ }
+ }
+
+ nextDot++;
+ }
+
+ return nextDot;
+ }
+ }
+
+ /**
+ * Default (no abbreviation) abbreviator.
+ */
+ private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
+
+ /**
+ * Gets an abbreviator.
+ *
+ * For example, "%logger{2}" will output only 2 elements of the logger name, %logger{-2} will drop 2 elements from the
+ * logger name, "%logger{1.}" will output only the first character of the non-final elements in the name,
+ * "%logger{1~.2~} will output the first character of the first element, two characters of the second and subsequent
+ * elements and will use a tilde to indicate abbreviated characters.
+ *
+ * @param pattern abbreviation pattern.
+ * @return abbreviator, will not be null.
+ */
+ public static NameAbbreviator getAbbreviator(final String pattern) {
+ if (pattern.length() > 0) {
+ // if pattern is just spaces and numbers then
+ // use MaxElementAbbreviator
+ final String trimmed = pattern.trim();
+
+ if (trimmed.length() == 0) {
+ return DEFAULT;
+ }
+
+ int i = 0;
+ if (trimmed.length() > 0) {
+ if (trimmed.charAt(0) == '-') {
+ i++;
+ }
+ for (; (i < trimmed.length()) && (trimmed.charAt(i) >= '0') && (trimmed.charAt(i) <= '9'); i++) {}
+ }
+
+ //
+ // if all blanks and digits
+ //
+ if (i == trimmed.length()) {
+ final int elements = Integer.parseInt(trimmed);
+ if (elements >= 0) {
+ return new MaxElementAbbreviator(elements);
+ } else {
+ return new DropElementAbbreviator(-elements);
+ }
+ }
+
+ final ArrayList fragments = new ArrayList(5);
+ char ellipsis;
+ int charCount;
+ int pos = 0;
+
+ while ((pos < trimmed.length()) && (pos >= 0)) {
+ int ellipsisPos = pos;
+
+ if (trimmed.charAt(pos) == '*') {
+ charCount = Integer.MAX_VALUE;
+ ellipsisPos++;
+ } else {
+ if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) {
+ charCount = trimmed.charAt(pos) - '0';
+ ellipsisPos++;
+ } else {
+ charCount = 0;
+ }
+ }
+
+ ellipsis = '\0';
+
+ if (ellipsisPos < trimmed.length()) {
+ ellipsis = trimmed.charAt(ellipsisPos);
+
+ if (ellipsis == '.') {
+ ellipsis = '\0';
+ }
+ }
+
+ fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
+ pos = trimmed.indexOf(".", pos);
+
+ if (pos == -1) {
+ break;
+ }
+
+ pos++;
+ }
+
+ return new PatternAbbreviator(fragments);
+ }
+
+ //
+ // no matching abbreviation, return defaultAbbreviator
+ //
+ return DEFAULT;
+ }
+
+ /**
+ * Gets default abbreviator.
+ *
+ * @return default abbreviator.
+ */
+ public static NameAbbreviator getDefaultAbbreviator() {
+ return DEFAULT;
+ }
+
+ /**
+ * Abbreviates a name in a StringBuffer.
+ *
+ * @param nameStart starting position of name in buf.
+ * @param buf buffer, may not be null.
+ */
+ public abstract void abbreviate(final int nameStart, final StringBuffer buf);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/package-info.java
new file mode 100644
index 00000000000..dea0a473e4e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/pattern/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.20.1")
+package org.apache.log4j.pattern;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
new file mode 100644
index 00000000000..5841920d674
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.java
@@ -0,0 +1,123 @@
+/*
+ * 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.log4j.rewrite;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.MapMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+
+/**
+ * This policy rewrites events where the message of the
+ * original event implements java.util.Map.
+ * All other events are passed through unmodified.
+ * If the map contains a "message" entry, the value will be
+ * used as the message for the rewritten event. The rewritten
+ * event will have a property set that is the combination of the
+ * original property set and the other members of the message map.
+ * If both the original property set and the message map
+ * contain the same entry, the value from the message map
+ * will overwrite the original property set.
+ *
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the MapFilter from log4j 1.3.
+ *
+ */
+public class MapRewritePolicy implements RewritePolicy {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LoggingEvent rewrite(final LoggingEvent source) {
+ final Object msg = source.getMessage();
+ if (msg instanceof MapMessage || msg instanceof Map) {
+ final Map props =
+ source.getProperties() != null ? new HashMap<>(source.getProperties()) : new HashMap<>();
+ @SuppressWarnings("unchecked")
+ final Map eventProps = msg instanceof Map ? (Map) msg : ((MapMessage) msg).getData();
+ //
+ // if the map sent in the logging request
+ // has "message" entry, use that as the message body
+ // otherwise, use the entire map.
+ //
+ Message newMessage = null;
+ final Object newMsg = eventProps.get("message");
+ if (newMsg != null) {
+ newMessage = new SimpleMessage(newMsg.toString());
+ for (Map.Entry entry : eventProps.entrySet()) {
+ if (!("message".equals(entry.getKey()))) {
+ props.put(entry.getKey(), entry.getValue().toString());
+ }
+ }
+ } else {
+ return source;
+ }
+
+ LogEvent event;
+ if (source instanceof LogEventAdapter) {
+ event = new Log4jLogEvent.Builder(((LogEventAdapter) source).getEvent())
+ .setMessage(newMessage)
+ .setContextData(new SortedArrayStringMap(props))
+ .build();
+ } else {
+ final LocationInfo info = source.getLocationInformation();
+ final StackTraceElement element = new StackTraceElement(
+ info.getClassName(),
+ info.getMethodName(),
+ info.getFileName(),
+ Integer.parseInt(info.getLineNumber()));
+ final Thread thread = getThread(source.getThreadName());
+ final long threadId = thread != null ? thread.getId() : 0;
+ final int threadPriority = thread != null ? thread.getPriority() : 0;
+ event = Log4jLogEvent.newBuilder()
+ .setContextData(new SortedArrayStringMap(props))
+ .setLevel(OptionConverter.convertLevel(source.getLevel()))
+ .setLoggerFqcn(source.getFQNOfLoggerClass())
+ .setMarker(null)
+ .setMessage(newMessage)
+ .setSource(element)
+ .setLoggerName(source.getLoggerName())
+ .setThreadName(source.getThreadName())
+ .setThreadId(threadId)
+ .setThreadPriority(threadPriority)
+ .setThrown(source.getThrowableInformation().getThrowable())
+ .setTimeMillis(source.getTimeStamp())
+ .setNanoTime(0)
+ .build();
+ }
+ return new LogEventAdapter(event);
+ }
+ return source;
+ }
+
+ private Thread getThread(final String name) {
+ for (Thread thread : Thread.getAllStackTraces().keySet()) {
+ if (thread.getName().equals(name)) {
+ return thread;
+ }
+ }
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
new file mode 100644
index 00000000000..1fd4c9fcd59
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.java
@@ -0,0 +1,128 @@
+/*
+ * 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.log4j.rewrite;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+
+/**
+ * This policy rewrites events by adding
+ * a user-specified list of properties to the event.
+ * Existing properties are not modified.
+ *
+ * The combination of the RewriteAppender and this policy
+ * performs the same actions as the PropertyFilter from log4j 1.3.
+ *
+ */
+public class PropertyRewritePolicy implements RewritePolicy {
+ private Map properties = Collections.EMPTY_MAP;
+
+ public PropertyRewritePolicy() {}
+
+ /**
+ * Set a string representing the property name/value pairs.
+ *
+ * Form:
+ *
+ *
+ * propname1=propvalue1,propname2=propvalue2
+ *
+ *
+ * @param properties The properties.
+ */
+ public void setProperties(final String properties) {
+ final Map newMap = new HashMap<>();
+ final StringTokenizer pairs = new StringTokenizer(properties, ",");
+ while (pairs.hasMoreTokens()) {
+ final StringTokenizer entry = new StringTokenizer(pairs.nextToken(), "=");
+ newMap.put(
+ entry.nextElement().toString().trim(),
+ entry.nextElement().toString().trim());
+ }
+ synchronized (this) {
+ this.properties = newMap;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public LoggingEvent rewrite(final LoggingEvent source) {
+ if (!properties.isEmpty()) {
+ final Map rewriteProps =
+ source.getProperties() != null ? new HashMap<>(source.getProperties()) : new HashMap<>();
+ for (Map.Entry entry : properties.entrySet()) {
+ if (!rewriteProps.containsKey(entry.getKey())) {
+ rewriteProps.put(entry.getKey(), entry.getValue());
+ }
+ }
+ LogEvent event;
+ if (source instanceof LogEventAdapter) {
+ event = new Log4jLogEvent.Builder(((LogEventAdapter) source).getEvent())
+ .setContextData(new SortedArrayStringMap(rewriteProps))
+ .build();
+ } else {
+ final LocationInfo info = source.getLocationInformation();
+ final StackTraceElement element = new StackTraceElement(
+ info.getClassName(),
+ info.getMethodName(),
+ info.getFileName(),
+ Integer.parseInt(info.getLineNumber()));
+ final Thread thread = getThread(source.getThreadName());
+ final long threadId = thread != null ? thread.getId() : 0;
+ final int threadPriority = thread != null ? thread.getPriority() : 0;
+ event = Log4jLogEvent.newBuilder()
+ .setContextData(new SortedArrayStringMap(rewriteProps))
+ .setLevel(OptionConverter.convertLevel(source.getLevel()))
+ .setLoggerFqcn(source.getFQNOfLoggerClass())
+ .setMarker(null)
+ .setMessage(new SimpleMessage(source.getRenderedMessage()))
+ .setSource(element)
+ .setLoggerName(source.getLoggerName())
+ .setThreadName(source.getThreadName())
+ .setThreadId(threadId)
+ .setThreadPriority(threadPriority)
+ .setThrown(source.getThrowableInformation().getThrowable())
+ .setTimeMillis(source.getTimeStamp())
+ .setNanoTime(0)
+ .build();
+ }
+ return new LogEventAdapter(event);
+ }
+ return source;
+ }
+
+ private Thread getThread(final String name) {
+ for (Thread thread : Thread.getAllStackTraces().keySet()) {
+ if (thread.getName().equals(name)) {
+ return thread;
+ }
+ }
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
new file mode 100644
index 00000000000..36b051fc0c5
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/RewritePolicy.java
@@ -0,0 +1,36 @@
+/*
+ * 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.log4j.rewrite;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This interface is implemented to provide a rewrite
+ * strategy for RewriteAppender. RewriteAppender will
+ * call the rewrite method with a source logging event.
+ * The strategy may return that event, create a new event
+ * or return null to suppress the logging request.
+ */
+public interface RewritePolicy {
+ /**
+ * Rewrite a logging event.
+ * @param source a logging event that may be returned or
+ * used to create a new logging event.
+ * @return a logging event or null to suppress processing.
+ */
+ LoggingEvent rewrite(final LoggingEvent source);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/package-info.java
new file mode 100644
index 00000000000..154ce83725b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.20.1")
+package org.apache.log4j.rewrite;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
new file mode 100644
index 00000000000..8bc4a43f803
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
@@ -0,0 +1,73 @@
+/*
+ * 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.log4j.spi;
+
+import java.util.Enumeration;
+import org.apache.log4j.Appender;
+
+/**
+ * Interface for attaching appenders to objects.
+ */
+public interface AppenderAttachable {
+
+ /**
+ * Add an appender.
+ * @param newAppender The Appender to add.
+ */
+ void addAppender(Appender newAppender);
+
+ /**
+ * Get all previously added appenders as an Enumeration.
+ * @return The Enumeration of the Appenders.
+ */
+ Enumeration getAllAppenders();
+
+ /**
+ * Get an appender by name.
+ * @param name The name of the Appender.
+ * @return The Appender.
+ */
+ Appender getAppender(String name);
+
+ /**
+ * Returns true
if the specified appender is in list of
+ * attached, false
otherwise.
+ * @param appender The Appender to check.
+ * @return true if the Appender is attached.
+ *
+ * @since 1.2
+ */
+ boolean isAttached(Appender appender);
+
+ /**
+ * Remove all previously added appenders.
+ */
+ void removeAllAppenders();
+
+ /**
+ * Remove the appender passed as parameter from the list of appenders.
+ * @param appender The Appender to remove.
+ */
+ void removeAppender(Appender appender);
+
+ /**
+ * Remove the appender with the name passed as parameter from the
+ * list of appenders.
+ * @param name The name of the Appender to remove.
+ */
+ void removeAppender(String name);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java
new file mode 100644
index 00000000000..a3f790ec5b1
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Configurator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.log4j.spi;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Log4j 1.x Configurator interface.
+ */
+public interface Configurator {
+
+ /**
+ * Special level value signifying inherited behavior. The current value of this string constant is inherited .
+ * {@link #NULL} is a synonym.
+ */
+ public static final String INHERITED = "inherited";
+
+ /**
+ * Special level signifying inherited behavior, same as {@link #INHERITED}. The current value of this string constant
+ * is null .
+ */
+ public static final String NULL = "null";
+
+ /**
+ * Interpret a resource pointed by a InputStream and set up log4j accordingly.
+ *
+ * The configuration is done relative to the hierarchy
parameter.
+ *
+ * @param inputStream The InputStream to parse
+ * @param loggerRepository The hierarchy to operation upon.
+ *
+ * @since 1.2.17
+ */
+ void doConfigure(InputStream inputStream, final LoggerRepository loggerRepository);
+
+ /**
+ * Interpret a resource pointed by a URL and set up log4j accordingly.
+ *
+ * The configuration is done relative to the hierarchy
parameter.
+ *
+ * @param url The URL to parse
+ * @param loggerRepository The hierarchy to operation upon.
+ */
+ void doConfigure(URL url, final LoggerRepository loggerRepository);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java
new file mode 100644
index 00000000000..5b51f5dd7d8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java
@@ -0,0 +1,31 @@
+/*
+ * 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.log4j.spi;
+
+public class DefaultRepositorySelector implements RepositorySelector {
+
+ final LoggerRepository repository;
+
+ public DefaultRepositorySelector(final LoggerRepository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ public LoggerRepository getLoggerRepository() {
+ return repository;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java
new file mode 100644
index 00000000000..b18367feaa6
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorCode.java
@@ -0,0 +1,31 @@
+/*
+ * 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.log4j.spi;
+
+/**
+ * This interface defines commonly encountered error codes.
+ */
+public interface ErrorCode {
+
+ public final int GENERIC_FAILURE = 0;
+ public final int WRITE_FAILURE = 1;
+ public final int FLUSH_FAILURE = 2;
+ public final int CLOSE_FAILURE = 3;
+ public final int FILE_OPEN_FAILURE = 4;
+ public final int MISSING_LAYOUT = 5;
+ public final int ADDRESS_PARSE_FAILURE = 6;
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java
index 2e6410391b7..5e94d343b38 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ErrorHandler.java
@@ -1,25 +1,24 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
-
/**
* Appenders may delegate their error handling to
* ErrorHandlers
.
@@ -27,11 +26,6 @@
* Error handling is a particularly tedious to get right because by
* definition errors are hard to predict and to reproduce.
*
- *
- * Please take the time to contact the author in case you discover
- * that errors are not properly handled. You are most welcome to
- * suggest new error handling policies or criticize existing policies.
- *
*/
public interface ErrorHandler {
@@ -46,10 +40,9 @@ public interface ErrorHandler {
*/
void setLogger(Logger logger);
-
/**
* Equivalent to the {@link #error(String, Exception, int,
- * LoggingEvent)} with the the event parameter set to
+ * LoggingEvent)} with the event parameter set to
* null
.
*
* @param message The message associated with the error.
@@ -95,4 +88,3 @@ public interface ErrorHandler {
*/
void setBackupAppender(Appender appender);
}
-
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
index 9bba50cbcf8..12026824a9f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/Filter.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
@@ -21,6 +21,16 @@
*/
public abstract class Filter {
+ static {
+ boolean temp;
+ try {
+ temp = Class.forName("org.apache.logging.log4j.core.Filter") != null;
+ } catch (Exception | LinkageError e) {
+ temp = false;
+ }
+ isCorePresent = temp;
+ }
+
/**
* The log event must be dropped immediately without consulting
* with the remaining filters, if any, in the chain.
@@ -47,14 +57,16 @@ public abstract class Filter {
@Deprecated
public Filter next;
+ private static final boolean isCorePresent;
+
/**
* Usually filters options become active when set. We provide a
* default do-nothing implementation for convenience.
*/
public void activateOptions() {
+ // noop
}
-
/**
* If the decision is DENY
, then the event will be
* dropped. If the decision is NEUTRAL
, then the next
@@ -82,5 +94,4 @@ public void setNext(final Filter next) {
public Filter getNext() {
return next;
}
-
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java
index 286ba541a03..db39828f6a1 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
@@ -20,10 +20,10 @@
import org.apache.log4j.Category;
/**
- Listen to events occurring within a Hierarchy.
-
- @since 1.2
-
+ * Listen to events occurring within a Hierarchy.
+ *
+ * @since 1.2
+ *
*/
public interface HierarchyEventListener {
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
new file mode 100644
index 00000000000..d705b856f55
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LocationInfo.java
@@ -0,0 +1,141 @@
+/*
+ * 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.log4j.spi;
+
+import java.io.Serializable;
+import java.util.Objects;
+import org.apache.logging.log4j.core.util.Integers;
+
+/**
+ * The internal representation of caller location information.
+ *
+ * @since 0.8.3
+ */
+public class LocationInfo implements Serializable {
+
+ /**
+ * When location information is not available the constant NA
is returned. Current value of this string
+ * constant is ? .
+ */
+ public static final String NA = "?";
+
+ static final long serialVersionUID = -1325822038990805636L;
+
+ private final StackTraceElement stackTraceElement;
+
+ public String fullInfo;
+
+ /**
+ * Constructs a new instance.
+ */
+ public LocationInfo(final StackTraceElement stackTraceElement) {
+ this.stackTraceElement = Objects.requireNonNull(stackTraceElement, "stackTraceElement");
+ this.fullInfo = stackTraceElement.toString();
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param file source file name
+ * @param declaringClass class name
+ * @param methodName method
+ * @param line source line number
+ *
+ * @since 1.2.15
+ */
+ public LocationInfo(final String file, final String declaringClass, final String methodName, final String line) {
+ this(new StackTraceElement(declaringClass, methodName, file, Integer.parseInt(line)));
+ }
+
+ /**
+ * Constructs a new instance.
+ */
+ public LocationInfo(final Throwable throwable, final String fqnOfCallingClass) {
+ String declaringClass = null, methodName = null, file = null, line = null;
+ if (throwable != null && fqnOfCallingClass != null) {
+ final StackTraceElement[] elements = throwable.getStackTrace();
+ String prevClass = NA;
+ for (int i = elements.length - 1; i >= 0; i--) {
+ final String thisClass = elements[i].getClassName();
+ if (fqnOfCallingClass.equals(thisClass)) {
+ final int caller = i + 1;
+ if (caller < elements.length) {
+ declaringClass = prevClass;
+ methodName = elements[caller].getMethodName();
+ file = elements[caller].getFileName();
+ if (file == null) {
+ file = NA;
+ }
+ final int lineNo = elements[caller].getLineNumber();
+ if (lineNo < 0) {
+ line = NA;
+ } else {
+ line = String.valueOf(lineNo);
+ }
+ final StringBuilder builder = new StringBuilder();
+ builder.append(declaringClass);
+ builder.append(".");
+ builder.append(methodName);
+ builder.append("(");
+ builder.append(file);
+ builder.append(":");
+ builder.append(line);
+ builder.append(")");
+ this.fullInfo = builder.toString();
+ }
+ break;
+ }
+ prevClass = thisClass;
+ }
+ }
+ if (declaringClass != null && methodName != null) {
+ this.stackTraceElement = new StackTraceElement(declaringClass, methodName, file, Integers.parseInt(line));
+ this.fullInfo = stackTraceElement.toString();
+ } else {
+ this.stackTraceElement = null;
+ this.fullInfo = null;
+ }
+ }
+
+ /**
+ * Gets the fully qualified class name of the caller making the logging request.
+ */
+ public String getClassName() {
+ return stackTraceElement != null ? stackTraceElement.getClassName() : NA;
+ }
+
+ /**
+ * Gets the file name of the caller.
+ */
+ public String getFileName() {
+ return stackTraceElement != null ? stackTraceElement.getFileName() : NA;
+ }
+
+ /**
+ * Gets the line number of the caller.
+ */
+ public String getLineNumber() {
+ return stackTraceElement != null ? Integer.toString(stackTraceElement.getLineNumber()) : NA;
+ }
+
+ /**
+ * Gets the method name of the caller.
+ */
+ public String getMethodName() {
+ return stackTraceElement != null ? stackTraceElement.getMethodName() : NA;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
index e2f37080572..1cca0912d4c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerFactory.java
@@ -1,25 +1,24 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
import org.apache.log4j.Logger;
/**
- *
* Implement this interface to create new instances of Logger or a sub-class of Logger.
*
*
@@ -29,5 +28,4 @@
public interface LoggerFactory {
Logger makeNewLoggerInstance(String name);
-
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerRepository.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerRepository.java
index 812280f747b..3820f075d86 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerRepository.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggerRepository.java
@@ -1,23 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
import java.util.Enumeration;
-
import org.apache.log4j.Appender;
import org.apache.log4j.Category;
import org.apache.log4j.Level;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
index 5f4b1727b90..71b9cb2246c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
@@ -1,23 +1,243 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
+import java.util.Map;
+import java.util.Set;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.bridge.LogEventAdapter;
+
/**
- * No-op version of Log4j 1.2 LoggingEvent.
+ * No-op version of Log4j 1.2 LoggingEvent. This class is not directly used by Log4j 1.x clients but is used by
+ * the Log4j 2 LogEvent adapter to be compatible with Log4j 1.x components.
*/
public class LoggingEvent {
+
+ /**
+ * Returns the time when the application started, in milliseconds
+ * elapsed since 01.01.1970.
+ * @return the JVM start time.
+ */
+ public static long getStartTime() {
+ return LogEventAdapter.getJvmStartTime();
+ }
+
+ /**
+ * The number of milliseconds elapsed from 1/1/1970 until logging event was created.
+ */
+ public final long timeStamp;
+
+ /**
+ * Constructs a new instance.
+ */
+ public LoggingEvent() {
+ timeStamp = System.currentTimeMillis();
+ }
+
+ /**
+ * Create new instance.
+ *
+ * @since 1.2.15
+ * @param fqnOfCategoryClass Fully qualified class name of Logger implementation.
+ * @param logger The logger generating this event.
+ * @param timeStamp the timestamp of this logging event
+ * @param level The level of this event.
+ * @param message The message of this event.
+ * @param threadName thread name
+ * @param throwable The throwable of this event.
+ * @param ndc Nested diagnostic context
+ * @param info Location info
+ * @param properties MDC properties
+ */
+ public LoggingEvent(
+ final String fqnOfCategoryClass,
+ final Category logger,
+ final long timeStamp,
+ final Level level,
+ final Object message,
+ final String threadName,
+ final ThrowableInformation throwable,
+ final String ndc,
+ final LocationInfo info,
+ final Map properties) {
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ * Instantiate a LoggingEvent from the supplied parameters.
+ *
+ *
+ * Except {@link #timeStamp} all the other fields of LoggingEvent
are filled when actually needed.
+ *
+ *
+ * @param logger The logger generating this event.
+ * @param timeStamp the timestamp of this logging event
+ * @param level The level of this event.
+ * @param message The message of this event.
+ * @param throwable The throwable of this event.
+ */
+ public LoggingEvent(
+ String fqnOfCategoryClass,
+ Category logger,
+ long timeStamp,
+ Priority level,
+ Object message,
+ Throwable throwable) {
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ * Instantiate a LoggingEvent from the supplied parameters.
+ *
+ *
+ * Except {@link #timeStamp} all the other fields of LoggingEvent
are filled when actually needed.
+ *
+ *
+ * @param logger The logger generating this event.
+ * @param level The level of this event.
+ * @param message The message of this event.
+ * @param throwable The throwable of this event.
+ */
+ public LoggingEvent(
+ final String fqnOfCategoryClass,
+ final Category logger,
+ final Priority level,
+ final Object message,
+ final Throwable throwable) {
+ timeStamp = System.currentTimeMillis();
+ }
+
+ public String getFQNOfLoggerClass() {
+ return null;
+ }
+
+ /**
+ * Return the level of this event. Use this form instead of directly
+ * accessing the level
field.
+ * @return Always returns null.
+ */
+ public Level getLevel() {
+ return null;
+ }
+
+ /**
+ * Set the location information for this logging event. The collected
+ * information is cached for future use.
+ * @return Always returns null.
+ */
+ public LocationInfo getLocationInformation() {
+ return null;
+ }
+
+ /**
+ * Gets the logger of the event.
+ * Use should be restricted to cloning events.
+ * @return Always returns null.
+ * @since 1.2.15
+ */
+ public Category getLogger() {
+ return null;
+ }
+
+ /**
+ * Return the name of the logger. Use this form instead of directly
+ * accessing the categoryName
field.
+ * @return Always returns null.
+ */
+ public String getLoggerName() {
+ return null;
+ }
+
+ public Object getMDC(final String key) {
+ return null;
+ }
+
+ /**
+ * Obtain a copy of this thread's MDC prior to serialization or
+ * asynchronous logging.
+ */
+ public void getMDCCopy() {}
+
+ /**
+ * Return the message for this logging event.
+ *
+ *
Before serialization, the returned object is the message
+ * passed by the user to generate the logging event. After
+ * serialization, the returned value equals the String form of the
+ * message possibly after object rendering.
+ * @return Always returns null.
+ * @since 1.1 */
+ public Object getMessage() {
+ return null;
+ }
+
+ public String getNDC() {
+ return null;
+ }
+
+ public Map getProperties() {
+ return null;
+ }
+
+ public String getProperty(final String key) {
+ return null;
+ }
+
+ public Set getPropertyKeySet() {
+ return null;
+ }
+
+ public String getRenderedMessage() {
+ return null;
+ }
+
+ public String getThreadName() {
+ return null;
+ }
+
+ /**
+ * Returns the throwable information contained within this
+ * event. May be null
if there is no such information.
+ *
+ *
Note that the {@link Throwable} object contained within a
+ * {@link ThrowableInformation} does not survive serialization.
+ * @return Always returns null.
+ * @since 1.1 */
+ public ThrowableInformation getThrowableInformation() {
+ return null;
+ }
+
+ /**
+ * Return this event's throwable's string[] representation.
+ * @return Always returns null.
+ */
+ public String[] getThrowableStrRep() {
+ return null;
+ }
+
+ public long getTimeStamp() {
+ return 0;
+ }
+
+ public Object removeProperty(final String propName) {
+ return null;
+ }
+
+ public void setProperty(final String propName, final String propValue) {}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLogger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLogger.java
new file mode 100644
index 00000000000..c47a30f019e
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLogger.java
@@ -0,0 +1,266 @@
+/*
+ * 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.log4j.spi;
+
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.Vector;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+
+/**
+ * No-operation implementation of Logger used by NOPLoggerRepository.
+ *
+ * @since 1.2.15
+ */
+public final class NOPLogger extends Logger {
+
+ /**
+ * Create instance of Logger.
+ *
+ * @param repo repository, may not be null.
+ * @param name name, may not be null, use "root" for root logger.
+ */
+ public NOPLogger(final NOPLoggerRepository repo, final String name) {
+ super(name);
+ this.repository = repo;
+ this.level = Level.OFF;
+ this.parent = this;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addAppender(final Appender newAppender) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void assertLog(final boolean assertion, final String msg) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void callAppenders(final LoggingEvent event) {
+ // NOP
+ }
+
+ void closeNestedAppenders() {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void debug(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void debug(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void error(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void error(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void fatal(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void fatal(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Enumeration getAllAppenders() {
+ return new Vector<>().elements();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Appender getAppender(final String name) {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Priority getChainedPriority() {
+ return getEffectiveLevel();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Level getEffectiveLevel() {
+ return Level.OFF;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ResourceBundle getResourceBundle() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void info(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void info(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isAttached(final Appender appender) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isEnabledFor(final Priority level) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isInfoEnabled() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isTraceEnabled() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void l7dlog(final Priority priority, final String key, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void log(final Priority priority, final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void log(final Priority priority, final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void log(final String callerFQCN, final Priority level, final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void removeAllAppenders() {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void removeAppender(final Appender appender) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void removeAppender(final String name) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setLevel(final Level level) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setPriority(final Priority priority) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setResourceBundle(final ResourceBundle bundle) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void trace(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void trace(final Object message, final Throwable t) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void warn(final Object message) {
+ // NOP
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void warn(final Object message, final Throwable t) {
+ // NOP
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java
new file mode 100644
index 00000000000..77031c75070
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java
@@ -0,0 +1,153 @@
+/*
+ * 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.log4j.spi;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+/**
+ * No-operation implementation of LoggerRepository which is used when LogManager.repositorySelector is erroneously
+ * nulled during class reloading.
+ *
+ * @since 1.2.15
+ */
+public final class NOPLoggerRepository implements LoggerRepository {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addHierarchyEventListener(final HierarchyEventListener listener) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void emitNoAppenderWarning(final Category cat) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger exists(final String name) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void fireAddAppenderEvent(final Category logger, final Appender appender) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration getCurrentCategories() {
+ return getCurrentLoggers();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Enumeration getCurrentLoggers() {
+ return new Vector<>().elements();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger getLogger(final String name) {
+ return new NOPLogger(this, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger getLogger(final String name, final LoggerFactory factory) {
+ return new NOPLogger(this, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Logger getRootLogger() {
+ return new NOPLogger(this, "root");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Level getThreshold() {
+ return Level.OFF;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isDisabled(final int level) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void resetConfiguration() {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setThreshold(final Level level) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setThreshold(final String val) {
+ // NOP
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown() {
+ // NOP
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/OptionHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/OptionHandler.java
index 1b855bc0ba9..b9ffe7e4d33 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/OptionHandler.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/OptionHandler.java
@@ -1,22 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
-
/**
* Log4j 1 Interface for dealing with configuration. Ignored in Log4j 2.
*/
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RendererSupport.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RendererSupport.java
new file mode 100644
index 00000000000..9a89ccc987f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RendererSupport.java
@@ -0,0 +1,27 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererMap;
+
+public interface RendererSupport {
+
+ public RendererMap getRendererMap();
+
+ public void setRenderer(Class renderedClass, ObjectRenderer renderer);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java
index 95666594a85..460dc6993fe 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RepositorySelector.java
@@ -1,42 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.spi;
/**
-
- The LogManager
uses one (and only one)
- RepositorySelector
implementation to select the
- {@link org.apache.log4j.spi.LoggerRepository} for a particular application context.
-
-
It is the responsibility of the RepositorySelector
- implementation to track the application context. Log4j makes no
- assumptions about the application context or on its management.
-
-
See also {@link org.apache.log4j.LogManager LogManager}.
-
- @since 1.2
-
+ *
+ * The LogManager
uses one (and only one) RepositorySelector
implementation to select the
+ * {@link LoggerRepository} for a particular application context.
+ *
+ *
+ * It is the responsibility of the RepositorySelector
implementation to track the application context.
+ * Log4j makes no assumptions about the application context or on its management.
+ *
+ *
+ *
+ * See also {@link org.apache.log4j.LogManager LogManager}.
+ *
+ * @since 1.2
*/
public interface RepositorySelector {
/**
- * Returns a {@link org.apache.log4j.spi.LoggerRepository} depending on the
- * context. Implementers must make sure that a valid (non-null)
+ * Gets a {@link LoggerRepository} depending on the context. Implementers must make sure that a valid (non-null)
* LoggerRepository is returned.
+ *
* @return a LoggerRepository.
*/
LoggerRepository getLoggerRepository();
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java
new file mode 100644
index 00000000000..9773bb246a8
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/RootLogger.java
@@ -0,0 +1,63 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+
+/**
+ * RootLogger sits at the top of the logger hierarchy. It is a regular logger except that it provides several guarantees.
+ *
+ * First, it cannot be assigned a null
level. Second, since root logger cannot have a parent, the
+ * {@link #getChainedLevel} method always returns the value of the level field without walking the hierarchy.
+ *
+ */
+public final class RootLogger extends Logger {
+
+ /**
+ * The root logger names itself as "root". However, the root logger cannot be retrieved by name.
+ */
+ public RootLogger(final Level level) {
+ // The Log4j 1 root logger name is "root".
+ // The Log4j 2 root logger name is "".
+ super("root");
+ setLevel(level);
+ }
+
+ /**
+ * Gets the assigned level value without walking the logger hierarchy.
+ */
+ public final Level getChainedLevel() {
+ return getLevel();
+ }
+
+ /**
+ * Sets the log level.
+ *
+ * Setting a null value to the level of the root logger may have catastrophic results. We prevent this here.
+ *
+ * @since 0.8.3
+ */
+ public final void setLevel(final Level level) {
+ if (level == null) {
+ LogLog.error("You have tried to set a null level to root.", new Throwable());
+ } else {
+ super.setLevel(level);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
new file mode 100644
index 00000000000..a6acbed90e3
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
@@ -0,0 +1,96 @@
+/*
+ * 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.log4j.spi;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.List;
+import org.apache.log4j.Category;
+import org.apache.logging.log4j.util.Strings;
+
+/**
+ * Log4j's internal representation of throwables.
+ */
+public class ThrowableInformation implements Serializable {
+
+ static final long serialVersionUID = -4748765566864322735L;
+
+ private transient Throwable throwable;
+ private transient Category category;
+ private String[] rep;
+ private static final Method TO_STRING_LIST;
+
+ static {
+ Method method = null;
+ try {
+ final Class> throwables = Class.forName("org.apache.logging.log4j.core.util.Throwables");
+ method = throwables.getMethod("toStringList", Throwable.class);
+ } catch (ClassNotFoundException | NoSuchMethodException ex) {
+ // Ignore the exception if Log4j-core is not present.
+ }
+ TO_STRING_LIST = method;
+ }
+
+ /**
+ * Constructs new instance.
+ *
+ * @since 1.2.15
+ * @param r String representation of throwable.
+ */
+ public ThrowableInformation(final String[] r) {
+ this.rep = r != null ? r.clone() : null;
+ }
+
+ /**
+ * Constructs new instance.
+ */
+ public ThrowableInformation(final Throwable throwable) {
+ this.throwable = throwable;
+ }
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param throwable throwable, may not be null.
+ * @param category category used to obtain ThrowableRenderer, may be null.
+ * @since 1.2.16
+ */
+ public ThrowableInformation(final Throwable throwable, final Category category) {
+ this(throwable);
+ this.category = category;
+ this.rep = null;
+ }
+
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ public synchronized String[] getThrowableStrRep() {
+ if (TO_STRING_LIST != null && throwable != null) {
+ try {
+ @SuppressWarnings("unchecked")
+ final List elements = (List) TO_STRING_LIST.invoke(null, throwable);
+ if (elements != null) {
+ return elements.toArray(Strings.EMPTY_ARRAY);
+ }
+ } catch (final ReflectiveOperationException ex) {
+ // Ignore the exception.
+ }
+ }
+ return rep;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
new file mode 100644
index 00000000000..efaab324a22
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
@@ -0,0 +1,34 @@
+/*
+ * 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.log4j.spi;
+
+/**
+ * Implemented by classes that render instances of java.lang.Throwable (exceptions and errors) into a string
+ * representation.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRenderer {
+
+ /**
+ * Render Throwable.
+ *
+ * @param t throwable, may not be null.
+ * @return String representation.
+ */
+ public String[] doRender(Throwable t);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
new file mode 100644
index 00000000000..5c28778808b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
@@ -0,0 +1,38 @@
+/*
+ * 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.log4j.spi;
+
+/**
+ * Implemented by logger repositories that support configurable rendering of Throwables.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRendererSupport {
+ /**
+ * Get throwable renderer.
+ *
+ * @return throwable renderer, may be null.
+ */
+ ThrowableRenderer getThrowableRenderer();
+
+ /**
+ * Set throwable renderer.
+ *
+ * @param renderer renderer, may be null.
+ */
+ void setThrowableRenderer(ThrowableRenderer renderer);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java
new file mode 100644
index 00000000000..9f9a95c9997
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.log4j.spi;
+
+/**
+ * Implementors decide when to perform an appender specific action.
+ *
+ *
+ * For example, the {@code org.apache.log4j.net.SMTPAppender} sends an email when the
+ * {@link #isTriggeringEvent(LoggingEvent)} method returns {@code true} and adds the event to an internal buffer when
+ * the returned result is {@code false}.
+ *
+ *
+ * @since version 1.0
+ */
+public interface TriggeringEventEvaluator {
+
+ /**
+ * Tests if this the triggering event.
+ *
+ * @param event The vent to test.
+ * @return Whether this the triggering event.
+ */
+ public boolean isTriggeringEvent(LoggingEvent event);
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/package-info.java
index a7648dc661f..4aeced5ea93 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/package-info.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/package-info.java
@@ -17,4 +17,9 @@
/**
* Log4j 1.x compatibility layer.
*/
+@Export
+@Version("2.20.1")
package org.apache.log4j.spi;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/DenyAllFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/DenyAllFilter.java
new file mode 100644
index 00000000000..901ceeb7132
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/DenyAllFilter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Denies all logging events.
+ *
+ *
+ * You can add this filter to the end of a filter chain to switch from the default "accept all unless instructed
+ * otherwise" filtering behavior to a "deny all unless instructed otherwise" behavior.
+ *
+ *
+ * @since 0.9.0
+ */
+public class DenyAllFilter extends Filter {
+
+ /**
+ * Always returns the integer constant {@link Filter#DENY} regardless of the {@link LoggingEvent} parameter.
+ *
+ * @param event The LoggingEvent to filter.
+ * @return Always returns {@link Filter#DENY}.
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ return Filter.DENY;
+ }
+
+ /**
+ * Returns null
as there are no options.
+ *
+ * @deprecated We now use JavaBeans introspection to configure components. Options strings are no longer needed.
+ */
+ @Deprecated
+ public String[] getOptionStrings() {
+ return null;
+ }
+
+ /**
+ * No options to set.
+ *
+ * @deprecated Use the setter method for the option directly instead of the generic setOption
method.
+ */
+ @Deprecated
+ public void setOption(final String key, final String value) {
+ // noop
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java
new file mode 100644
index 00000000000..593f0411d8f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java
@@ -0,0 +1,124 @@
+/*
+ * 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.log4j.varia;
+
+import java.io.InterruptedIOException;
+import java.util.Vector;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * An ErrorHandler with a secondary appender. This secondary appender takes over if the primary appender fails for
+ * whatever reason.
+ *
+ *
+ * The error message is printed on System.err
, and logged in the new secondary appender.
+ *
+ */
+public class FallbackErrorHandler implements ErrorHandler {
+
+ Appender backup;
+ Appender primary;
+ Vector loggers;
+
+ public FallbackErrorHandler() {
+ // noop
+ }
+
+ /**
+ * No options to activate.
+ */
+ public void activateOptions() {
+ // noop
+ }
+
+ /**
+ * Print a the error message passed as parameter on System.err
.
+ */
+ @Override
+ public void error(final String message) {
+ // if(firstTime) {
+ // LogLog.error(message);
+ // firstTime = false;
+ // }
+ }
+
+ /**
+ * Prints the message and the stack trace of the exception on System.err
.
+ */
+ @Override
+ public void error(final String message, final Exception e, final int errorCode) {
+ error(message, e, errorCode, null);
+ }
+
+ /**
+ * Prints the message and the stack trace of the exception on System.err
.
+ */
+ @Override
+ public void error(final String message, final Exception e, final int errorCode, final LoggingEvent event) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.debug("FB: The following error reported: " + message, e);
+ LogLog.debug("FB: INITIATING FALLBACK PROCEDURE.");
+ if (loggers != null) {
+ for (int i = 0; i < loggers.size(); i++) {
+ final Logger l = (Logger) loggers.elementAt(i);
+ LogLog.debug("FB: Searching for [" + primary.getName() + "] in logger [" + l.getName() + "].");
+ LogLog.debug("FB: Replacing [" + primary.getName() + "] by [" + backup.getName() + "] in logger ["
+ + l.getName() + "].");
+ l.removeAppender(primary);
+ LogLog.debug("FB: Adding appender [" + backup.getName() + "] to logger " + l.getName());
+ l.addAppender(backup);
+ }
+ }
+ }
+
+ /**
+ * The appender to which this error handler is attached.
+ */
+ @Override
+ public void setAppender(final Appender primary) {
+ LogLog.debug("FB: Setting primary appender to [" + primary.getName() + "].");
+ this.primary = primary;
+ }
+
+ /**
+ * Set the backup appender.
+ */
+ @Override
+ public void setBackupAppender(final Appender backup) {
+ LogLog.debug("FB: Setting backup appender to [" + backup.getName() + "].");
+ this.backup = backup;
+ }
+
+ /**
+ * Adds the logger passed as parameter to the list of loggers that we need to search for in case of appender
+ * failure.
+ */
+ @Override
+ public void setLogger(final Logger logger) {
+ LogLog.debug("FB: Adding logger [" + logger.getName() + "].");
+ if (loggers == null) {
+ loggers = new Vector();
+ }
+ loggers.addElement(logger);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java
new file mode 100644
index 00000000000..fb7cb77ccbf
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java
@@ -0,0 +1,90 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Simple filter based on level matching.
+ *
+ *
+ * The filter admits two options LevelToMatch and AcceptOnMatch . If there is an exact match between the
+ * value of the LevelToMatch option and the level of the {@link LoggingEvent}, then the {@link #decide} method
+ * returns {@link Filter#ACCEPT} in case the AcceptOnMatch option value is set to true
, if it is
+ * false
then {@link Filter#DENY} is returned. If there is no match, {@link Filter#NEUTRAL} is returned.
+ *
+ *
+ * @since 1.2
+ */
+public class LevelMatchFilter extends Filter {
+
+ /**
+ * Do we return ACCEPT when a match occurs. Default is true
.
+ */
+ boolean acceptOnMatch = true;
+
+ /**
+ */
+ Level levelToMatch;
+
+ /**
+ * Return the decision of this filter.
+ *
+ * Returns {@link Filter#NEUTRAL} if the LevelToMatch option is not set or if there is not match. Otherwise, if
+ * there is a match, then the returned decision is {@link Filter#ACCEPT} if the AcceptOnMatch property is set to
+ * true
. The returned decision is {@link Filter#DENY} if the AcceptOnMatch property is set to false.
+ *
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ if (this.levelToMatch == null) {
+ return Filter.NEUTRAL;
+ }
+
+ boolean matchOccured = false;
+ if (this.levelToMatch.equals(event.getLevel())) {
+ matchOccured = true;
+ }
+
+ if (matchOccured) {
+ if (this.acceptOnMatch) {
+ return Filter.ACCEPT;
+ }
+ return Filter.DENY;
+ }
+ return Filter.NEUTRAL;
+ }
+
+ public boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ public String getLevelToMatch() {
+ return levelToMatch == null ? null : levelToMatch.toString();
+ }
+
+ public void setAcceptOnMatch(final boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ public void setLevelToMatch(final String level) {
+ levelToMatch = OptionConverter.toLevel(level, null);
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java
new file mode 100644
index 00000000000..546d2fa409f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java
@@ -0,0 +1,130 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This is a very simple filter based on level matching, which can be used to reject messages with priorities outside a
+ * certain range.
+ *
+ * The filter admits three options LevelMin , LevelMax and AcceptOnMatch .
+ *
+ *
+ * If the level of the {@link LoggingEvent} is not between Min and Max (inclusive), then {@link Filter#DENY} is
+ * returned.
+ *
+ *
+ * If the Logging event level is within the specified range, then if AcceptOnMatch is true, {@link Filter#ACCEPT}
+ * is returned, and if AcceptOnMatch is false, {@link Filter#NEUTRAL} is returned.
+ *
+ *
+ * If LevelMin
w is not defined, then there is no minimum acceptable level (ie a level is never rejected for
+ * being too "low"/unimportant). If LevelMax
is not defined, then there is no maximum acceptable level (ie
+ * a level is never rejected for beeing too "high"/important).
+ *
+ *
+ * Refer to the {@link org.apache.log4j.AppenderSkeleton#setThreshold setThreshold} method available to all
+ * appenders extending {@link org.apache.log4j.AppenderSkeleton} for a more convenient way to filter out events by
+ * level.
+ *
+ */
+public class LevelRangeFilter extends Filter {
+
+ /**
+ * Do we return ACCEPT when a match occurs. Default is false
, so that later filters get run by default
+ */
+ boolean acceptOnMatch;
+
+ Level levelMin;
+ Level levelMax;
+
+ /**
+ * Return the decision of this filter.
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ if (this.levelMin != null) {
+ if (!event.getLevel().isGreaterOrEqual(levelMin)) {
+ // level of event is less than minimum
+ return Filter.DENY;
+ }
+ }
+
+ if (this.levelMax != null) {
+ if (event.getLevel().toInt() > levelMax.toInt()) {
+ // level of event is greater than maximum
+ // Alas, there is no Level.isGreater method. and using
+ // a combo of isGreaterOrEqual && !Equal seems worse than
+ // checking the int values of the level objects..
+ return Filter.DENY;
+ }
+ }
+
+ if (acceptOnMatch) {
+ // this filter set up to bypass later filters and always return
+ // accept if level in range
+ return Filter.ACCEPT;
+ }
+ // event is ok for this filter; allow later filters to have a look..
+ return Filter.NEUTRAL;
+ }
+
+ /**
+ * Get the value of the AcceptOnMatch
option.
+ */
+ public boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ /**
+ * Get the value of the LevelMax
option.
+ */
+ public Level getLevelMax() {
+ return levelMax;
+ }
+
+ /**
+ * Get the value of the LevelMin
option.
+ */
+ public Level getLevelMin() {
+ return levelMin;
+ }
+
+ /**
+ * Set the AcceptOnMatch
option.
+ */
+ public void setAcceptOnMatch(final boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ /**
+ * Set the LevelMax
option.
+ */
+ public void setLevelMax(final Level levelMax) {
+ this.levelMax = levelMax;
+ }
+
+ /**
+ * Set the LevelMin
option.
+ */
+ public void setLevelMin(final Level levelMin) {
+ this.levelMin = levelMin;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/NullAppender.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/NullAppender.java
new file mode 100644
index 00000000000..4733c5a2a17
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/NullAppender.java
@@ -0,0 +1,86 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A NullAppender never outputs a message to any device.
+ */
+public class NullAppender extends AppenderSkeleton {
+
+ private static final NullAppender INSTANCE = new NullAppender();
+
+ /**
+ * Whenever you can, use this method to retreive an instance instead of instantiating a new one with new
.
+ */
+ public static NullAppender getNullAppender() {
+ return INSTANCE;
+ }
+
+ public NullAppender() {
+ // noop
+ }
+
+ /**
+ * There are no options to acticate.
+ */
+ @Override
+ public void activateOptions() {
+ // noop
+ }
+
+ /**
+ * Does not do anything.
+ */
+ @Override
+ protected void append(final LoggingEvent event) {
+ // noop
+ }
+
+ @Override
+ public void close() {
+ // noop
+ }
+
+ /**
+ * Does not do anything.
+ */
+ @Override
+ public void doAppend(final LoggingEvent event) {
+ // noop
+ }
+
+ /**
+ * Whenever you can, use this method to retreive an instance instead of instantiating a new one with new
.
+ *
+ * @deprecated Use getNullAppender instead. getInstance should have been static.
+ */
+ @Deprecated
+ public NullAppender getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * NullAppenders do not need a layout.
+ */
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
new file mode 100644
index 00000000000..d1b646e015d
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.log4j.varia;
+
+import java.io.InputStream;
+import java.net.URL;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+
+public class ReloadingPropertyConfigurator implements Configurator {
+
+ PropertyConfigurator delegate = new PropertyConfigurator();
+
+ public ReloadingPropertyConfigurator() {}
+
+ /**
+ * @since 1.2.17
+ */
+ @Override
+ public void doConfigure(final InputStream inputStream, final LoggerRepository repository) {
+ // noop
+ }
+
+ @Override
+ public void doConfigure(final URL url, final LoggerRepository repository) {
+ // noop
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/StringMatchFilter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/StringMatchFilter.java
new file mode 100644
index 00000000000..b1d60188a26
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/StringMatchFilter.java
@@ -0,0 +1,105 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Simple filter based on string matching.
+ *
+ *
+ * The filter admits two options StringToMatch and AcceptOnMatch . If there is a match between the value of
+ * the StringToMatch option and the message of the {@link org.apache.log4j.spi.LoggingEvent}, then the
+ * {@link #decide(LoggingEvent)} method returns {@link org.apache.log4j.spi.Filter#ACCEPT} if the AcceptOnMatch
+ * option value is true, if it is false then {@link org.apache.log4j.spi.Filter#DENY} is returned. If there is no match,
+ * {@link org.apache.log4j.spi.Filter#NEUTRAL} is returned.
+ *
+ *
+ * @since 0.9.0
+ */
+public class StringMatchFilter extends Filter {
+
+ /**
+ * @deprecated Options are now handled using the JavaBeans paradigm. This constant is not longer needed and will be
+ * removed in the near term.
+ */
+ @Deprecated
+ public static final String STRING_TO_MATCH_OPTION = "StringToMatch";
+
+ /**
+ * @deprecated Options are now handled using the JavaBeans paradigm. This constant is not longer needed and will be
+ * removed in the near term.
+ */
+ @Deprecated
+ public static final String ACCEPT_ON_MATCH_OPTION = "AcceptOnMatch";
+
+ boolean acceptOnMatch = true;
+ String stringToMatch;
+
+ /**
+ * Returns {@link Filter#NEUTRAL} is there is no string match.
+ */
+ @Override
+ public int decide(final LoggingEvent event) {
+ final String msg = event.getRenderedMessage();
+ if (msg == null || stringToMatch == null) {
+ return Filter.NEUTRAL;
+ }
+ if (msg.indexOf(stringToMatch) == -1) {
+ return Filter.NEUTRAL;
+ }
+ return acceptOnMatch ? Filter.ACCEPT : Filter.DENY;
+ }
+
+ public boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ /**
+ * @deprecated We now use JavaBeans introspection to configure components. Options strings are no longer needed.
+ */
+ @Deprecated
+ public String[] getOptionStrings() {
+ return new String[] {STRING_TO_MATCH_OPTION, ACCEPT_ON_MATCH_OPTION};
+ }
+
+ public String getStringToMatch() {
+ return stringToMatch;
+ }
+
+ public void setAcceptOnMatch(final boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ /**
+ * @deprecated Use the setter method for the option directly instead of the generic setOption
method.
+ */
+ @Deprecated
+ public void setOption(final String key, final String value) {
+ if (key.equalsIgnoreCase(STRING_TO_MATCH_OPTION)) {
+ stringToMatch = value;
+ } else if (key.equalsIgnoreCase(ACCEPT_ON_MATCH_OPTION)) {
+ acceptOnMatch = OptionConverter.toBoolean(value, acceptOnMatch);
+ }
+ }
+
+ public void setStringToMatch(final String s) {
+ stringToMatch = s;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/varia/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/package-info.java
new file mode 100644
index 00000000000..c8c79ec86a2
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/varia/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.20.1")
+package org.apache.log4j.varia;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
index 04a45551e03..5aff69da56f 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
@@ -1,80 +1,187 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.xml;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
+import java.io.StringWriter;
import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Properties;
-
import javax.xml.parsers.FactoryConfigurationError;
-
+import org.apache.log4j.LogManager;
import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.LoggerRepository;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.net.UrlConnectionFactory;
+import org.apache.logging.log4j.core.util.IOUtils;
+import org.apache.logging.log4j.util.PropertiesUtil;
import org.w3c.dom.Element;
/**
+ * Use this class to initialize the log4j environment using a DOM tree.
+ *
+ *
+ * The DTD is specified in log4j.dtd .
+ *
+ *
+ * Sometimes it is useful to see how log4j is reading configuration files. You can enable log4j internal logging by
+ * defining the log4j.debug variable on the java command line. Alternatively, set the debug
+ * attribute in the log4j:configuration
element. As in
+ *
+ *
+ * <log4j:configuration debug="true" xmlns:log4j="http://jakarta.apache.org/log4j/">
+ * ...
+ * </log4j:configuration>
+ *
*
+ *
+ * There are sample XML files included in the package.
+ *
+ * @since 0.8.3
*/
public class DOMConfigurator {
- public void doConfigure(final String filename, final LoggerRepository repository) {
+ private static boolean isFullCompatibilityEnabled() {
+ return PropertiesUtil.getProperties().getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL);
}
- public void doConfigure(final URL url, final LoggerRepository repository) {
+ private static void warnFullCompatibilityDisabled() {
+ LogLog.warn(
+ "Ignoring `DOMConfigurator` call, since `log4j1.compatibility` is not enabled.\n"
+ + "See https://logging.staged.apache.org/log4j/2.x/migrate-from-log4j1.html#log4j1.compatibility for details.");
}
- public void doConfigure(final InputStream inputStream, final LoggerRepository repository)
- throws FactoryConfigurationError {
+ public static void configure(final Element element) {}
+
+ @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "The filename comes from a system property.")
+ public static void configure(final String fileName) throws FactoryConfigurationError {
+ if (isFullCompatibilityEnabled()) {
+ final Path path = Paths.get(fileName);
+ try (final InputStream inputStream = Files.newInputStream(path)) {
+ final ConfigurationSource source = new ConfigurationSource(inputStream, path);
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration;
+ configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ LogManager.getRootLogger().removeAllAppenders();
+ Configurator.reconfigure(configuration);
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
+ } else {
+ warnFullCompatibilityDisabled();
+ }
}
- public void doConfigure(final Reader reader, final LoggerRepository repository)
- throws FactoryConfigurationError {
+ public static void configure(final URL url) throws FactoryConfigurationError {
+ new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
}
- public void doConfigure(final Element element, final LoggerRepository repository) {
+ public static void configureAndWatch(final String fileName) {
+ // TODO Watch
+ configure(fileName);
}
- public static void configure(final Element element) {
+ public static void configureAndWatch(final String fileName, final long delay) {
+ if (isFullCompatibilityEnabled()) {
+ final XMLWatchdog xdog = new XMLWatchdog(fileName);
+ xdog.setDelay(delay);
+ xdog.start();
+ } else {
+ warnFullCompatibilityDisabled();
+ }
}
- public static void configureAndWatch(final String configFilename) {
+ public static Object parseElement(
+ final Element element, final Properties props, @SuppressWarnings("rawtypes") final Class expectedClass) {
+ return null;
}
- public static void configureAndWatch(final String configFilename, final long delay) {
- }
+ public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {}
- public static void configure(final String filename) throws FactoryConfigurationError {
+ public static String subst(final String value, final Properties props) {
+ return OptionConverter.substVars(value, props);
}
- public static void configure(final URL url) throws FactoryConfigurationError {
+ private void doConfigure(final ConfigurationSource source) {
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ final Configuration configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ Configurator.reconfigure(configuration);
}
- public static String subst(final String value, final Properties props) {
- return value;
+ public void doConfigure(final Element element, final LoggerRepository repository) {}
+
+ public void doConfigure(final InputStream inputStream, final LoggerRepository repository)
+ throws FactoryConfigurationError {
+ if (isFullCompatibilityEnabled()) {
+ try {
+ doConfigure(new ConfigurationSource(inputStream));
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
+ } else {
+ warnFullCompatibilityDisabled();
+ }
}
- public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+ public void doConfigure(final Reader reader, final LoggerRepository repository) throws FactoryConfigurationError {
+ if (isFullCompatibilityEnabled()) {
+ try {
+ final StringWriter sw = new StringWriter();
+ IOUtils.copy(reader, sw);
+ doConfigure(new ConfigurationSource(
+ new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8))));
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
+ } else {
+ warnFullCompatibilityDisabled();
+ }
+ }
+ public void doConfigure(final String fileName, final LoggerRepository repository) {
+ configure(fileName);
}
- public static Object parseElement(final Element element, final Properties props,
- @SuppressWarnings("rawtypes") final Class expectedClass)
- throws Exception {
- return null;
+ public void doConfigure(final URL url, final LoggerRepository repository) {
+ if (isFullCompatibilityEnabled()) {
+ try {
+ final URLConnection connection = UrlConnectionFactory.createConnection(url);
+ try (final InputStream inputStream = connection.getInputStream()) {
+ doConfigure(new ConfigurationSource(inputStream, url));
+ }
+ } catch (final IOException e) {
+ throw new FactoryConfigurationError(e);
+ }
+ } else {
+ warnFullCompatibilityDisabled();
+ }
}
}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
new file mode 100644
index 00000000000..1533fc89389
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
@@ -0,0 +1,51 @@
+/*
+ * 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.log4j.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.Constants;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * An {@link EntityResolver} specifically designed to return
+ * log4j.dtd
which is embedded within the log4j jar
+ * file.
+ */
+public class Log4jEntityResolver implements EntityResolver {
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String PUBLIC_ID = "-//APACHE//DTD LOG4J 1.2//EN";
+
+ @Override
+ public InputSource resolveEntity(final String publicId, final String systemId) {
+ if (systemId.endsWith("log4j.dtd") || PUBLIC_ID.equals(publicId)) {
+ final Class> clazz = getClass();
+ InputStream in = clazz.getResourceAsStream("/org/apache/log4j/xml/log4j.dtd");
+ if (in == null) {
+ LOGGER.warn(
+ "Could not find [log4j.dtd] using [{}] class loader, parsed without DTD.",
+ clazz.getClassLoader());
+ in = new ByteArrayInputStream(Constants.EMPTY_BYTE_ARRAY);
+ }
+ return new InputSource(in);
+ }
+ return null;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
new file mode 100644
index 00000000000..99451054aaa
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
@@ -0,0 +1,42 @@
+/*
+ * 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.log4j.xml;
+
+import java.util.Properties;
+import org.w3c.dom.Element;
+
+/**
+ * When implemented by an object configured by DOMConfigurator,
+ * the handle method will be called when an unrecognized child
+ * element is encountered. Unrecognized child elements of
+ * the log4j:configuration element will be dispatched to
+ * the logger repository if it supports this interface.
+ *
+ * @since 1.2.15
+ */
+public interface UnrecognizedElementHandler {
+ /**
+ * Called to inform a configured object when
+ * an unrecognized child element is encountered.
+ * @param element element, may not be null.
+ * @param props properties in force, may be null.
+ * @return true if configured object recognized the element
+ * @throws Exception throw an exception to prevent activation
+ * of the configured object.
+ */
+ boolean parseUnrecognizedElement(Element element, Properties props) throws Exception;
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
new file mode 100644
index 00000000000..6356cced5db
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
@@ -0,0 +1,36 @@
+/*
+ * 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.log4j.xml;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.spi.LoggerRepository;
+
+class XMLWatchdog extends FileWatchdog {
+
+ XMLWatchdog(final String filename) {
+ super(filename);
+ }
+
+ /**
+ * Calls {@link DOMConfigurator#doConfigure(String, LoggerRepository)} with the filename
to reconfigure Log4j.
+ */
+ @Override
+ public void doOnChange() {
+ new DOMConfigurator().doConfigure(filename, LogManager.getLoggerRepository());
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
new file mode 100644
index 00000000000..dd2a0312b06
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -0,0 +1,831 @@
+/*
+ * 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.log4j.xml;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.stream.IntStream;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.core.Filter.Result;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.status.StatusConfiguration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.LoaderUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Class Description goes here.
+ */
+public class XmlConfiguration extends Log4j1Configuration {
+
+ private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+ private static final String CONFIGURATION_TAG = "log4j:configuration";
+ private static final String OLD_CONFIGURATION_TAG = "configuration";
+ private static final String RENDERER_TAG = "renderer";
+ private static final String APPENDER_TAG = "appender";
+ public static final String PARAM_TAG = "param";
+ public static final String LAYOUT_TAG = "layout";
+ private static final String CATEGORY = "category";
+ private static final String LOGGER_ELEMENT = "logger";
+ private static final String CATEGORY_FACTORY_TAG = "categoryFactory";
+ private static final String LOGGER_FACTORY_TAG = "loggerFactory";
+ public static final String NAME_ATTR = "name";
+ private static final String CLASS_ATTR = "class";
+ public static final String VALUE_ATTR = "value";
+ private static final String ROOT_TAG = "root";
+ private static final String LEVEL_TAG = "level";
+ private static final String PRIORITY_TAG = "priority";
+ public static final String FILTER_TAG = "filter";
+ private static final String ERROR_HANDLER_TAG = "errorHandler";
+ public static final String REF_ATTR = "ref";
+ private static final String ADDITIVITY_ATTR = "additivity";
+ private static final String CONFIG_DEBUG_ATTR = "configDebug";
+ private static final String INTERNAL_DEBUG_ATTR = "debug";
+ private static final String THRESHOLD_ATTR = "threshold";
+ private static final String EMPTY_STR = "";
+ private static final String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
+ private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
+
+ public static final long DEFAULT_DELAY = 60000;
+
+ /**
+ * File name prefix for test configurations.
+ */
+ protected static final String TEST_PREFIX = "log4j-test";
+
+ /**
+ * File name prefix for standard configurations.
+ */
+ protected static final String DEFAULT_PREFIX = "log4j";
+
+ // key: appenderName, value: appender
+ private final Map appenderMap;
+
+ private final Properties props = null;
+
+ public XmlConfiguration(
+ final LoggerContext loggerContext, final ConfigurationSource source, final int monitorIntervalSeconds) {
+ super(loggerContext, source, monitorIntervalSeconds);
+ appenderMap = new HashMap<>();
+ }
+
+ public void addAppenderIfAbsent(Appender appender) {
+ appenderMap.putIfAbsent(appender.getName(), appender);
+ }
+
+ /**
+ * Configures log4j by reading in a log4j.dtd compliant XML
+ * configuration file.
+ */
+ @Override
+ public void doConfigure() throws FactoryConfigurationError {
+ final ConfigurationSource source = getConfigurationSource();
+ final ParseAction action = new ParseAction() {
+ @Override
+ @SuppressFBWarnings(
+ value = "XXE_DOCUMENT",
+ justification = "The `DocumentBuilder` is configured to not resolve external entities.")
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ @SuppressWarnings("resource")
+ final // The ConfigurationSource and its caller manages the InputStream.
+ InputSource inputSource = new InputSource(source.getInputStream());
+ inputSource.setSystemId("dummy://log4j.dtd");
+ return parser.parse(inputSource);
+ }
+
+ @Override
+ public String toString() {
+ return getConfigurationSource().getLocation();
+ }
+ };
+ doConfigure(action);
+ }
+
+ private void doConfigure(final ParseAction action) throws FactoryConfigurationError {
+ DocumentBuilderFactory dbf;
+ try {
+ LOGGER.debug("System property is : {}", OptionConverter.getSystemProperty(dbfKey, null));
+ dbf = DocumentBuilderFactory.newInstance();
+ LOGGER.debug("Standard DocumentBuilderFactory search succeeded.");
+ LOGGER.debug("DocumentBuilderFactory is: " + dbf.getClass().getName());
+ } catch (FactoryConfigurationError fce) {
+ final Exception e = fce.getException();
+ LOGGER.debug("Could not instantiate a DocumentBuilderFactory.", e);
+ throw fce;
+ }
+
+ try {
+ dbf.setValidating(true);
+
+ final DocumentBuilder docBuilder = dbf.newDocumentBuilder();
+
+ docBuilder.setErrorHandler(new SAXErrorHandler());
+ docBuilder.setEntityResolver(new Log4jEntityResolver());
+
+ final Document doc = action.parse(docBuilder);
+ parse(doc.getDocumentElement());
+ } catch (Exception e) {
+ if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // I know this is miserable...
+ LOGGER.error("Could not parse " + action.toString() + ".", e);
+ }
+ }
+
+ @Override
+ public Configuration reconfigure() {
+ try {
+ final ConfigurationSource source = getConfigurationSource().resetInputStream();
+ if (source == null) {
+ return null;
+ }
+ final XmlConfigurationFactory factory = new XmlConfigurationFactory();
+ final XmlConfiguration config = (XmlConfiguration) factory.getConfiguration(getLoggerContext(), source);
+ return config == null || config.getState() != State.INITIALIZING ? null : config;
+ } catch (final IOException ex) {
+ LOGGER.error("Cannot locate file {}: {}", getConfigurationSource(), ex);
+ }
+ return null;
+ }
+
+ /**
+ * Delegates unrecognized content to created instance if it supports UnrecognizedElementParser.
+ *
+ * @param instance instance, may be null.
+ * @param element element, may not be null.
+ * @param props properties
+ * @throws IOException thrown if configuration of owner object should be abandoned.
+ */
+ private void parseUnrecognizedElement(final Object instance, final Element element, final Properties props)
+ throws Exception {
+ boolean recognized = false;
+ if (instance instanceof UnrecognizedElementHandler) {
+ recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(element, props);
+ }
+ if (!recognized) {
+ LOGGER.warn("Unrecognized element {}", element.getNodeName());
+ }
+ }
+
+ /**
+ * Delegates unrecognized content to created instance if
+ * it supports UnrecognizedElementParser and catches and
+ * logs any exception.
+ *
+ * @param instance instance, may be null.
+ * @param element element, may not be null.
+ * @param props properties
+ * @since 1.2.15
+ */
+ private void quietParseUnrecognizedElement(final Object instance, final Element element, final Properties props) {
+ try {
+ parseUnrecognizedElement(instance, element, props);
+ } catch (Exception ex) {
+ if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Error in extension content: ", ex);
+ }
+ }
+
+ /**
+ * Substitutes property value for any references in expression.
+ *
+ * @param value value from configuration file, may contain
+ * literal text, property references or both
+ * @param props properties.
+ * @return evaluated expression, may still contain expressions
+ * if unable to expand.
+ */
+ public String subst(final String value, final Properties props) {
+ try {
+ return OptionConverter.substVars(value, props);
+ } catch (IllegalArgumentException e) {
+ LOGGER.warn("Could not perform variable substitution.", e);
+ return value;
+ }
+ }
+
+ /**
+ * Sets a parameter based from configuration file content.
+ *
+ * @param elem param element, may not be null.
+ * @param propSetter property setter, may not be null.
+ * @param props properties
+ * @since 1.2.15
+ */
+ public void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+ final String name = subst(elem.getAttribute("name"), props);
+ String value = (elem.getAttribute("value"));
+ value = subst(OptionConverter.convertSpecialChars(value), props);
+ propSetter.setProperty(name, value);
+ }
+
+ /**
+ * Creates an object and processes any nested param elements
+ * but does not call activateOptions. If the class also supports
+ * UnrecognizedElementParser, the parseUnrecognizedElement method
+ * will be call for any child elements other than param.
+ *
+ * @param element element, may not be null.
+ * @param props properties
+ * @param expectedClass interface or class expected to be implemented
+ * by created class
+ * @return created class or null.
+ * @throws Exception thrown if the contain object should be abandoned.
+ * @since 1.2.15
+ */
+ public Object parseElement(
+ final Element element, final Properties props, @SuppressWarnings("rawtypes") final Class expectedClass)
+ throws Exception {
+ final String clazz = subst(element.getAttribute("class"), props);
+ final Object instance = OptionConverter.instantiateByClassName(clazz, expectedClass, null);
+
+ if (instance != null) {
+ final PropertySetter propSetter = new PropertySetter(instance);
+ final NodeList children = element.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ final Node currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ final Element currentElement = (Element) currentNode;
+ final String tagName = currentElement.getTagName();
+ if (tagName.equals("param")) {
+ setParameter(currentElement, propSetter, props);
+ } else {
+ parseUnrecognizedElement(instance, currentElement, props);
+ }
+ }
+ }
+ return instance;
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to parse appenders by IDREF name.
+ */
+ private Appender findAppenderByName(final Document doc, final String appenderName) {
+ Appender appender = appenderMap.get(appenderName);
+
+ if (appender != null) {
+ return appender;
+ }
+ // Endre's hack:
+ Element element = null;
+ final NodeList list = doc.getElementsByTagName("appender");
+ for (int t = 0; t < list.getLength(); t++) {
+ final Node node = list.item(t);
+ final NamedNodeMap map = node.getAttributes();
+ final Node attrNode = map.getNamedItem("name");
+ if (appenderName.equals(attrNode.getNodeValue())) {
+ element = (Element) node;
+ break;
+ }
+ }
+ // Hack finished.
+
+ if (element == null) {
+
+ LOGGER.error("No appender named [{}] could be found.", appenderName);
+ return null;
+ }
+ appender = parseAppender(element);
+ if (appender != null) {
+ appenderMap.put(appenderName, appender);
+ }
+ return appender;
+ }
+
+ /**
+ * Used internally to parse appenders by IDREF element.
+ * @param appenderRef The Appender Reference Element.
+ * @return The Appender.
+ */
+ public Appender findAppenderByReference(final Element appenderRef) {
+ final String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
+ final Document doc = appenderRef.getOwnerDocument();
+ return findAppenderByName(doc, appenderName);
+ }
+
+ /**
+ * Used internally to parse an appender element.
+ * @param appenderElement The Appender Element.
+ * @return The Appender.
+ */
+ public Appender parseAppender(final Element appenderElement) {
+ final String className = subst(appenderElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Class name: [" + className + ']');
+ Appender appender = manager.parseAppender(className, appenderElement, this);
+ if (appender == null) {
+ appender = buildAppender(className, appenderElement);
+ }
+ return appender;
+ }
+
+ private Appender buildAppender(final String className, final Element appenderElement) {
+ try {
+ final Appender appender = LoaderUtil.newInstanceOf(className);
+ final PropertySetter propSetter = new PropertySetter(appender);
+
+ appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
+ final AtomicReference filterChain = new AtomicReference<>();
+ forEachElement(appenderElement.getChildNodes(), currentElement -> {
+ // Parse appender parameters
+ switch (currentElement.getTagName()) {
+ case PARAM_TAG:
+ setParameter(currentElement, propSetter);
+ break;
+ case LAYOUT_TAG:
+ appender.setLayout(parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ addFilter(filterChain, currentElement);
+ break;
+ case ERROR_HANDLER_TAG:
+ parseErrorHandler(currentElement, appender);
+ break;
+ case APPENDER_REF_TAG:
+ final String refName = subst(currentElement.getAttribute(REF_ATTR));
+ if (appender instanceof AppenderAttachable) {
+ final AppenderAttachable aa = (AppenderAttachable) appender;
+ final Appender child = findAppenderByReference(currentElement);
+ LOGGER.debug(
+ "Attaching appender named [{}] to appender named [{}].",
+ refName,
+ appender.getName());
+ aa.addAppender(child);
+ } else {
+ LOGGER.error(
+ "Requesting attachment of appender named [{}] to appender named [{}]"
+ + "which does not implement org.apache.log4j.spi.AppenderAttachable.",
+ refName,
+ appender.getName());
+ }
+ break;
+ default:
+ try {
+ parseUnrecognizedElement(appender, currentElement, props);
+ } catch (Exception ex) {
+ throw new ConsumerException(ex);
+ }
+ }
+ });
+ final Filter head = filterChain.get();
+ if (head != null) {
+ appender.addFilter(head);
+ }
+ propSetter.activate();
+ return appender;
+ } catch (ConsumerException ex) {
+ final Throwable t = ex.getCause();
+ if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Appender. Reported error follows.", t);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Appender. Reported error follows.", oops);
+ }
+ return null;
+ }
+
+ public RewritePolicy parseRewritePolicy(final Element rewritePolicyElement) {
+ final String className = subst(rewritePolicyElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Class name: [" + className + ']');
+ RewritePolicy policy = manager.parseRewritePolicy(className, rewritePolicyElement, this);
+ if (policy == null) {
+ policy = buildRewritePolicy(className, rewritePolicyElement);
+ }
+ return policy;
+ }
+
+ private RewritePolicy buildRewritePolicy(String className, Element element) {
+ try {
+ final RewritePolicy policy = LoaderUtil.newInstanceOf(className);
+ final PropertySetter propSetter = new PropertySetter(policy);
+
+ forEachElement(element.getChildNodes(), currentElement -> {
+ if (currentElement.getTagName().equalsIgnoreCase(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ }
+ });
+ propSetter.activate();
+ return policy;
+ } catch (ConsumerException ex) {
+ final Throwable t = ex.getCause();
+ if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an RewritePolicy. Reported error follows.", t);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an RewritePolicy. Reported error follows.", oops);
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to parse an {@link ErrorHandler} element.
+ */
+ private void parseErrorHandler(Element element, Appender appender) {
+ final ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
+ subst(element.getAttribute(CLASS_ATTR)), ErrorHandler.class, null);
+
+ if (eh != null) {
+ eh.setAppender(appender);
+
+ final PropertySetter propSetter = new PropertySetter(eh);
+ forEachElement(element.getChildNodes(), currentElement -> {
+ final String tagName = currentElement.getTagName();
+ if (tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ }
+ });
+ propSetter.activate();
+ appender.setErrorHandler(eh);
+ }
+ }
+
+ /**
+ * Used internally to parse a filter element.
+ * @param filterElement The Filter Element.
+ */
+ public void addFilter(final AtomicReference ref, final Element filterElement) {
+ final Filter value = parseFilters(filterElement);
+ ref.accumulateAndGet(value, FilterAdapter::addFilter);
+ }
+
+ /**
+ * Used internally to parse a filter element.
+ */
+ public Filter parseFilters(final Element filterElement) {
+ final String className = subst(filterElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Class name: [" + className + ']');
+ Filter filter = manager.parseFilter(className, filterElement, this);
+ if (filter == null) {
+ filter = buildFilter(className, filterElement);
+ }
+ return filter;
+ }
+
+ private Filter buildFilter(final String className, final Element filterElement) {
+ try {
+ final Filter filter = LoaderUtil.newInstanceOf(className);
+ final PropertySetter propSetter = new PropertySetter(filter);
+
+ forEachElement(filterElement.getChildNodes(), currentElement -> {
+ // Parse appender parameters
+ switch (currentElement.getTagName()) {
+ case PARAM_TAG:
+ setParameter(currentElement, propSetter);
+ break;
+ }
+ });
+ propSetter.activate();
+ return filter;
+ } catch (ConsumerException ex) {
+ final Throwable t = ex.getCause();
+ if (t instanceof InterruptedException || t instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Filter. Reported error follows.", t);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create an Filter. Reported error follows.", oops);
+ }
+ return null;
+ }
+
+ /**
+ * Used internally to parse an category element.
+ */
+ private void parseCategory(final Element loggerElement) {
+ // Create a new org.apache.log4j.Category object from the element.
+ final String catName = subst(loggerElement.getAttribute(NAME_ATTR));
+ final boolean additivity = OptionConverter.toBoolean(subst(loggerElement.getAttribute(ADDITIVITY_ATTR)), true);
+ LoggerConfig loggerConfig = getLogger(catName);
+ if (loggerConfig == null) {
+ loggerConfig = new LoggerConfig(catName, org.apache.logging.log4j.Level.ERROR, additivity);
+ addLogger(catName, loggerConfig);
+ } else {
+ loggerConfig.setAdditive(additivity);
+ }
+ parseChildrenOfLoggerElement(loggerElement, loggerConfig, false);
+ }
+
+ /**
+ * Used internally to parse the root category element.
+ */
+ private void parseRoot(final Element rootElement) {
+ final LoggerConfig root = getRootLogger();
+ parseChildrenOfLoggerElement(rootElement, root, true);
+ }
+
+ /**
+ * Used internally to parse the children of a LoggerConfig element.
+ */
+ private void parseChildrenOfLoggerElement(Element catElement, LoggerConfig loggerConfig, boolean isRoot) {
+
+ final PropertySetter propSetter = new PropertySetter(loggerConfig);
+ loggerConfig.getAppenderRefs().clear();
+ forEachElement(catElement.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG: {
+ final Appender appender = findAppenderByReference(currentElement);
+ final String refName = subst(currentElement.getAttribute(REF_ATTR));
+ if (appender != null) {
+ LOGGER.debug(
+ "Adding appender named [{}] to loggerConfig [{}].", refName, loggerConfig.getName());
+ loggerConfig.addAppender(getAppender(refName), null, null);
+ } else {
+ LOGGER.debug("Appender named [{}] not found.", refName);
+ }
+ break;
+ }
+ case LEVEL_TAG:
+ case PRIORITY_TAG: {
+ parseLevel(currentElement, loggerConfig, isRoot);
+ break;
+ }
+ case PARAM_TAG: {
+ setParameter(currentElement, propSetter);
+ break;
+ }
+ default: {
+ quietParseUnrecognizedElement(loggerConfig, currentElement, props);
+ }
+ }
+ });
+ propSetter.activate();
+ }
+
+ /**
+ * Used internally to parse a layout element.
+ * @param layoutElement The Layout Element.
+ * @return The Layout.
+ */
+ public Layout parseLayout(final Element layoutElement) {
+ final String className = subst(layoutElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Parsing layout of class: \"{}\"", className);
+ Layout layout = manager.parseLayout(className, layoutElement, this);
+ if (layout == null) {
+ layout = buildLayout(className, layoutElement);
+ }
+ return layout;
+ }
+
+ private Layout buildLayout(final String className, final Element layout_element) {
+ try {
+ final Layout layout = LoaderUtil.newInstanceOf(className);
+ final PropertySetter propSetter = new PropertySetter(layout);
+ forEachElement(layout_element.getChildNodes(), currentElement -> {
+ final String tagName = currentElement.getTagName();
+ if (tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ try {
+ parseUnrecognizedElement(layout, currentElement, props);
+ } catch (Exception ex) {
+ throw new ConsumerException(ex);
+ }
+ }
+ });
+
+ propSetter.activate();
+ return layout;
+ } catch (Exception e) {
+ final Throwable cause = e.getCause();
+ if (e instanceof InterruptedException
+ || e instanceof InterruptedIOException
+ || cause instanceof InterruptedException
+ || cause instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LOGGER.error("Could not create the Layout. Reported error follows.", e);
+ }
+ return null;
+ }
+
+ public TriggeringPolicy parseTriggeringPolicy(final Element policyElement) {
+ final String className = subst(policyElement.getAttribute(CLASS_ATTR));
+ LOGGER.debug("Parsing triggering policy of class: \"{}\"", className);
+ return manager.parseTriggeringPolicy(className, policyElement, this);
+ }
+
+ /**
+ * Used internally to parse a level element.
+ */
+ private void parseLevel(Element element, LoggerConfig logger, boolean isRoot) {
+ String catName = logger.getName();
+ if (isRoot) {
+ catName = "root";
+ }
+
+ final String priStr = subst(element.getAttribute(VALUE_ATTR));
+ LOGGER.debug("Level value for {} is [{}].", catName, priStr);
+
+ if (INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
+ if (isRoot) {
+ LOGGER.error("Root level cannot be inherited. Ignoring directive.");
+ } else {
+ logger.setLevel(null);
+ }
+ } else {
+ final String className = subst(element.getAttribute(CLASS_ATTR));
+ final Level level;
+ if (EMPTY_STR.equals(className)) {
+ level = OptionConverter.toLevel(priStr, DEFAULT_LEVEL);
+ } else {
+ level = OptionConverter.toLevel(className, priStr, DEFAULT_LEVEL);
+ }
+ logger.setLevel(level != null ? level.getVersion2Level() : null);
+ }
+ LOGGER.debug("{} level set to {}", catName, logger.getLevel());
+ }
+
+ private void setParameter(Element element, PropertySetter propSetter) {
+ final String name = subst(element.getAttribute(NAME_ATTR));
+ String value = element.getAttribute(VALUE_ATTR);
+ value = subst(OptionConverter.convertSpecialChars(value));
+ propSetter.setProperty(name, value);
+ }
+
+ /**
+ * Used internally to configure the log4j framework by parsing a DOM
+ * tree of XML elements based on log4j.dtd .
+ */
+ private void parse(Element element) {
+ final String rootElementName = element.getTagName();
+
+ if (!rootElementName.equals(CONFIGURATION_TAG)) {
+ if (rootElementName.equals(OLD_CONFIGURATION_TAG)) {
+ LOGGER.warn("The <" + OLD_CONFIGURATION_TAG + "> element has been deprecated.");
+ LOGGER.warn("Use the <" + CONFIGURATION_TAG + "> element instead.");
+ } else {
+ LOGGER.error("DOM element is - not a <" + CONFIGURATION_TAG + "> element.");
+ return;
+ }
+ }
+
+ final String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
+
+ LOGGER.debug("debug attribute= \"" + debugAttrib + "\".");
+ // if the log4j.dtd is not specified in the XML file, then the
+ // "debug" attribute is returned as the empty string.
+ String status = "error";
+ if (!debugAttrib.isEmpty() && !debugAttrib.equals("null")) {
+ status = OptionConverter.toBoolean(debugAttrib, true) ? "debug" : "error";
+ } else {
+ LOGGER.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
+ }
+
+ final String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
+ if (!confDebug.isEmpty() && !confDebug.equals("null")) {
+ LOGGER.warn("The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
+ LOGGER.warn("Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
+ status = OptionConverter.toBoolean(confDebug, true) ? "debug" : "error";
+ }
+
+ final StatusConfiguration statusConfig = new StatusConfiguration().withStatus(status);
+ statusConfig.initialize();
+
+ final String threshold = subst(element.getAttribute(THRESHOLD_ATTR));
+ if (threshold != null) {
+ final org.apache.logging.log4j.Level level =
+ OptionConverter.convertLevel(threshold.trim(), org.apache.logging.log4j.Level.ALL);
+ addFilter(ThresholdFilter.createFilter(level, Result.NEUTRAL, Result.DENY));
+ }
+
+ forEachElement(element.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case CATEGORY:
+ case LOGGER_ELEMENT:
+ parseCategory(currentElement);
+ break;
+ case ROOT_TAG:
+ parseRoot(currentElement);
+ break;
+ case RENDERER_TAG:
+ LOGGER.warn("Log4j 1 renderers are not supported by Log4j 2 and will be ignored.");
+ break;
+ case THROWABLE_RENDERER_TAG:
+ LOGGER.warn("Log4j 1 throwable renderers are not supported by Log4j 2 and will be ignored.");
+ break;
+ case CATEGORY_FACTORY_TAG:
+ case LOGGER_FACTORY_TAG:
+ LOGGER.warn("Log4j 1 logger factories are not supported by Log4j 2 and will be ignored.");
+ break;
+ case APPENDER_TAG:
+ final Appender appender = parseAppender(currentElement);
+ appenderMap.put(appender.getName(), appender);
+ addAppender(AppenderAdapter.adapt(appender));
+ break;
+ default:
+ quietParseUnrecognizedElement(null, currentElement, props);
+ }
+ });
+ }
+
+ private String subst(final String value) {
+ return getStrSubstitutor().replace(value);
+ }
+
+ public static void forEachElement(final NodeList list, final Consumer consumer) {
+ IntStream.range(0, list.getLength())
+ .mapToObj(list::item)
+ .filter(node -> node.getNodeType() == Node.ELEMENT_NODE)
+ .forEach(node -> consumer.accept((Element) node));
+ }
+
+ private interface ParseAction {
+ Document parse(final DocumentBuilder parser) throws SAXException, IOException;
+ }
+
+ private static class SAXErrorHandler implements org.xml.sax.ErrorHandler {
+ private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+ @Override
+ public void error(final SAXParseException ex) {
+ emitMessage("Continuable parsing error ", ex);
+ }
+
+ @Override
+ public void fatalError(final SAXParseException ex) {
+ emitMessage("Fatal parsing error ", ex);
+ }
+
+ @Override
+ public void warning(final SAXParseException ex) {
+ emitMessage("Parsing warning ", ex);
+ }
+
+ private static void emitMessage(final String msg, final SAXParseException ex) {
+ LOGGER.warn("{} {} and column {}", msg, ex.getLineNumber(), ex.getColumnNumber());
+ LOGGER.warn(ex.getMessage(), ex.getException());
+ }
+ }
+
+ private static class ConsumerException extends RuntimeException {
+
+ ConsumerException(final Exception ex) {
+ super(ex);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
new file mode 100644
index 00000000000..a5b10fce848
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfigurationFactory.java
@@ -0,0 +1,79 @@
+/*
+ * 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.log4j.xml;
+
+import org.apache.log4j.config.Log4j1Configuration;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Order;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+/**
+ * Constructs a Configuration usable in Log4j 2 from a Log4j 1 configuration file.
+ */
+@Plugin(name = "Log4j1XmlConfigurationFactory", category = ConfigurationFactory.CATEGORY)
+@Order(2)
+public class XmlConfigurationFactory extends ConfigurationFactory {
+
+ public static final String FILE_EXTENSION = ".xml";
+
+ private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();
+
+ /**
+ * File name prefix for test configurations.
+ */
+ protected static final String TEST_PREFIX = "log4j-test";
+
+ /**
+ * File name prefix for standard configurations.
+ */
+ protected static final String DEFAULT_PREFIX = "log4j";
+
+ @Override
+ protected String[] getSupportedTypes() {
+ if (!PropertiesUtil.getProperties()
+ .getBooleanProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, Boolean.FALSE)) {
+ return null;
+ }
+ return new String[] {FILE_EXTENSION};
+ }
+
+ @Override
+ public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
+ final int interval = PropertiesUtil.getProperties().getIntegerProperty(Log4j1Configuration.MONITOR_INTERVAL, 0);
+ return new XmlConfiguration(loggerContext, source, interval);
+ }
+
+ @Override
+ protected String getTestPrefix() {
+ return TEST_PREFIX;
+ }
+
+ @Override
+ protected String getDefaultPrefix() {
+ return DEFAULT_PREFIX;
+ }
+
+ @Override
+ protected String getVersion() {
+ return LOG4J1_VERSION;
+ }
+}
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java
index e3ed0d16bdd..b7c3d31598d 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/package-info.java
@@ -17,4 +17,9 @@
/**
* Log4j 1.x compatibility layer.
*/
+@Export
+@Version("2.20.2")
package org.apache.log4j.xml;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd b/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd
new file mode 100644
index 00000000000..f8e433a50e6
--- /dev/null
+++ b/log4j-1.2-api/src/main/resources/org/apache/log4j/xml/log4j.dtd
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/site/markdown/index.md b/log4j-1.2-api/src/site/markdown/index.md
deleted file mode 100644
index a80e6873f6d..00000000000
--- a/log4j-1.2-api/src/site/markdown/index.md
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-# Log4j 1.2 Bridge
-
-The Log4j 1.2 Bridge allows applications coded to use Log4j 1.2 API to use
-Log4j 2 instead.
-
-## Requirements
-
-The Log4j 1.2 bridge is dependent on the Log4j 2 API and implementation.
-For more information, see [Runtime Dependencies](../runtime-dependencies.html).
-
-## Usage
-
-To use the Log4j Legacy Bridge just remove all the Log4j 1.x jars from the application and replace them
-with the bridge jar. Once in place all logging that uses Log4j 1.x will be routed to Log4j 2. However,
-applications that attempt to modify legacy Log4j by adding Appenders, Filters, etc may experience problems
-if they try to verify the success of these actions as these methods are largely no-ops.
diff --git a/log4j-1.2-api/src/site/site.xml b/log4j-1.2-api/src/site/site.xml
deleted file mode 100644
index b27991ccafa..00000000000
--- a/log4j-1.2-api/src/site/site.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
index d231d82346c..afb282b9c65 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfigurationFactory.java
@@ -1,23 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
import java.net.URI;
-
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.AbstractConfiguration;
@@ -33,7 +32,7 @@ public class BasicConfigurationFactory extends ConfigurationFactory {
@Override
public String[] getSupportedTypes() {
- return new String[] { "*" };
+ return new String[] {"*"};
}
@Override
@@ -42,11 +41,12 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C
}
@Override
- public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
+ public Configuration getConfiguration(
+ final LoggerContext loggerContext, final String name, final URI configLocation) {
return new BasicConfiguration(loggerContext);
}
- public class BasicConfiguration extends AbstractConfiguration {
+ public static class BasicConfiguration extends AbstractConfiguration {
private static final long serialVersionUID = -2716784321395089563L;
@@ -58,13 +58,12 @@ public BasicConfiguration(final LoggerContext loggerContext) {
final LoggerConfig root = getRootLogger();
setName("BasicConfiguration");
final String levelName = System.getProperty(DEFAULT_LEVEL);
- final Level level = (levelName != null && Level.getLevel(levelName) != null) ? Level.getLevel(levelName)
- : Level.DEBUG;
+ final Level level =
+ (levelName != null && Level.getLevel(levelName) != null) ? Level.getLevel(levelName) : Level.DEBUG;
root.setLevel(level);
}
@Override
- protected void doConfigure() {
- }
+ protected void doConfigure() {}
}
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfiguratorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfiguratorTest.java
new file mode 100644
index 00000000000..ab632507dc0
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/BasicConfiguratorTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.varia.NullAppender;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test {@link BasicConfigurator}.
+ */
+class BasicConfiguratorTest {
+
+ @Test
+ void testConfigure() {
+ // TODO More...
+ BasicConfigurator.configure();
+ }
+
+ @Test
+ void testResetConfiguration() {
+ // TODO More...
+ BasicConfigurator.resetConfiguration();
+ }
+
+ @Test
+ void testConfigureAppender() {
+ BasicConfigurator.configure(null);
+ // TODO More...
+ }
+
+ @Test
+ void testConfigureConsoleAppender() {
+ // TODO What to do? Map to Log4j 2 Appender deeper in the code?
+ BasicConfigurator.configure(new ConsoleAppender());
+ }
+
+ @Test
+ void testConfigureNullAppender() {
+ // The NullAppender name is null and we do not want an NPE when the name is used as a key in a
+ // ConcurrentHashMap.
+ BasicConfigurator.configure(NullAppender.getNullAppender());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
index a5b30907f07..1212fc97c0c 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CallerInformationTest.java
@@ -1,65 +1,61 @@
-/*
- * 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.log4j;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.List;
-
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-public class CallerInformationTest {
-
- // config from log4j-core test-jar
- private static final String CONFIG = "log4j2-calling-class.xml";
-
- @ClassRule
- public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG);
-
- @Test
- public void testClassLogger() throws Exception {
- final ListAppender app = ctx.getListAppender("Class").clear();
- final Logger logger = Logger.getLogger("ClassLogger");
- logger.info("Ignored message contents.");
- logger.warn("Verifying the caller class is still correct.");
- logger.error("Hopefully nobody breaks me!");
- final List messages = app.getMessages();
- assertEquals("Incorrect number of messages.", 3, messages.size());
- for (final String message : messages) {
- assertEquals("Incorrect caller class name.", this.getClass().getName(), message);
- }
- }
-
- @Test
- public void testMethodLogger() throws Exception {
- final ListAppender app = ctx.getListAppender("Method").clear();
- final Logger logger = Logger.getLogger("MethodLogger");
- logger.info("More messages.");
- logger.warn("CATASTROPHE INCOMING!");
- logger.error("ZOMBIES!!!");
- logger.warn("brains~~~");
- logger.info("Itchy. Tasty.");
- final List messages = app.getMessages();
- assertEquals("Incorrect number of messages.", 5, messages.size());
- for (final String message : messages) {
- assertEquals("Incorrect caller method name.", "testMethodLogger", message);
- }
- }
-}
+/*
+ * 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.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.List;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.core.test.junit.Named;
+import org.junit.jupiter.api.Test;
+
+// config from log4j-core test-jar
+@LoggerContextSource(value = "log4j2-calling-class.xml")
+class CallerInformationTest {
+
+ @Test
+ void testClassLogger(@Named("Class") final ListAppender app) {
+ app.clear();
+ final Logger logger = Logger.getLogger("ClassLogger");
+ logger.info("Ignored message contents.");
+ logger.warn("Verifying the caller class is still correct.");
+ logger.error("Hopefully nobody breaks me!");
+ final List messages = app.getMessages();
+
+ assertEquals(3, messages.size(), "Incorrect number of messages.");
+ for (final String message : messages) {
+ assertEquals(this.getClass().getName(), message, "Incorrect caller class name.");
+ }
+ }
+
+ @Test
+ void testMethodLogger(@Named("Method") final ListAppender app) {
+ app.clear();
+ final Logger logger = Logger.getLogger("MethodLogger");
+ logger.info("More messages.");
+ logger.warn("CATASTROPHE INCOMING!");
+ logger.error("ZOMBIES!!!");
+ logger.warn("brains~~~");
+ logger.info("Itchy. Tasty.");
+ final List messages = app.getMessages();
+ assertEquals(5, messages.size(), "Incorrect number of messages.");
+ for (final String message : messages) {
+ assertEquals("testMethodLogger", message, "Incorrect caller method name.");
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
index f6711933b8d..6dd7305c07e 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java
@@ -2,7 +2,7 @@
* 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 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
*
@@ -14,87 +14,146 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.log4j;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Enumeration;
import java.util.List;
-
+import java.util.Map;
+import java.util.function.Consumer;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.spi.LoggingEvent;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.message.MapMessage;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ObjectMessage;
-import org.apache.logging.log4j.test.appender.ListAppender;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.util.Constants;
import org.apache.logging.log4j.util.Strings;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
/**
* Tests of Category.
*/
-public class CategoryTest {
+class CategoryTest {
static ConfigurationFactory cf = new BasicConfigurationFactory();
- private static ListAppender appender = new ListAppender("List");
+ private static final String VERSION1_APPENDER_NAME = "Version1List";
+ private static final String VERSION2_APPENDER_NAME = "List";
+ private static final ListAppender appender = new ListAppender(VERSION2_APPENDER_NAME);
+ private static final org.apache.log4j.ListAppender version1Appender = new org.apache.log4j.ListAppender();
- @BeforeClass
- public static void setupClass() {
+ @BeforeAll
+ static void setupAll() {
appender.start();
+ version1Appender.setName(VERSION1_APPENDER_NAME);
ConfigurationFactory.setConfigurationFactory(cf);
LoggerContext.getContext().reconfigure();
}
- @AfterClass
- public static void cleanupClass() {
+ @AfterAll
+ static void cleanupAll() {
ConfigurationFactory.removeConfigurationFactory(cf);
appender.stop();
}
- @Before
- public void before() {
+ @BeforeEach
+ void before() {
appender.clear();
}
-
+
+ @Test
+ void testExist() {
+ assertNull(Category.exists("Does not exist for sure"));
+ }
+
/**
* Tests Category.forcedLog.
*/
@Test
@SuppressWarnings("deprecation")
- public void testForcedLog() {
+ void testForcedLog() {
final MockCategory category = new MockCategory("org.example.foo");
category.setAdditivity(false);
- category.getLogger().addAppender(appender);
+ category.setHierarchy(LogManager.getHierarchy());
+ ((org.apache.logging.log4j.core.Logger) category.getLogger()).addAppender(appender);
+ // Logging a String
category.info("Hello, World");
- final List list = appender.getEvents();
+ List list = appender.getEvents();
int events = list.size();
- assertTrue("Number of events should be 1, was " + events, events == 1);
+ assertEquals(1, events, "Number of events");
LogEvent event = list.get(0);
Message msg = event.getMessage();
- assertNotNull("No message", msg);
- assertTrue("Incorrect Message type", msg instanceof ObjectMessage);
+ assertNotNull(msg, "No message");
+ // LOG4J2-3080: use message type consistently
+ assertInstanceOf(SimpleMessage.class, msg, "Incorrect Message type");
+ assertEquals("Hello, World", msg.getFormat());
+ appender.clear();
+ // Logging a String map
+ category.info(Collections.singletonMap("hello", "world"));
+ list = appender.getEvents();
+ events = list.size();
+ assertEquals(1, events, "Number of events");
+ event = list.get(0);
+ msg = event.getMessage();
+ assertNotNull(msg, "No message");
+ assertInstanceOf(MapMessage.class, msg, "Incorrect Message type");
Object[] objects = msg.getParameters();
- assertTrue("Incorrect Object type", objects[0] instanceof String);
+ assertEquals("world", objects[0]);
appender.clear();
- category.log(Priority.INFO, "Hello, World");
+ // Logging a generic map
+ category.info(Collections.singletonMap(1234L, "world"));
+ list = appender.getEvents();
+ events = list.size();
+ assertEquals(1, events, "Number of events");
+ event = list.get(0);
+ msg = event.getMessage();
+ assertNotNull(msg, "No message");
+ assertInstanceOf(MapMessage.class, msg, "Incorrect Message type");
+ objects = msg.getParameters();
+ assertEquals("world", objects[0]);
+ appender.clear();
+ // Logging an object
+ final Object obj = new Object();
+ category.info(obj);
+ list = appender.getEvents();
events = list.size();
- assertTrue("Number of events should be 1, was " + events, events == 1);
+ assertEquals(1, events, "Number of events");
event = list.get(0);
msg = event.getMessage();
- assertNotNull("No message", msg);
- assertTrue("Incorrect Message type", msg instanceof ObjectMessage);
+ assertNotNull(msg, "No message");
+ assertInstanceOf(ObjectMessage.class, msg, "Incorrect Message type");
objects = msg.getParameters();
- assertTrue("Incorrect Object type", objects[0] instanceof String);
+ assertEquals(obj, objects[0]);
+ appender.clear();
+
+ category.log(Priority.INFO, "Hello, World");
+ list = appender.getEvents();
+ events = list.size();
+ assertEquals(1, events, "Number of events");
+ event = list.get(0);
+ msg = event.getMessage();
+ assertNotNull(msg, "No message");
+ assertInstanceOf(SimpleMessage.class, msg, "Incorrect Message type");
+ assertEquals("Hello, World", msg.getFormat());
appender.clear();
}
@@ -104,40 +163,40 @@ public void testForcedLog() {
* @throws Exception thrown if Category.getChainedPriority can not be found.
*/
@Test
- public void testGetChainedPriorityReturnType() throws Exception {
+ void testGetChainedPriorityReturnType() throws Exception {
final Method method = Category.class.getMethod("getChainedPriority", (Class[]) null);
- assertTrue(method.getReturnType() == Priority.class);
+ assertEquals(Priority.class, method.getReturnType());
}
/**
* Tests l7dlog(Priority, String, Throwable).
*/
@Test
- public void testL7dlog() {
+ void testL7dlog() {
final Logger logger = Logger.getLogger("org.example.foo");
logger.setLevel(Level.ERROR);
final Priority debug = Level.DEBUG;
logger.l7dlog(debug, "Hello, World", null);
- assertTrue(appender.getEvents().size() == 0);
+ assertTrue(appender.getEvents().isEmpty());
}
/**
* Tests l7dlog(Priority, String, Object[], Throwable).
*/
@Test
- public void testL7dlog4Param() {
+ void testL7dlog4Param() {
final Logger logger = Logger.getLogger("org.example.foo");
logger.setLevel(Level.ERROR);
final Priority debug = Level.DEBUG;
- logger.l7dlog(debug, "Hello, World", new Object[0], null);
- assertTrue(appender.getEvents().size() == 0);
+ logger.l7dlog(debug, "Hello, World", Constants.EMPTY_OBJECT_ARRAY, null);
+ assertTrue(appender.getEvents().isEmpty());
}
/**
* Test using a pre-existing Log4j 2 logger
*/
@Test
- public void testExistingLog4j2Logger() {
+ void testExistingLog4j2Logger() {
// create the logger using LogManager
org.apache.logging.log4j.LogManager.getLogger("existingLogger");
// Logger will be the one created above
@@ -148,8 +207,8 @@ public void testExistingLog4j2Logger() {
final Priority debug = Level.DEBUG;
// the next line will throw an exception if the LogManager loggers
// aren't supported by 1.2 Logger/Category
- logger.l7dlog(debug, "Hello, World", new Object[0], null);
- assertTrue(appender.getEvents().size() == 0);
+ logger.l7dlog(debug, "Hello, World", Constants.EMPTY_OBJECT_ARRAY, null);
+ assertTrue(appender.getEvents().isEmpty());
}
/**
@@ -159,28 +218,195 @@ public void testExistingLog4j2Logger() {
*/
@Deprecated
@Test
- public void testSetPriority() {
+ void testSetPriority() {
final Logger logger = Logger.getLogger("org.example.foo");
final Priority debug = Level.DEBUG;
logger.setPriority(debug);
}
+ /**
+ * Tests setPriority(Priority).
+ *
+ * @deprecated
+ */
+ @Deprecated
+ @Test
+ void testSetPriorityNull() {
+ Logger.getLogger("org.example.foo").setPriority(null);
+ }
+
@Test
- public void testClassName() {
+ void testClassName() {
final Category category = Category.getInstance("TestCategory");
- final Layout layout = PatternLayout.newBuilder().withPattern("%d %p %C{1.} [%t] %m%n").build();
+ final Layout layout =
+ PatternLayout.newBuilder().setPattern("%d %p %C{1.} [%t] %m%n").build();
final ListAppender appender = new ListAppender("List2", null, layout, false, false);
appender.start();
category.setAdditivity(false);
- category.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) category.getLogger()).addAppender(appender);
category.error("Test Message");
final List msgs = appender.getMessages();
- assertTrue("Incorrect number of messages. Expected 1 got " + msgs.size(), msgs.size() == 1);
+ assertEquals(1, msgs.size(), "Incorrect number of messages. Expected 1 got " + msgs.size());
final String msg = msgs.get(0);
appender.clear();
final String threadName = Thread.currentThread().getName();
final String expected = "ERROR o.a.l.CategoryTest [" + threadName + "] Test Message" + Strings.LINE_SEPARATOR;
- assertTrue("Incorrect message " + Strings.dquote(msg) + " expected " + Strings.dquote(expected), msg.endsWith(expected));
+ assertTrue(
+ msg.endsWith(expected),
+ "Incorrect message " + Strings.dquote(msg) + " expected " + Strings.dquote(expected));
+ }
+
+ @Test
+ void testStringLog() {
+ final String payload = "payload";
+ testMessageImplementation(
+ payload, SimpleMessage.class, message -> assertEquals(payload, message.getFormattedMessage()));
+ }
+
+ @Test
+ void testCharSequenceLog() {
+ final CharSequence payload = new CharSequence() {
+
+ @Override
+ public int length() {
+ return 3;
+ }
+
+ @Override
+ public char charAt(final int index) {
+ return "abc".charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(final int start, final int end) {
+ return "abc".subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return "abc";
+ }
+ };
+ testMessageImplementation(
+ payload,
+ SimpleMessage.class,
+ message -> assertEquals(message.getFormattedMessage(), payload.toString()));
+ }
+
+ @Test
+ void testMapLog() {
+ final String key = "key";
+ final Object value = 0xDEADBEEF;
+ final Map payload = Collections.singletonMap(key, value);
+ testMessageImplementation(payload, MapMessage.class, message -> assertEquals(message.getData(), payload));
+ }
+
+ @Test
+ void testObjectLog() {
+ final Object payload = new Object();
+ testMessageImplementation(
+ payload, ObjectMessage.class, message -> assertEquals(message.getParameter(), payload));
+ }
+
+ private void testMessageImplementation(
+ final Object messagePayload, final Class expectedMessageClass, final Consumer messageTester) {
+
+ // Setup the logger and the appender.
+ final Category category = Category.getInstance("TestCategory");
+ final org.apache.logging.log4j.core.Logger logger = (org.apache.logging.log4j.core.Logger) category.getLogger();
+ logger.addAppender(appender);
+
+ // Log the message payload.
+ category.info(messagePayload);
+
+ // Verify collected log events.
+ final List events = appender.getEvents();
+ assertEquals(1, events.size(), "was expecting a single event");
+ final LogEvent logEvent = events.get(0);
+
+ // Verify the collected message.
+ final Message message = logEvent.getMessage();
+ final Class extends Message> actualMessageClass = message.getClass();
+ assertTrue(
+ expectedMessageClass.isAssignableFrom(actualMessageClass),
+ "was expecting message to be instance of " + expectedMessageClass + ", found: " + actualMessageClass);
+ @SuppressWarnings("unchecked")
+ final M typedMessage = (M) message;
+ messageTester.accept(typedMessage);
+ }
+
+ @Test
+ void testAddAppender() {
+ try {
+ final Logger rootLogger = LogManager.getRootLogger();
+ int count = version1Appender.getEvents().size();
+ rootLogger.addAppender(version1Appender);
+ final Logger logger = LogManager.getLogger(CategoryTest.class);
+ final org.apache.log4j.ListAppender appender = new org.apache.log4j.ListAppender();
+ appender.setName("appender2");
+ logger.addAppender(appender);
+ // Root logger
+ rootLogger.info("testAddLogger");
+ assertEquals(++count, version1Appender.getEvents().size(), "adding at root works");
+ assertEquals(0, appender.getEvents().size(), "adding at child works");
+ // Another logger
+ logger.info("testAddLogger2");
+ assertEquals(++count, version1Appender.getEvents().size(), "adding at root works");
+ assertEquals(1, appender.getEvents().size(), "adding at child works");
+ // Call appenders
+ final LoggingEvent event = new LoggingEvent();
+ logger.callAppenders(event);
+ assertEquals(++count, version1Appender.getEvents().size(), "callAppenders");
+ assertEquals(2, appender.getEvents().size(), "callAppenders");
+ } finally {
+ LogManager.resetConfiguration();
+ }
+ }
+
+ @Test
+ void testGetAppender() {
+ try {
+ final Logger rootLogger = LogManager.getRootLogger();
+ final org.apache.logging.log4j.core.Logger v2RootLogger =
+ (org.apache.logging.log4j.core.Logger) rootLogger.getLogger();
+ v2RootLogger.addAppender(AppenderAdapter.adapt(version1Appender));
+ v2RootLogger.addAppender(appender);
+ final List rootAppenders = Collections.list(rootLogger.getAllAppenders());
+ assertEquals(1, rootAppenders.size(), "only v1 appenders");
+ assertInstanceOf(
+ org.apache.log4j.ListAppender.class, rootAppenders.get(0), "appender is a v1 ListAppender");
+ assertEquals(
+ VERSION1_APPENDER_NAME,
+ rootLogger.getAppender(VERSION1_APPENDER_NAME).getName(),
+ "explicitly named appender");
+ final Appender v2ListAppender = rootLogger.getAppender(VERSION2_APPENDER_NAME);
+ assertInstanceOf(AppenderWrapper.class, v2ListAppender, "explicitly named appender");
+ assertInstanceOf(
+ ListAppender.class,
+ ((AppenderWrapper) v2ListAppender).getAppender(),
+ "appender is a v2 ListAppender");
+
+ final Logger logger = LogManager.getLogger(CategoryTest.class);
+ final org.apache.logging.log4j.core.Logger v2Logger =
+ (org.apache.logging.log4j.core.Logger) logger.getLogger();
+ final org.apache.log4j.ListAppender loggerAppender = new org.apache.log4j.ListAppender();
+ loggerAppender.setName("appender2");
+ v2Logger.addAppender(AppenderAdapter.adapt(loggerAppender));
+ final List appenders = Collections.list(logger.getAllAppenders());
+ assertEquals(1, appenders.size(), "no parent appenders");
+ assertEquals(loggerAppender, appenders.get(0));
+ assertNull(logger.getAppender(VERSION1_APPENDER_NAME), "no parent appenders");
+ assertNull(logger.getAppender(VERSION2_APPENDER_NAME), "no parent appenders");
+
+ final Logger childLogger = LogManager.getLogger(CategoryTest.class.getName() + ".child");
+ final Enumeration childAppenders = childLogger.getAllAppenders();
+ assertFalse(childAppenders.hasMoreElements(), "no parent appenders");
+ assertNull(childLogger.getAppender("appender2"), "no parent appenders");
+ assertNull(childLogger.getAppender(VERSION1_APPENDER_NAME), "no parent appenders");
+ assertNull(childLogger.getAppender(VERSION2_APPENDER_NAME), "no parent appenders");
+ } finally {
+ LogManager.resetConfiguration();
+ }
}
/**
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/ConsoleAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/ConsoleAppenderTest.java
new file mode 100644
index 00000000000..7fe9a08e29f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/ConsoleAppenderTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Used to test Log4j 1 support.
+ */
+class ConsoleAppenderTest {
+
+ private ConsoleAppender consoleAppender;
+
+ @BeforeEach
+ void beforeEach() {
+ consoleAppender = new ConsoleAppender();
+ }
+
+ @Test
+ void testFollow() {
+ // Only really care that it compiles, behavior is secondary at this level.
+ consoleAppender.setFollow(true);
+ assertTrue(consoleAppender.getFollow());
+ }
+
+ @Test
+ void testTarget() {
+ // Only really care that it compiles, behavior is secondary at this level.
+ consoleAppender.setTarget(ConsoleAppender.SYSTEM_OUT);
+ assertEquals(ConsoleAppender.SYSTEM_OUT, consoleAppender.getTarget());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomAppenderSkeleton.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomAppenderSkeleton.java
new file mode 100644
index 00000000000..53596ed7c38
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomAppenderSkeleton.java
@@ -0,0 +1,75 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Used to test Log4j 1 support. All we are looking for here is that this code compiles.
+ */
+public class CustomAppenderSkeleton extends AppenderSkeleton {
+
+ @Override
+ protected void append(final LoggingEvent event) {
+ // NOOP @Override
+ }
+
+ @Override
+ public void close() {
+ // NOOP @Override
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderSkeletonVariables() {
+ if (closed) {
+ // Yep, it compiles.
+ final boolean compileMe = closed;
+ }
+ if (errorHandler instanceof ErrorHandler) {
+ // Yep, it compiles.
+ final ErrorHandler other = errorHandler;
+ }
+ if (headFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = headFilter;
+ }
+ if (layout instanceof Layout) {
+ // Yep, it compiles.
+ final Layout other = layout;
+ }
+ if (name instanceof String) {
+ // Yep, it compiles.
+ final String other = name;
+ }
+ if (tailFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = tailFilter;
+ }
+ if (threshold instanceof Priority) {
+ // Yep, it compiles.
+ final Priority other = threshold;
+ }
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ // NOOP @Override
+ return false;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomConsoleAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomConsoleAppender.java
new file mode 100644
index 00000000000..b14c6114ff4
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomConsoleAppender.java
@@ -0,0 +1,84 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+
+/**
+ * Used to test Log4j 1 support. All we are looking for here is that this code compiles.
+ */
+public class CustomConsoleAppender extends ConsoleAppender {
+
+ public CustomConsoleAppender() {
+ super();
+ }
+
+ public CustomConsoleAppender(final Layout layout) {
+ super(layout);
+ }
+
+ public CustomConsoleAppender(final Layout layout, final String target) {
+ super(layout, target);
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToConsoleAppenderInstanceVariables() {
+ if (target instanceof String) {
+ final String other = name;
+ }
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderInstanceVariables() {
+ if (immediateFlush) {
+ final boolean other = immediateFlush;
+ }
+ if (encoding instanceof String) {
+ final String other = encoding;
+ }
+ if (qw instanceof QuietWriter) {
+ final QuietWriter other = qw;
+ }
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderSkeletonVariables() {
+ if (closed) {
+ final boolean compileMe = closed;
+ }
+ if (errorHandler instanceof ErrorHandler) {
+ final ErrorHandler other = errorHandler;
+ }
+ if (headFilter instanceof Filter) {
+ final Filter other = headFilter;
+ }
+ if (layout instanceof Layout) {
+ final Layout other = layout;
+ }
+ if (name instanceof String) {
+ final String other = name;
+ }
+ if (tailFilter instanceof Filter) {
+ final Filter other = tailFilter;
+ }
+ if (threshold instanceof Priority) {
+ final Priority other = threshold;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomFileAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomFileAppender.java
new file mode 100644
index 00000000000..965287f9284
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomFileAppender.java
@@ -0,0 +1,48 @@
+/*
+ * 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.log4j;
+
+public class CustomFileAppender extends FileAppender {
+
+ private boolean booleanA;
+ private int intA;
+ private String stringA;
+
+ public boolean getBooleanA() {
+ return booleanA;
+ }
+
+ public int getIntA() {
+ return intA;
+ }
+
+ public String getStringA() {
+ return stringA;
+ }
+
+ public void setBooleanA(final boolean booleanA) {
+ this.booleanA = booleanA;
+ }
+
+ public void setIntA(final int intA) {
+ this.intA = intA;
+ }
+
+ public void setStringA(final String stringA) {
+ this.stringA = stringA;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomNoopAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomNoopAppender.java
new file mode 100644
index 00000000000..2c775960961
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomNoopAppender.java
@@ -0,0 +1,65 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+public class CustomNoopAppender extends AppenderSkeleton {
+
+ private boolean booleanA;
+ private int intA;
+ private String stringA;
+
+ @Override
+ protected void append(final LoggingEvent event) {
+ // Noop
+ }
+
+ @Override
+ public void close() {
+ // Noop
+ }
+
+ public boolean getBooleanA() {
+ return booleanA;
+ }
+
+ public int getIntA() {
+ return intA;
+ }
+
+ public String getStringA() {
+ return stringA;
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ public void setBooleanA(final boolean booleanA) {
+ this.booleanA = booleanA;
+ }
+
+ public void setIntA(final int intA) {
+ this.intA = intA;
+ }
+
+ public void setStringA(final String stringA) {
+ this.stringA = stringA;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CustomWriterAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomWriterAppender.java
new file mode 100644
index 00000000000..e4c5a4a14ee
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CustomWriterAppender.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+
+/**
+ * Used to test Log4j 1 support. All we are looking for here is that this code compiles.
+ */
+public class CustomWriterAppender extends WriterAppender {
+
+ public void compilerAccessToWriterAppenderInstanceVariables() {
+ if (immediateFlush) {
+ // Yep, it compiles.
+ final boolean other = immediateFlush;
+ }
+ if (encoding instanceof String) {
+ // Yep, it compiles.
+ final String other = encoding;
+ }
+ if (qw instanceof QuietWriter) {
+ // Yep, it compiles.
+ final QuietWriter other = qw;
+ }
+ }
+
+ @SuppressWarnings({"cast", "unused"})
+ public void compilerAccessToWriterAppenderSkeletonVariables() {
+ if (closed) {
+ // Yep, it compiles.
+ final boolean compileMe = closed;
+ }
+ if (errorHandler instanceof ErrorHandler) {
+ // Yep, it compiles.
+ final ErrorHandler other = errorHandler;
+ }
+ if (headFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = headFilter;
+ }
+ if (layout instanceof Layout) {
+ // Yep, it compiles.
+ final Layout other = layout;
+ }
+ if (name instanceof String) {
+ // Yep, it compiles.
+ final String other = name;
+ }
+ if (tailFilter instanceof Filter) {
+ // Yep, it compiles.
+ final Filter other = tailFilter;
+ }
+ if (threshold instanceof Priority) {
+ // Yep, it compiles.
+ final Priority other = threshold;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
new file mode 100644
index 00000000000..35c1b2cf1b3
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LayoutTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.log4j;
+
+import junit.framework.TestCase;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Tests for Layout.
+ *
+ */
+public class LayoutTest extends TestCase {
+
+ /**
+ * Concrete Layout class for tests.
+ */
+ private static final class MockLayout extends Layout {
+ /**
+ * {@inheritDoc}
+ */
+ public void activateOptions() {}
+
+ /**
+ * {@inheritDoc}
+ */
+ public String format(final LoggingEvent event) {
+ return "Mock";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean ignoresThrowable() {
+ return true;
+ }
+ }
+
+ /**
+ * Expected content type.
+ */
+ private final String contentType;
+
+ /**
+ * Expected value for ignoresThrowable.
+ */
+ private final boolean ignoresThrowable;
+
+ /**
+ * Expected value for header.
+ */
+ private final String header;
+
+ /**
+ * Expected value for footer.
+ */
+ private final String footer;
+
+ /**
+ * Construct a new instance of LayoutTest.
+ *
+ * @param testName test name.
+ */
+ public LayoutTest(final String testName) {
+ super(testName);
+ contentType = "text/plain";
+ ignoresThrowable = true;
+ header = null;
+ footer = null;
+ }
+
+ /**
+ * Constructor for use by derived tests.
+ *
+ * @param testName name of test.
+ * @param expectedContentType expected value for getContentType().
+ * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+ * @param expectedHeader expected value for getHeader().
+ * @param expectedFooter expected value for getFooter().
+ */
+ protected LayoutTest(
+ final String testName,
+ final String expectedContentType,
+ final boolean expectedIgnoresThrowable,
+ final String expectedHeader,
+ final String expectedFooter) {
+ super(testName);
+ contentType = expectedContentType;
+ ignoresThrowable = expectedIgnoresThrowable;
+ header = expectedHeader;
+ footer = expectedFooter;
+ }
+
+ /**
+ * Creates layout for test.
+ *
+ * @return new instance of Layout.
+ */
+ protected Layout createLayout() {
+ return new MockLayout();
+ }
+
+ /**
+ * Tests format.
+ *
+ */
+ public void testFormat() {
+ final Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+ final LoggingEvent event =
+ new LoggingEvent("org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+ final String result = createLayout().format(event);
+ assertEquals("Mock", result);
+ }
+
+ /**
+ * Tests getContentType.
+ */
+ public void testGetContentType() {
+ assertEquals(contentType, createLayout().getContentType());
+ }
+
+ /**
+ * Tests getFooter.
+ */
+ public void testGetFooter() {
+ assertEquals(footer, createLayout().getFooter());
+ }
+
+ /**
+ * Tests getHeader.
+ */
+ public void testGetHeader() {
+ assertEquals(header, createLayout().getHeader());
+ }
+
+ /**
+ * Tests ignoresThrowable.
+ */
+ public void testIgnoresThrowable() {
+ assertEquals(ignoresThrowable, createLayout().ignoresThrowable());
+ }
+
+ /**
+ * Tests Layout.LINE_SEP.
+ */
+ public void testLineSep() {
+ assertEquals(System.getProperty("line.separator"), Layout.LINE_SEP);
+ }
+
+ /**
+ * Tests Layout.LINE_SEP.
+ */
+ public void testLineSepLen() {
+ assertEquals(Layout.LINE_SEP.length(), Layout.LINE_SEP_LEN);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java
index bb991ad33da..32b574b8de3 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LevelTest.java
@@ -2,7 +2,7 @@
* 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 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
*
@@ -14,23 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.log4j;
-import java.util.Locale;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.Locale;
+import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.util.SerializationTestHelper;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
/**
* Tests of Level.
*
* @since 1.2.12
*/
-public class LevelTest {
+class LevelTest {
/**
* Serialize Level.INFO and check against witness.
@@ -38,11 +40,10 @@ public class LevelTest {
* @throws Exception if exception during test.
*/
@Test
- public void testSerializeINFO() throws Exception {
- final int[] skip = new int[]{};
+ void testSerializeINFO() throws Exception {
+ final int[] skip = new int[] {};
SerializationTestHelper.assertSerializationEquals(
- "target/test-classes/witness/serialization/info.bin",
- Level.INFO, skip, Integer.MAX_VALUE);
+ "target/test-classes/witness/serialization/info.bin", Level.INFO, skip, Integer.MAX_VALUE);
}
/**
@@ -51,17 +52,16 @@ public void testSerializeINFO() throws Exception {
* @throws Exception if exception during test.
*/
@Test
- public void testDeserializeINFO() throws Exception {
+ void testDeserializeINFO() throws Exception {
final Object obj =
- SerializationTestHelper.deserializeStream(
- "target/test-classes/witness/serialization/info.bin");
- assertTrue(obj instanceof Level);
+ SerializationTestHelper.deserializeStream("target/test-classes/witness/serialization/info.bin");
+ assertInstanceOf(Level.class, obj);
final Level info = (Level) obj;
assertEquals("INFO", info.toString());
//
// JDK 1.1 doesn't support readResolve necessary for the assertion
if (!System.getProperty("java.version").startsWith("1.1.")) {
- assertTrue(obj == Level.INFO);
+ assertEquals(Level.INFO, obj);
}
}
@@ -72,15 +72,16 @@ public void testDeserializeINFO() throws Exception {
* @throws Exception if exception during test.
*/
@Test
- public void testCustomLevelSerialization() throws Exception {
+ void testCustomLevelSerialization() throws Exception {
final CustomLevel custom = new CustomLevel();
final Object obj = SerializationTestHelper.serializeClone(custom);
- assertTrue(obj instanceof CustomLevel);
+ assertInstanceOf(CustomLevel.class, obj);
final CustomLevel clone = (CustomLevel) obj;
assertEquals(Level.INFO.level, clone.level);
assertEquals(Level.INFO.levelStr, clone.levelStr);
assertEquals(Level.INFO.syslogEquivalent, clone.syslogEquivalent);
+ assertEquals(OptionConverter.createLevel(custom), clone.version2Level);
}
/**
@@ -97,8 +98,7 @@ private static class CustomLevel extends Level {
* Create an instance of CustomLevel.
*/
public CustomLevel() {
- super(
- Level.INFO.level, Level.INFO.levelStr, Level.INFO.syslogEquivalent);
+ super(Level.INFO.level, Level.INFO.levelStr, Level.INFO.syslogEquivalent);
}
}
@@ -106,7 +106,7 @@ public CustomLevel() {
* Tests Level.TRACE_INT.
*/
@Test
- public void testTraceInt() {
+ void testTraceInt() {
assertEquals(5000, Level.TRACE_INT);
}
@@ -114,7 +114,7 @@ public void testTraceInt() {
* Tests Level.TRACE.
*/
@Test
- public void testTrace() {
+ void testTrace() {
assertEquals("TRACE", Level.TRACE.toString());
assertEquals(5000, Level.TRACE.toInt());
assertEquals(7, Level.TRACE.getSyslogEquivalent());
@@ -124,7 +124,7 @@ public void testTrace() {
* Tests Level.toLevel(Level.TRACE_INT).
*/
@Test
- public void testIntToTrace() {
+ void testIntToTrace() {
final Level trace = Level.toLevel(5000);
assertEquals("TRACE", trace.toString());
}
@@ -133,7 +133,7 @@ public void testIntToTrace() {
* Tests Level.toLevel("TRACE");
*/
@Test
- public void testStringToTrace() {
+ void testStringToTrace() {
final Level trace = Level.toLevel("TRACE");
assertEquals("TRACE", trace.toString());
}
@@ -142,7 +142,7 @@ public void testStringToTrace() {
* Tests that Level extends Priority.
*/
@Test
- public void testLevelExtendsPriority() {
+ void testLevelExtendsPriority() {
assertTrue(Priority.class.isAssignableFrom(Level.class));
}
@@ -150,71 +150,80 @@ public void testLevelExtendsPriority() {
* Tests Level.OFF.
*/
@Test
- public void testOFF() {
- assertTrue(Level.OFF instanceof Level);
+ void testOFF() {
+ assertInstanceOf(Level.class, Level.OFF);
}
/**
* Tests Level.FATAL.
*/
@Test
- public void testFATAL() {
- assertTrue(Level.FATAL instanceof Level);
+ void testFATAL() {
+ assertInstanceOf(Level.class, Level.FATAL);
}
/**
* Tests Level.ERROR.
*/
@Test
- public void testERROR() {
- assertTrue(Level.ERROR instanceof Level);
+ void testERROR() {
+ assertInstanceOf(Level.class, Level.ERROR);
}
/**
* Tests Level.WARN.
*/
@Test
- public void testWARN() {
- assertTrue(Level.WARN instanceof Level);
+ void testWARN() {
+ assertInstanceOf(Level.class, Level.WARN);
}
/**
* Tests Level.INFO.
*/
@Test
- public void testINFO() {
- assertTrue(Level.INFO instanceof Level);
+ void testINFO() {
+ assertInstanceOf(Level.class, Level.INFO);
}
/**
* Tests Level.DEBUG.
*/
@Test
- public void testDEBUG() {
- assertTrue(Level.DEBUG instanceof Level);
+ void testDEBUG() {
+ assertInstanceOf(Level.class, Level.DEBUG);
}
/**
* Tests Level.TRACE.
*/
@Test
- public void testTRACE() {
- assertTrue(Level.TRACE instanceof Level);
+ void testTRACE() {
+ assertInstanceOf(Level.class, Level.TRACE);
}
/**
* Tests Level.ALL.
*/
@Test
- public void testALL() {
- assertTrue(Level.ALL instanceof Level);
+ void testALL() {
+ assertInstanceOf(Level.class, Level.ALL);
+ }
+
+ /**
+ * Tests version2Level.
+ */
+ @ParameterizedTest
+ @MethodSource("org.apache.log4j.helpers.OptionConverterLevelTest#standardLevels")
+ void testVersion2Level(final Level log4j1Level, final org.apache.logging.log4j.Level log4j2Level) {
+ assertEquals(log4j2Level, log4j1Level.getVersion2Level());
}
/**
* Tests Level.toLevel(Level.All_INT).
*/
@Test
- public void testIntToAll() {
+ void testIntToAll() {
final Level level = Level.toLevel(Priority.ALL_INT);
assertEquals("ALL", level.toString());
}
@@ -223,17 +232,16 @@ public void testIntToAll() {
* Tests Level.toLevel(Level.FATAL_INT).
*/
@Test
- public void testIntToFatal() {
+ void testIntToFatal() {
final Level level = Level.toLevel(Priority.FATAL_INT);
assertEquals("FATAL", level.toString());
}
-
/**
* Tests Level.toLevel(Level.OFF_INT).
*/
@Test
- public void testIntToOff() {
+ void testIntToOff() {
final Level level = Level.toLevel(Priority.OFF_INT);
assertEquals("OFF", level.toString());
}
@@ -242,7 +250,7 @@ public void testIntToOff() {
* Tests Level.toLevel(17, Level.FATAL).
*/
@Test
- public void testToLevelUnrecognizedInt() {
+ void testToLevelUnrecognizedInt() {
final Level level = Level.toLevel(17, Level.FATAL);
assertEquals("FATAL", level.toString());
}
@@ -251,7 +259,7 @@ public void testToLevelUnrecognizedInt() {
* Tests Level.toLevel(null, Level.FATAL).
*/
@Test
- public void testToLevelNull() {
+ void testToLevelNull() {
final Level level = Level.toLevel(null, Level.FATAL);
assertEquals("FATAL", level.toString());
}
@@ -260,7 +268,7 @@ public void testToLevelNull() {
* Test that dotless lower I + "nfo" is recognized as INFO.
*/
@Test
- public void testDotlessLowerI() {
+ void testDotlessLowerI() {
final Level level = Level.toLevel("\u0131nfo");
assertEquals("INFO", level.toString());
}
@@ -270,7 +278,7 @@ public void testDotlessLowerI() {
* even in Turkish locale.
*/
@Test
- public void testDottedLowerI() {
+ void testDottedLowerI() {
final Locale defaultLocale = Locale.getDefault();
final Locale turkey = new Locale("tr", "TR");
Locale.setDefault(turkey);
@@ -278,7 +286,4 @@ public void testDottedLowerI() {
Locale.setDefault(defaultLocale);
assertEquals("INFO", level.toString());
}
-
-
}
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
new file mode 100644
index 00000000000..34bce86bd63
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/ListAppender.java
@@ -0,0 +1,82 @@
+/*
+ * 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.log4j;
+
+import static org.awaitility.Awaitility.waitAtMost;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Used to test Log4j 1 support.
+ */
+public class ListAppender extends AppenderSkeleton {
+ // Use Collections.synchronizedList rather than CopyOnWriteArrayList because we expect
+ // more frequent writes than reads.
+ final List events = Collections.synchronizedList(new ArrayList<>());
+
+ private final List messages = Collections.synchronizedList(new ArrayList<>());
+
+ private static final String WINDOWS_LINE_SEP = "\r\n";
+
+ @Override
+ protected void append(final LoggingEvent event) {
+ final Layout layout = getLayout();
+ if (layout != null) {
+ final String result = layout.format(event);
+ if (result != null) {
+ messages.add(result);
+ }
+ } else {
+ events.add(event);
+ }
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ /** Returns an immutable snapshot of captured log events */
+ public List getEvents() {
+ return Collections.unmodifiableList(new ArrayList<>(events));
+ }
+
+ /** Returns an immutable snapshot of captured messages */
+ public List getMessages() {
+ return Collections.unmodifiableList(new ArrayList<>(messages));
+ }
+
+ /**
+ * Polls the messages list for it to grow to a given minimum size at most timeout timeUnits and return a copy of
+ * what we have so far.
+ */
+ public List getMessages(final int minSize, final long timeout, final TimeUnit timeUnit) {
+ waitAtMost(timeout, timeUnit).until(() -> messages.size() >= minSize);
+ return getMessages();
+ }
+
+ public String toString() {
+ return String.format("ListAppender[%s]", getName());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java
new file mode 100644
index 00000000000..501d2c36709
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogManagerTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link LogManager}.
+ */
+class LogManagerTest {
+
+ private static final String SIMPLE_NAME = LogManagerTest.class.getSimpleName();
+
+ List getCurrentLoggerNames() {
+ return Collections.list((Enumeration) LogManager.getCurrentLoggers()).stream()
+ .map(Logger::getName)
+ .collect(Collectors.toList());
+ }
+
+ @Test
+ void testGetCurrentLoggers() {
+ Logger.getLogger(SIMPLE_NAME);
+ Logger.getLogger(SIMPLE_NAME + ".foo");
+ Logger.getLogger(SIMPLE_NAME + ".foo.bar");
+ final List names = getCurrentLoggerNames();
+ assertTrue(names.contains(SIMPLE_NAME));
+ assertTrue(names.contains(SIMPLE_NAME + ".foo"));
+ assertTrue(names.contains(SIMPLE_NAME + ".foo.bar"));
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
index c48e35ece7a..0e5165d4796 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithMDCTest.java
@@ -1,52 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.ClassRule;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
-
-import static org.junit.Assert.*;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.core.test.junit.Named;
+import org.junit.jupiter.api.Test;
/**
* Test logging with MDC values.
*/
-public class LogWithMDCTest {
-
- private static final String CONFIG = "logWithMDC.xml";
-
- @ClassRule
- public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
+@LoggerContextSource("logWithMDC.xml")
+class LogWithMDCTest {
@Test
- public void testMDC() throws Exception {
+ void testMDC(@Named("List") final ListAppender listApp) {
MDC.put("Key1", "John");
MDC.put("Key2", "Smith");
- final Logger logger = Logger.getLogger("org.apache.test.logging");
- logger.debug("This is a test");
- final ListAppender listApp = (ListAppender) CTX.getAppender("List");
- assertNotNull(listApp);
- final List msgs = listApp.getMessages();
- assertNotNull("No messages received", msgs);
- assertTrue(msgs.size() == 1);
- assertTrue("Key1 is missing", msgs.get(0).contains("Key1=John"));
- assertTrue("Key2 is missing", msgs.get(0).contains("Key2=Smith"));
+ try {
+ final Logger logger = Logger.getLogger("org.apache.test.logging");
+ logger.debug("This is a test");
+ assertNotNull(listApp);
+ final List msgs = listApp.getMessages();
+ assertNotNull(msgs, "No messages received");
+ assertEquals(1, msgs.size());
+ assertTrue(msgs.get(0).contains("Key1=John"), "Key1 is missing");
+ assertTrue(msgs.get(0).contains("Key2=Smith"), "Key2 is missing");
+ } finally {
+ MDC.remove("Key1");
+ MDC.remove("Key2");
+ }
}
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
index 06ad4e8090c..18ad15293c8 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LogWithRouteTest.java
@@ -1,53 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.ClassRule;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.apache.logging.log4j.core.test.junit.Named;
+import org.junit.jupiter.api.Test;
/**
* Test passing MDC values to the Routing appender.
*/
-public class LogWithRouteTest {
-
- private static final String CONFIG = "log-RouteWithMDC.xml";
-
- @ClassRule
- public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
+@LoggerContextSource("log-RouteWithMDC.xml")
+class LogWithRouteTest {
@Test
- public void testMDC() throws Exception {
+ void testMDC(@Named("List") final ListAppender listApp) {
MDC.put("Type", "Service");
MDC.put("Name", "John Smith");
- final Logger logger = Logger.getLogger("org.apache.test.logging");
- logger.debug("This is a test");
- final ListAppender listApp = (ListAppender) CTX.getAppender("List");
- assertNotNull(listApp);
- final List msgs = listApp.getMessages();
- assertNotNull("No messages received", msgs);
- assertTrue(msgs.size() == 1);
- assertTrue("Type is missing", msgs.get(0).contains("Type=Service"));
- assertTrue("Name is missing", msgs.get(0).contains("Name=John Smith"));
+ try {
+ final Logger logger = Logger.getLogger("org.apache.test.logging");
+ logger.debug("This is a test");
+ assertNotNull(listApp);
+ final List msgs = listApp.getMessages();
+ assertNotNull(msgs, "No messages received");
+ assertEquals(1, msgs.size());
+ assertTrue(msgs.get(0).contains("Type=Service"), "Type is missing");
+ assertTrue(msgs.get(0).contains("Name=John Smith"), "Name is missing");
+ } finally {
+ MDC.remove("Type");
+ MDC.remove("Name");
+ }
}
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerJira3410Test.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerJira3410Test.java
new file mode 100644
index 00000000000..be16d3d0953
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerJira3410Test.java
@@ -0,0 +1,80 @@
+/*
+ * 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.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.config.TestConfigurator;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests Jira3410.
+ */
+class LoggerJira3410Test {
+
+ @Test
+ void test() throws Exception {
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/log4j1-list.properties")) {
+ final Logger logger = LogManager.getLogger("test");
+ //
+ final Map map = new HashMap<>(1);
+ map.put(Long.MAX_VALUE, 1);
+ logger.debug(map);
+ //
+ map.put(null, null);
+ logger.debug(map);
+ //
+ logger.debug(new SortedArrayStringMap((Map) map));
+ //
+ final Configuration configuration = loggerContext.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender listAppender = null;
+ for (final Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ listAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(listAppender, "No Message Appender");
+ final List messages = listAppender.getMessages();
+ assertTrue(messages != null && !messages.isEmpty(), "No messages");
+ final String msg0 = messages.get(0);
+ final String msg1 = messages.get(1);
+ final String msg2 = messages.get(2);
+ // TODO Should be 1, not "1".
+ // TODO Where are the {} characters?
+ assertTrue(msg0.trim().endsWith(Long.MAX_VALUE + "=\"1\""), msg0);
+ //
+ // TODO Should be 1, not "1".
+ // TODO Should be null, not "null".
+ // TODO Where are the {} characters?
+ // TODO Where is the , characters?
+ assertTrue(msg1.trim().endsWith("null=\"null\" " + Long.MAX_VALUE + "=\"1\""), msg1);
+ //
+ assertTrue(msg2.trim().endsWith("{null=null, " + Long.MAX_VALUE + "=1}"), msg2);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java
index d21fa6243df..f1f93599d8b 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggerTest.java
@@ -2,7 +2,7 @@
* 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 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
*
@@ -14,31 +14,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.log4j;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.ResourceBundle;
-
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.layout.PatternLayout;
-import org.apache.logging.log4j.test.appender.ListAppender;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
/**
* Used for internal unit testing the Logger class.
*/
-public class LoggerTest {
+class LoggerTest {
Appender a1;
Appender a2;
@@ -52,28 +60,28 @@ public class LoggerTest {
static ConfigurationFactory configurationFactory = new BasicConfigurationFactory();
- @BeforeClass
- public static void setUpClass() {
+ @BeforeAll
+ static void setUpAll() {
rbUS = ResourceBundle.getBundle("L7D", new Locale("en", "US"));
assertNotNull(rbUS);
rbFR = ResourceBundle.getBundle("L7D", new Locale("fr", "FR"));
- assertNotNull("Got a null resource bundle.", rbFR);
+ assertNotNull(rbFR, "Got a null resource bundle.");
rbCH = ResourceBundle.getBundle("L7D", new Locale("fr", "CH"));
- assertNotNull("Got a null resource bundle.", rbCH);
+ assertNotNull(rbCH, "Got a null resource bundle.");
ConfigurationFactory.setConfigurationFactory(configurationFactory);
}
- @AfterClass
- public static void tearDownClass() {
+ @AfterAll
+ static void tearDownAll() {
ConfigurationFactory.removeConfigurationFactory(configurationFactory);
}
- @After
- public void tearDown() {
- LoggerContext.getContext().reconfigure();
+ @BeforeEach
+ void resetTest() {
+ Objects.requireNonNull(LogManager.getHierarchy()).resetConfiguration();
a1 = null;
a2 = null;
}
@@ -81,47 +89,47 @@ public void tearDown() {
/**
* Add an appender and see if it can be retrieved.
* Skipping this test as the Appender interface isn't compatible with legacy Log4j.
- public void testAppender1() {
- logger = Logger.getLogger("test");
- a1 = new ListAppender("testAppender1");
- logger.addAppender(a1);
-
- Enumeration enumeration = logger.getAllAppenders();
- Appender aHat = (Appender) enumeration.nextElement();
- assertEquals(a1, aHat);
- } */
+ * public void testAppender1() {
+ * logger = Logger.getLogger("test");
+ * a1 = new ListAppender("testAppender1");
+ * logger.addAppender(a1);
+ *
+ * Enumeration enumeration = logger.getAllAppenders();
+ * Appender aHat = (Appender) enumeration.nextElement();
+ * assertEquals(a1, aHat);
+ * } */
/**
* Add an appender X, Y, remove X and check if Y is the only
* remaining appender.
* Skipping this test as the Appender interface isn't compatible with legacy Log4j.
- public void testAppender2() {
- a1 = new FileAppender();
- a1.setName("testAppender2.1");
- a2 = new FileAppender();
- a2.setName("testAppender2.2");
-
- logger = Logger.getLogger("test");
- logger.addAppender(a1);
- logger.addAppender(a2);
- logger.removeAppender("testAppender2.1");
- Enumeration enumeration = logger.getAllAppenders();
- Appender aHat = (Appender) enumeration.nextElement();
- assertEquals(a2, aHat);
- assertTrue(!enumeration.hasMoreElements());
- } */
+ * public void testAppender2() {
+ * a1 = new FileAppender();
+ * a1.setName("testAppender2.1");
+ * a2 = new FileAppender();
+ * a2.setName("testAppender2.2");
+ *
+ * logger = Logger.getLogger("test");
+ * logger.addAppender(a1);
+ * logger.addAppender(a2);
+ * logger.removeAppender("testAppender2.1");
+ * Enumeration enumeration = logger.getAllAppenders();
+ * Appender aHat = (Appender) enumeration.nextElement();
+ * assertEquals(a2, aHat);
+ * assertTrue(!enumeration.hasMoreElements());
+ * } */
/**
* Test if logger a.b inherits its appender from a.
*/
@Test
- public void testAdditivity1() {
+ void testAdditivity1() {
final Logger loggerA = Logger.getLogger("a");
final Logger loggerAB = Logger.getLogger("a.b");
final CountingAppender coutingAppender = new CountingAppender();
coutingAppender.start();
try {
- loggerA.getLogger().addAppender(coutingAppender);
+ ((org.apache.logging.log4j.core.Logger) loggerA.getLogger()).addAppender(coutingAppender);
assertEquals(0, coutingAppender.counter);
loggerAB.debug(MSG);
@@ -134,7 +142,7 @@ public void testAdditivity1() {
assertEquals(4, coutingAppender.counter);
coutingAppender.stop();
} finally {
- loggerA.getLogger().removeAppender(coutingAppender);
+ ((org.apache.logging.log4j.core.Logger) loggerA.getLogger()).removeAppender(coutingAppender);
}
}
@@ -142,7 +150,7 @@ public void testAdditivity1() {
* Test multiple additivity.
*/
@Test
- public void testAdditivity2() {
+ void testAdditivity2() {
final Logger a = Logger.getLogger("a");
final Logger ab = Logger.getLogger("a.b");
final Logger abc = Logger.getLogger("a.b.c");
@@ -154,35 +162,36 @@ public void testAdditivity2() {
ca2.start();
try {
- a.getLogger().addAppender(ca1);
- abc.getLogger().addAppender(ca2);
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).addAppender(ca1);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).addAppender(ca2);
- assertEquals(ca1.counter, 0);
- assertEquals(ca2.counter, 0);
+ assertEquals(0, ca1.counter);
+ assertEquals(0, ca2.counter);
ab.debug(MSG);
- assertEquals(ca1.counter, 1);
- assertEquals(ca2.counter, 0);
+ assertEquals(1, ca1.counter);
+ assertEquals(0, ca2.counter);
abc.debug(MSG);
- assertEquals(ca1.counter, 2);
- assertEquals(ca2.counter, 1);
+ assertEquals(2, ca1.counter);
+ assertEquals(1, ca2.counter);
x.debug(MSG);
- assertEquals(ca1.counter, 2);
- assertEquals(ca2.counter, 1);
+ assertEquals(2, ca1.counter);
+ assertEquals(1, ca2.counter);
ca1.stop();
ca2.stop();
} finally {
- a.getLogger().removeAppender(ca1);
- abc.getLogger().removeAppender(ca2);
- }}
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).removeAppender(ca1);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).removeAppender(ca2);
+ }
+ }
/**
* Test additivity flag.
*/
@Test
- public void testAdditivity3() {
+ void testAdditivity3() {
final Logger root = Logger.getRootLogger();
final Logger a = Logger.getLogger("a");
final Logger ab = Logger.getLogger("a.b");
@@ -196,38 +205,39 @@ public void testAdditivity3() {
final CountingAppender caABC = new CountingAppender();
caABC.start();
try {
- root.getLogger().addAppender(caRoot);
- a.getLogger().addAppender(caA);
- abc.getLogger().addAppender(caABC);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(caRoot);
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).addAppender(caA);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).addAppender(caABC);
- assertEquals(caRoot.counter, 0);
- assertEquals(caA.counter, 0);
- assertEquals(caABC.counter, 0);
+ assertEquals(0, caRoot.counter);
+ assertEquals(0, caA.counter);
+ assertEquals(0, caABC.counter);
ab.setAdditivity(false);
a.debug(MSG);
- assertEquals(caRoot.counter, 1);
- assertEquals(caA.counter, 1);
- assertEquals(caABC.counter, 0);
+ assertEquals(1, caRoot.counter);
+ assertEquals(1, caA.counter);
+ assertEquals(0, caABC.counter);
ab.debug(MSG);
- assertEquals(caRoot.counter, 1);
- assertEquals(caA.counter, 1);
- assertEquals(caABC.counter, 0);
+ assertEquals(1, caRoot.counter);
+ assertEquals(1, caA.counter);
+ assertEquals(0, caABC.counter);
abc.debug(MSG);
- assertEquals(caRoot.counter, 1);
- assertEquals(caA.counter, 1);
- assertEquals(caABC.counter, 1);
+ assertEquals(1, caRoot.counter);
+ assertEquals(1, caA.counter);
+ assertEquals(1, caABC.counter);
caRoot.stop();
caA.stop();
caABC.stop();
} finally {
- root.getLogger().removeAppender(caRoot);
- a.getLogger().removeAppender(caA);
- abc.getLogger().removeAppender(caABC);
- }}
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(caRoot);
+ ((org.apache.logging.log4j.core.Logger) a.getLogger()).removeAppender(caA);
+ ((org.apache.logging.log4j.core.Logger) abc.getLogger()).removeAppender(caABC);
+ }
+ }
/* Don't support getLoggerRepository
public void testDisable1() {
@@ -294,7 +304,7 @@ public void testDisable1() {
} */
@Test
- public void testRB1() {
+ void testRB1() {
final Logger root = Logger.getRootLogger();
root.setResourceBundle(rbUS);
ResourceBundle t = root.getResourceBundle();
@@ -313,11 +323,11 @@ public void testRB1() {
}
@Test
- public void testRB2() {
+ void testRB2() {
final Logger root = Logger.getRootLogger();
root.setResourceBundle(rbUS);
ResourceBundle t = root.getResourceBundle();
- assertTrue(t == rbUS);
+ assertEquals(t, rbUS);
final Logger x = Logger.getLogger("x");
final Logger x_y = Logger.getLogger("x.y");
@@ -333,11 +343,11 @@ public void testRB2() {
}
@Test
- public void testRB3() {
+ void testRB3() {
final Logger root = Logger.getRootLogger();
root.setResourceBundle(rbUS);
ResourceBundle t = root.getResourceBundle();
- assertTrue(t == rbUS);
+ assertEquals(t, rbUS);
final Logger x = Logger.getLogger("x");
final Logger x_y = Logger.getLogger("x.y");
@@ -354,7 +364,7 @@ public void testRB3() {
}
@Test
- public void testExists() {
+ void testExists() {
final Logger a = Logger.getLogger("a");
final Logger a_b = Logger.getLogger("a.b");
final Logger a_b_c = Logger.getLogger("a.b.c");
@@ -369,6 +379,7 @@ public void testExists() {
t = LogManager.exists("a.b.c");
assertSame(a_b_c, t);
}
+
/* Don't support hierarchy
public void testHierarchy1() {
Hierarchy h = new Hierarchy(new RootLogger((Level) Level.ERROR));
@@ -385,11 +396,11 @@ public void testHierarchy1() {
* Tests logger.trace(Object).
*/
@Test
- public void testTrace() {
+ void testTrace() {
final ListAppender appender = new ListAppender("List");
appender.start();
final Logger root = Logger.getRootLogger();
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final Logger tracer = Logger.getLogger("com.example.Tracer");
@@ -405,19 +416,19 @@ public void testTrace() {
assertEquals(org.apache.logging.log4j.Level.TRACE, event.getLevel());
assertEquals("Message 1", event.getMessage().getFormat());
appender.stop();
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
}
/**
* Tests logger.trace(Object, Exception).
*/
@Test
- public void testTraceWithException() {
+ void testTraceWithException() {
final ListAppender appender = new ListAppender("List");
appender.start();
final Logger root = Logger.getRootLogger();
try {
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final Logger tracer = Logger.getLogger("com.example.Tracer");
@@ -435,7 +446,7 @@ public void testTraceWithException() {
assertEquals("Message 1", event.getMessage().getFormattedMessage());
appender.stop();
} finally {
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
}
}
@@ -443,12 +454,12 @@ public void testTraceWithException() {
* Tests isTraceEnabled.
*/
@Test
- public void testIsTraceEnabled() {
+ void testIsTraceEnabled() {
final ListAppender appender = new ListAppender("List");
appender.start();
final Logger root = Logger.getRootLogger();
try {
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final Logger tracer = Logger.getLogger("com.example.Tracer");
@@ -458,31 +469,82 @@ public void testIsTraceEnabled() {
assertFalse(root.isTraceEnabled());
appender.stop();
} finally {
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
}
}
@Test
@SuppressWarnings("deprecation")
- public void testLog() {
- final PatternLayout layout = PatternLayout.newBuilder().withPattern("%d %C %L %m").build();
+ void testLog() {
+ final PatternLayout layout =
+ PatternLayout.newBuilder().setPattern("%d %C %L %m").build();
final ListAppender appender = new ListAppender("List", null, layout, false, false);
appender.start();
final Logger root = Logger.getRootLogger();
try {
- root.getLogger().addAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).addAppender(appender);
root.setLevel(Level.INFO);
final MyLogger log = new MyLogger(root);
log.logInfo("This is a test", null);
root.log(Priority.INFO, "Test msg2", null);
root.log(Priority.INFO, "Test msg3");
final List msgs = appender.getMessages();
- assertTrue("Incorrect number of messages", msgs.size() == 3);
+ assertEquals(3, msgs.size(), "Incorrect number of messages");
final String msg = msgs.get(0);
- assertTrue("Message contains incorrect class name: " + msg, msg.contains(LoggerTest.class.getName()));
+ assertTrue(msg.contains(LoggerTest.class.getName()), "Message contains incorrect class name: " + msg);
appender.stop();
} finally {
- root.getLogger().removeAppender(appender);
+ ((org.apache.logging.log4j.core.Logger) root.getLogger()).removeAppender(appender);
+ }
+ }
+
+ @Test
+ void testSetLevel() {
+ final Logger a = Logger.getLogger("a");
+ final Logger a_b = Logger.getLogger("a.b");
+ final Logger a_b_c = Logger.getLogger("a.b.c");
+ // test default for this test
+ assertThat(a.getLevel(), nullValue());
+ assertThat(a_b.getLevel(), nullValue());
+ assertThat(a_b_c.getLevel(), nullValue());
+ assertThat(a.getEffectiveLevel(), is(equalTo(Level.DEBUG)));
+ assertThat(a_b.getEffectiveLevel(), is(equalTo(Level.DEBUG)));
+ assertThat(a_b_c.getEffectiveLevel(), is(equalTo(Level.DEBUG)));
+
+ // all
+ for (final Level level :
+ new Level[] {Level.DEBUG, Level.ERROR, Level.FATAL, Level.INFO, Level.TRACE, Level.WARN}) {
+ a.setLevel(level);
+ assertThat(a.getLevel(), is(equalTo(level)));
+ assertThat(a_b.getLevel(), nullValue());
+ assertThat(a_b.getEffectiveLevel(), is(equalTo(level)));
+ assertThat(a_b_c.getLevel(), nullValue());
+ assertThat(a_b_c.getEffectiveLevel(), is(equalTo(level)));
+ }
+ }
+
+ @Test
+ void testSetPriority() {
+ final Logger a = Logger.getLogger("a");
+ final Logger a_b = Logger.getLogger("a.b");
+ final Logger a_b_c = Logger.getLogger("a.b.c");
+ // test default for this test
+ assertThat(a.getPriority(), nullValue());
+ assertThat(a_b.getPriority(), nullValue());
+ assertThat(a_b_c.getPriority(), nullValue());
+
+ assertThat(a.getEffectiveLevel(), is(equalTo(Level.DEBUG)));
+ assertThat(a_b.getEffectiveLevel(), is(equalTo(Level.DEBUG)));
+ assertThat(a_b_c.getEffectiveLevel(), is(equalTo(Level.DEBUG)));
+
+ // all
+ for (final Priority level : Level.getAllPossiblePriorities()) {
+ a.setPriority(level);
+ assertThat(a.getPriority(), is(equalTo(level)));
+ assertThat(a_b.getPriority(), nullValue());
+ assertThat(a_b.getEffectiveLevel(), is(equalTo(level)));
+ assertThat(a_b.getPriority(), nullValue());
+ assertThat(a_b_c.getEffectiveLevel(), is(equalTo(level)));
}
}
@@ -507,7 +569,7 @@ private static class CountingAppender extends AbstractAppender {
int counter;
CountingAppender() {
- super("Counter", null, null);
+ super("Counter", null, null, true, Property.EMPTY_ARRAY);
counter = 0;
}
@@ -521,4 +583,3 @@ public boolean requiresLayout() {
}
}
}
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java
index a30dd881bf5..0e77fd8962e 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/LoggingTest.java
@@ -1,43 +1,38 @@
-/*
- * 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.log4j;
-
-import org.apache.logging.log4j.junit.LoggerContextRule;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- *
- */
-public class LoggingTest {
-
- private static final String CONFIG = "log4j2-config.xml";
-
- @ClassRule
- public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG);
-
- @Test
- public void testParent() {
- final Logger logger = Logger.getLogger("org.apache.test.logging.Test");
- final Category parent = logger.getParent();
- assertNotNull("No parent Logger", parent);
- assertEquals("Incorrect parent logger", "org.apache.test.logging", parent.getName());
- }
-
-}
+/*
+ * 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.log4j;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.apache.logging.log4j.core.test.junit.LoggerContextSource;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ */
+@LoggerContextSource("log4j2-config.xml")
+class LoggingTest {
+
+ @Test
+ void testParent() {
+ final Logger logger = Logger.getLogger("org.apache.test.logging.Test");
+ final Category parent = logger.getParent();
+ assertNotNull(parent, "No parent Logger");
+ assertEquals("org.apache.test.logging", parent.getName(), "Incorrect parent logger");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
index c0e5ba59418..b90b59ae671 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/MDCTestCase.java
@@ -2,12 +2,12 @@
* 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 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.
@@ -16,34 +16,34 @@
*/
package org.apache.log4j;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-public class MDCTestCase {
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
- @Before
- public void setUp() {
+class MDCTestCase {
+
+ @BeforeEach
+ void setUp() {
MDC.clear();
}
- @After
- public void tearDown() {
+ @AfterEach
+ void tearDown() {
MDC.clear();
}
@Test
- public void testPut() throws Exception {
+ void testPut() {
MDC.put("key", "some value");
- Assert.assertEquals("some value", MDC.get("key"));
- Assert.assertEquals(1, MDC.getContext().size());
+ assertEquals("some value", MDC.get("key"));
+ assertEquals(1, MDC.getContext().size());
}
@Test
- public void testRemoveLastKey() throws Exception {
+ void testRemoveLastKey() {
MDC.put("key", "some value");
MDC.remove("key");
}
-
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java
index c8baf0ff9ba..30173b5e2ee 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/NDCTest.java
@@ -1,36 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Stack;
import org.apache.logging.log4j.util.Strings;
-import org.junit.Assert;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-public class NDCTest {
+class NDCTest {
@Test
- public void testPopEmpty() {
+ void testPopEmpty() {
NDC.clear();
- Assert.assertEquals(Strings.EMPTY, NDC.pop());
+ assertEquals(Strings.EMPTY, NDC.pop());
}
@Test
- public void testPeekEmpty() {
+ void testPeekEmpty() {
NDC.clear();
- Assert.assertEquals(Strings.EMPTY, NDC.peek());
+ assertEquals(Strings.EMPTY, NDC.peek());
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ @Test
+ void testCompileCloneToInherit() {
+ NDC.inherit(NDC.cloneStack());
+ final Stack stackRaw = NDC.cloneStack();
+ NDC.inherit(stackRaw);
+ final Stack> stackAny = NDC.cloneStack();
+ NDC.inherit(stackAny);
}
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java
index 63321a74d7e..7d649fdc9b5 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/PriorityTest.java
@@ -2,7 +2,7 @@
* 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 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
*
@@ -14,26 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.log4j;
-import java.util.Locale;
-
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.Assert.*;
+import java.util.Locale;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
/**
* Tests of Priority.
*
*/
-public class PriorityTest {
+class PriorityTest {
/**
* Tests Priority.OFF_INT.
*/
@Test
- public void testOffInt() {
+ void testOffInt() {
assertEquals(Integer.MAX_VALUE, Priority.OFF_INT);
}
@@ -41,7 +45,7 @@ public void testOffInt() {
* Tests Priority.FATAL_INT.
*/
@Test
- public void testFatalInt() {
+ void testFatalInt() {
assertEquals(50000, Priority.FATAL_INT);
}
@@ -49,7 +53,7 @@ public void testFatalInt() {
* Tests Priority.ERROR_INT.
*/
@Test
- public void testErrorInt() {
+ void testErrorInt() {
assertEquals(40000, Priority.ERROR_INT);
}
@@ -57,7 +61,7 @@ public void testErrorInt() {
* Tests Priority.WARN_INT.
*/
@Test
- public void testWarnInt() {
+ void testWarnInt() {
assertEquals(30000, Priority.WARN_INT);
}
@@ -65,7 +69,7 @@ public void testWarnInt() {
* Tests Priority.INFO_INT.
*/
@Test
- public void testInfoInt() {
+ void testInfoInt() {
assertEquals(20000, Priority.INFO_INT);
}
@@ -73,7 +77,7 @@ public void testInfoInt() {
* Tests Priority.DEBUG_INT.
*/
@Test
- public void testDebugInt() {
+ void testDebugInt() {
assertEquals(10000, Priority.DEBUG_INT);
}
@@ -81,17 +85,36 @@ public void testDebugInt() {
* Tests Priority.ALL_INT.
*/
@Test
- public void testAllInt() {
+ void testAllInt() {
assertEquals(Integer.MIN_VALUE, Priority.ALL_INT);
}
+ @SuppressWarnings("deprecation")
+ static Stream testVersion2Level() {
+ return Stream.of(
+ Arguments.of(Priority.FATAL, org.apache.logging.log4j.Level.FATAL),
+ Arguments.of(Priority.ERROR, org.apache.logging.log4j.Level.ERROR),
+ Arguments.of(Priority.WARN, org.apache.logging.log4j.Level.WARN),
+ Arguments.of(Priority.INFO, org.apache.logging.log4j.Level.INFO),
+ Arguments.of(Priority.DEBUG, org.apache.logging.log4j.Level.DEBUG));
+ }
+
+ /**
+ * Tests version2Level.
+ */
+ @ParameterizedTest
+ @MethodSource()
+ void testVersion2Level(final Priority log4j1Priority, final org.apache.logging.log4j.Level log4j2Level) {
+ assertEquals(log4j2Level, log4j1Priority.getVersion2Level());
+ }
+
/**
* Tests Priority.FATAL.
*/
@Test
@SuppressWarnings("deprecation")
- public void testFatal() {
- assertTrue(Priority.FATAL instanceof Level);
+ void testFATAL() {
+ assertFalse(Priority.FATAL instanceof Level);
}
/**
@@ -99,8 +122,8 @@ public void testFatal() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testERROR() {
- assertTrue(Priority.ERROR instanceof Level);
+ void testERROR() {
+ assertFalse(Priority.ERROR instanceof Level);
}
/**
@@ -108,8 +131,8 @@ public void testERROR() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testWARN() {
- assertTrue(Priority.WARN instanceof Level);
+ void testWARN() {
+ assertFalse(Priority.WARN instanceof Level);
}
/**
@@ -117,8 +140,8 @@ public void testWARN() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testINFO() {
- assertTrue(Priority.INFO instanceof Level);
+ void testINFO() {
+ assertFalse(Priority.INFO instanceof Level);
}
/**
@@ -126,8 +149,8 @@ public void testINFO() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testDEBUG() {
- assertTrue(Priority.DEBUG instanceof Level);
+ void testDEBUG() {
+ assertFalse(Priority.DEBUG instanceof Level);
}
/**
@@ -135,8 +158,8 @@ public void testDEBUG() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testEqualsNull() {
- assertFalse(Priority.DEBUG.equals(null));
+ void testEqualsNull() {
+ assertNotEquals(null, Priority.DEBUG);
}
/**
@@ -144,11 +167,11 @@ public void testEqualsNull() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testEqualsLevel() {
+ void testEqualsLevel() {
//
// this behavior violates the equals contract.
//
- assertTrue(Priority.DEBUG.equals(Level.DEBUG));
+ assertEquals(Priority.DEBUG, Level.DEBUG);
}
/**
@@ -156,7 +179,7 @@ public void testEqualsLevel() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testGetAllPossiblePriorities() {
+ void testGetAllPossiblePriorities() {
final Priority[] priorities = Priority.getAllPossiblePriorities();
assertEquals(5, priorities.length);
}
@@ -166,8 +189,8 @@ public void testGetAllPossiblePriorities() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testToPriorityString() {
- assertTrue(Priority.toPriority("DEBUG") == Level.DEBUG);
+ void testToPriorityString() {
+ assertEquals(Level.DEBUG, Priority.toPriority("DEBUG"));
}
/**
@@ -175,8 +198,8 @@ public void testToPriorityString() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testToPriorityInt() {
- assertTrue(Priority.toPriority(Priority.DEBUG_INT) == Level.DEBUG);
+ void testToPriorityInt() {
+ assertEquals(Level.DEBUG, Priority.toPriority(Priority.DEBUG_INT));
}
/**
@@ -184,8 +207,8 @@ public void testToPriorityInt() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testToPriorityStringPriority() {
- assertTrue(Priority.toPriority("foo", Priority.DEBUG) == Priority.DEBUG);
+ void testToPriorityStringPriority() {
+ assertEquals(Priority.DEBUG, Priority.toPriority("foo", Priority.DEBUG));
}
/**
@@ -193,8 +216,8 @@ public void testToPriorityStringPriority() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testToPriorityIntPriority() {
- assertTrue(Priority.toPriority(17, Priority.DEBUG) == Priority.DEBUG);
+ void testToPriorityIntPriority() {
+ assertEquals(Priority.DEBUG, Priority.toPriority(17, Priority.DEBUG));
}
/**
@@ -202,7 +225,7 @@ public void testToPriorityIntPriority() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testDotlessLowerI() {
+ void testDotlessLowerI() {
final Priority level = Priority.toPriority("\u0131nfo");
assertEquals("INFO", level.toString());
}
@@ -213,14 +236,12 @@ public void testDotlessLowerI() {
*/
@Test
@SuppressWarnings("deprecation")
- public void testDottedLowerI() {
+ void testDottedLowerI() {
final Locale defaultLocale = Locale.getDefault();
final Locale turkey = new Locale("tr", "TR");
Locale.setDefault(turkey);
final Priority level = Priority.toPriority("info");
Locale.setDefault(defaultLocale);
assertEquals("INFO", level.toString());
- }
-
+ }
}
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
new file mode 100644
index 00000000000..b80296f898f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
@@ -0,0 +1,386 @@
+/*
+ * 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.log4j;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link PropertyConfigurator}.
+ */
+@SetTestProperty(key = "log4j1.compatibility", value = "true")
+class PropertyConfiguratorTest {
+
+ /**
+ * Mock definition of FilterBasedTriggeringPolicy from extras companion.
+ */
+ public static final class FilterBasedTriggeringPolicy extends TriggeringPolicy {
+ private Filter filter;
+
+ public FilterBasedTriggeringPolicy() {}
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public void setFilter(final Filter val) {
+ filter = val;
+ }
+ }
+
+ /**
+ * Mock definition of FixedWindowRollingPolicy from extras companion.
+ */
+ public static final class FixedWindowRollingPolicy extends RollingPolicy {
+ private String activeFileName;
+ private String fileNamePattern;
+ private int minIndex;
+
+ public FixedWindowRollingPolicy() {
+ minIndex = -1;
+ }
+
+ public String getActiveFileName() {
+ return activeFileName;
+ }
+
+ public String getFileNamePattern() {
+ return fileNamePattern;
+ }
+
+ public int getMinIndex() {
+ return minIndex;
+ }
+
+ public void setActiveFileName(final String val) {
+ activeFileName = val;
+ }
+
+ public void setFileNamePattern(final String val) {
+ fileNamePattern = val;
+ }
+
+ public void setMinIndex(final int val) {
+ minIndex = val;
+ }
+ }
+
+ /**
+ * Mock ThrowableRenderer for testThrowableRenderer. See bug 45721.
+ */
+ public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+ private boolean activated = false;
+ private boolean showVersion = true;
+
+ public MockThrowableRenderer() {}
+
+ @Override
+ public void activateOptions() {
+ activated = true;
+ }
+
+ @Override
+ public String[] doRender(final Throwable t) {
+ return new String[0];
+ }
+
+ public boolean getShowVersion() {
+ return showVersion;
+ }
+
+ public boolean isActivated() {
+ return activated;
+ }
+
+ public void setShowVersion(final boolean v) {
+ showVersion = v;
+ }
+ }
+
+ /**
+ * Mock definition of org.apache.log4j.rolling.RollingFileAppender from extras companion.
+ */
+ public static final class RollingFileAppender extends AppenderSkeleton {
+ private RollingPolicy rollingPolicy;
+ private TriggeringPolicy triggeringPolicy;
+ private boolean append;
+
+ public RollingFileAppender() {}
+
+ @Override
+ public void append(final LoggingEvent event) {}
+
+ @Override
+ public void close() {}
+
+ public boolean getAppend() {
+ return append;
+ }
+
+ public RollingPolicy getRollingPolicy() {
+ return rollingPolicy;
+ }
+
+ public TriggeringPolicy getTriggeringPolicy() {
+ return triggeringPolicy;
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ public void setAppend(final boolean val) {
+ append = val;
+ }
+
+ public void setRollingPolicy(final RollingPolicy policy) {
+ rollingPolicy = policy;
+ }
+
+ public void setTriggeringPolicy(final TriggeringPolicy policy) {
+ triggeringPolicy = policy;
+ }
+ }
+
+ /**
+ * Mock definition of org.apache.log4j.rolling.RollingPolicy from extras companion.
+ */
+ public static class RollingPolicy implements OptionHandler {
+ private boolean activated = false;
+
+ public RollingPolicy() {}
+
+ @Override
+ public void activateOptions() {
+ activated = true;
+ }
+
+ public final boolean isActivated() {
+ return activated;
+ }
+ }
+
+ /**
+ * Mock definition of TriggeringPolicy from extras companion.
+ */
+ public static class TriggeringPolicy implements OptionHandler {
+ private boolean activated = false;
+
+ public TriggeringPolicy() {}
+
+ @Override
+ public void activateOptions() {
+ activated = true;
+ }
+
+ public final boolean isActivated() {
+ return activated;
+ }
+ }
+
+ private static final String BAD_ESCAPE_PROPERTIES = "/PropertyConfiguratorTest/badEscape.properties";
+ private static final String FILTER_PROPERTIES = "/PropertyConfiguratorTest/filter.properties";
+
+ private static final String CAT_A_NAME = "categoryA";
+
+ private static final String CAT_B_NAME = "categoryB";
+
+ private static final String CAT_C_NAME = "categoryC";
+
+ @AfterEach
+ void cleanup() {
+ LogManager.resetConfiguration();
+ }
+
+ /**
+ * Test for bug 40944. Did not catch IllegalArgumentException on Properties.load and close input stream.
+ *
+ * @throws IOException if IOException creating properties file.
+ */
+ @Test
+ void testBadUnicodeEscape() throws IOException {
+ try (final InputStream is = PropertyConfiguratorTest.class.getResourceAsStream(BAD_ESCAPE_PROPERTIES)) {
+ PropertyConfigurator.configure(is);
+ }
+ }
+
+ /**
+ * Tests configuring Log4J from an InputStream.
+ *
+ * @since 1.2.17
+ */
+ @Test
+ void testInputStream() throws IOException {
+ try (final InputStream inputStream = PropertyConfiguratorTest.class.getResourceAsStream(FILTER_PROPERTIES)) {
+ PropertyConfigurator.configure(inputStream);
+
+ final Logger rootLogger = Logger.getRootLogger();
+ assertThat(rootLogger.getLevel(), is(equalTo(Level.INFO)));
+ assertThat(rootLogger.getAppender("CONSOLE"), notNullValue());
+ final Logger logger = Logger.getLogger("org.apache.log4j.PropertyConfiguratorTest");
+ assertThat(logger.getLevel(), is(equalTo(Level.DEBUG)));
+ assertThat(logger.getAppender("ROLLING"), notNullValue());
+ }
+ }
+
+ /**
+ * Test for bug 47465. configure(URL) did not close opened JarURLConnection.
+ *
+ * @throws IOException if IOException creating properties jar.
+ */
+ @Test
+ void testJarURL() throws IOException {
+ final File dir = new File("output");
+ dir.mkdirs();
+ final File file = new File("output/properties.jar");
+ try (final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file))) {
+ zos.putNextEntry(new ZipEntry(LogManager.DEFAULT_CONFIGURATION_FILE));
+ zos.write("log4j.rootLogger=debug".getBytes());
+ zos.closeEntry();
+ }
+ final URL url = new URL("jar:" + file.toURI().toURL() + "!/" + LogManager.DEFAULT_CONFIGURATION_FILE);
+ PropertyConfigurator.configure(url);
+ assertTrue(file.delete());
+ assertFalse(file.exists());
+ }
+
+ @Test
+ void testLocalVsGlobal() {
+ LoggerRepository repos1, repos2;
+ final Logger catA = Logger.getLogger(CAT_A_NAME);
+ final Logger catB = Logger.getLogger(CAT_B_NAME);
+ final Logger catC = Logger.getLogger(CAT_C_NAME);
+
+ final Properties globalSettings = new Properties();
+ globalSettings.put("log4j.logger." + CAT_A_NAME, Level.WARN.toString());
+ globalSettings.put("log4j.logger." + CAT_B_NAME, Level.WARN.toString());
+ globalSettings.put("log4j.logger." + CAT_C_NAME, Level.DEBUG.toString());
+ PropertyConfigurator.configure(globalSettings);
+ assertEquals(Level.WARN, catA.getLevel());
+ assertEquals(Level.WARN, catB.getLevel());
+ assertEquals(Level.DEBUG, catC.getLevel());
+
+ assertEquals(
+ Level.WARN, catA.getLoggerRepository().getLogger(CAT_A_NAME).getLevel());
+ assertEquals(
+ Level.WARN, catB.getLoggerRepository().getLogger(CAT_B_NAME).getLevel());
+ assertEquals(
+ Level.DEBUG, catC.getLoggerRepository().getLogger(CAT_C_NAME).getLevel());
+
+ final Properties repos1Settings = new Properties();
+ repos1Settings.put("log4j.logger." + CAT_A_NAME, Level.DEBUG.toString());
+ repos1Settings.put("log4j.logger." + CAT_B_NAME, Level.INFO.toString());
+ repos1 = new Hierarchy(new RootLogger(Level.OFF));
+ new PropertyConfigurator().doConfigure(repos1Settings, repos1);
+ assertEquals(Level.DEBUG, repos1.getLogger(CAT_A_NAME).getLevel());
+ assertEquals(Level.INFO, repos1.getLogger(CAT_B_NAME).getLevel());
+
+ final Properties repos2Settings = new Properties();
+ repos2Settings.put("log4j.logger." + CAT_A_NAME, Level.INFO.toString());
+ repos2Settings.put("log4j.logger." + CAT_B_NAME, Level.DEBUG.toString());
+ repos2 = new Hierarchy(new RootLogger(Level.OFF));
+ new PropertyConfigurator().doConfigure(repos2Settings, repos2);
+ assertEquals(Level.INFO, repos2.getLogger(CAT_A_NAME).getLevel());
+ assertEquals(Level.DEBUG, repos2.getLogger(CAT_B_NAME).getLevel());
+ }
+
+ /**
+ * Test processing of log4j.reset property, see bug 17531.
+ */
+ @Test
+ void testReset() {
+ final VectorAppender appender = new VectorAppender();
+ appender.setName("A1");
+ Logger.getRootLogger().addAppender(appender);
+ final Properties properties = new Properties();
+ properties.put("log4j.reset", "true");
+ PropertyConfigurator.configure(properties);
+ assertNull(Logger.getRootLogger().getAppender("A1"));
+ }
+
+ /**
+ * Test for bug 40944. configure(URL) never closed opened stream.
+ *
+ * @throws IOException if IOException creating properties file.
+ */
+ @Test
+ void testURL() throws IOException {
+ final File file = new File("target/unclosed.properties");
+ try (final FileWriter writer = new FileWriter(file)) {
+ writer.write("log4j.rootLogger=debug");
+ }
+ final URL url = file.toURI().toURL();
+ PropertyConfigurator.configure(url);
+ assertTrue(file.delete());
+ assertFalse(file.exists());
+ }
+
+ /**
+ * Test for bug 40944. configure(URL) did not catch IllegalArgumentException and did not close stream.
+ *
+ */
+ @Test
+ void testURLBadEscape() {
+ final URL configURL = PropertyConfiguratorTest.class.getResource(BAD_ESCAPE_PROPERTIES);
+ PropertyConfigurator.configure(configURL);
+ }
+
+ @Test
+ @SetTestProperty(key = "log4j1.compatibility", value = "false")
+ void when_compatibility_disabled_configurator_is_no_op() throws IOException {
+ final Logger rootLogger = Logger.getRootLogger();
+ final Logger logger = Logger.getLogger("org.apache.log4j.PropertyConfiguratorTest");
+ assertThat(logger.getLevel(), nullValue());
+ try (final InputStream inputStream = PropertyConfiguratorTest.class.getResourceAsStream(FILTER_PROPERTIES)) {
+ PropertyConfigurator.configure(inputStream);
+
+ assertThat(rootLogger.getAppender("CONSOLE"), nullValue());
+ assertThat(rootLogger.getLevel(), is(not(equalTo(Level.INFO))));
+
+ assertThat(logger.getAppender("ROLLING"), nullValue());
+ assertThat(logger.getLevel(), nullValue());
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/VelocityTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/VelocityTest.java
deleted file mode 100644
index 99bd5a2c7c6..00000000000
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/VelocityTest.java
+++ /dev/null
@@ -1,61 +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.log4j;
-
-import java.io.StringWriter;
-
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configurator;
-import org.apache.logging.log4j.status.StatusLogger;
-import org.apache.velocity.Template;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.Velocity;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-/**
- * Note that this test must clean up after itself or it may cause other tests to fail.
- */
-public class VelocityTest {
-
-private static LoggerContext context;
-
- @BeforeClass
- public static void setupClass() {
- context = LoggerContext.getContext(false);
- }
-
- @AfterClass
- public static void tearDownClass() {
- Configurator.shutdown(context);
- StatusLogger.getLogger().reset();
- }
-
- @Test
- public void testVelocity() {
- Velocity.init();
- final VelocityContext vContext = new VelocityContext();
- vContext.put("name", new String("Velocity"));
-
- final Template template = Velocity.getTemplate("target/test-classes/hello.vm");
-
- final StringWriter sw = new StringWriter();
-
- template.merge(vContext, sw);
- }
-}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/bridge/LogEventWrapperTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/bridge/LogEventWrapperTest.java
new file mode 100644
index 00000000000..ff5faff5f1d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/bridge/LogEventWrapperTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.log4j.bridge;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.junit.jupiter.api.Test;
+
+class LogEventWrapperTest {
+
+ @Test
+ void testThread() {
+ final Thread currentThread = Thread.currentThread();
+ final String threadName = currentThread.getName();
+ final LoggingEvent log4j1Event = new LoggingEvent() {
+
+ @Override
+ public String getThreadName() {
+ return threadName;
+ }
+ };
+ final LogEvent log4j2Event = new LogEventWrapper(log4j1Event);
+ assertEquals(currentThread.getId(), log4j2Event.getThreadId());
+ assertEquals(currentThread.getPriority(), log4j2Event.getThreadPriority());
+ }
+
+ @Test
+ void testToImmutable() {
+ final LogEventWrapper wrapper = new LogEventWrapper(new LoggingEvent());
+ assertSame(wrapper, wrapper.toImmutable());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/BuilderManagerTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/BuilderManagerTest.java
new file mode 100644
index 00000000000..8d6b5a23f9d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/BuilderManagerTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.log4j.builders;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Properties;
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.varia.StringMatchFilter;
+import org.junit.jupiter.api.Test;
+
+class BuilderManagerTest {
+
+ /**
+ * This test ensures that instantiation failures due to missing parameters
+ * always return an empty wrapper instead of null, hence disabling the
+ * "instantiate by classname" fallback mechanism for supported components.
+ */
+ @Test
+ void testReturnInvalidValueOnError() {
+ final PropertiesConfiguration config = new PropertiesConfiguration(null, null);
+ final BuilderManager manager = new BuilderManager();
+ final Properties props = new Properties();
+ props.setProperty("FILE", FileAppender.class.getName());
+ props.setProperty("FILE.filter.1", StringMatchFilter.class.getName());
+ // Parse an invalid StringMatchFilter
+ final Filter filter = manager.parse(
+ StringMatchFilter.class.getName(), "FILE.filter", props, config, BuilderManager.INVALID_FILTER);
+ assertEquals(BuilderManager.INVALID_FILTER, filter);
+ // Parse an invalid FileAppender
+ final Appender appender = manager.parseAppender(
+ "FILE", FileAppender.class.getName(), "FILE", "FILE.layout", "FILE.filter.", props, config);
+ assertEquals(BuilderManager.INVALID_APPENDER, appender);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java
new file mode 100644
index 00000000000..dbed77a6ab5
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/Log4j2ListAppenderBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * 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.log4j.builders;
+
+import static org.apache.log4j.builders.BuilderManager.CATEGORY;
+import static org.apache.log4j.xml.XmlConfiguration.FILTER_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.LAYOUT_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.log4j.builders.appender.AppenderBuilder;
+import org.apache.log4j.config.PropertiesConfiguration;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.test.appender.ListAppender;
+import org.w3c.dom.Element;
+
+/**
+ * Builder for the native Log4j 2.x list appender to be used in the tests.
+ */
+@Plugin(name = "org.apache.logging.log4j.test.appender.ListAppender", category = CATEGORY)
+public class Log4j2ListAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ public Log4j2ListAppenderBuilder() {}
+
+ public Log4j2ListAppenderBuilder(final String prefix, final Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element element, final XmlConfiguration configuration) {
+ final String name = getNameAttribute(element);
+ final AtomicReference layout = new AtomicReference<>();
+ final AtomicReference filter = new AtomicReference<>();
+ forEachElement(element.getChildNodes(), currentElement -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(configuration.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ configuration.addFilter(filter, currentElement);
+ break;
+ default:
+ }
+ });
+ return createAppender(name, layout.get(), filter.get());
+ }
+
+ @Override
+ public Appender parseAppender(
+ final String name,
+ final String appenderPrefix,
+ final String layoutPrefix,
+ final String filterPrefix,
+ final Properties props,
+ final PropertiesConfiguration configuration) {
+ final Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ final Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ return createAppender(name, layout, filter);
+ }
+
+ private Appender createAppender(final String name, final Layout layout, final Filter filter) {
+ final org.apache.logging.log4j.core.Layout> log4j2Layout = LayoutAdapter.adapt(layout);
+ return AppenderWrapper.adapt(ListAppender.newBuilder()
+ .setName(name)
+ .setLayout(log4j2Layout)
+ .setFilter(AbstractBuilder.buildFilters(null, filter))
+ .build());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilderTest.java
new file mode 100644
index 00000000000..4a6290da846
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/filter/LevelRangeFilterBuilderTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.log4j.builders.filter;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import java.io.StringReader;
+import java.util.Properties;
+import java.util.stream.Stream;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.log4j.spi.Filter;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Filter.Result;
+import org.apache.logging.log4j.core.filter.LevelRangeFilter;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+class LevelRangeFilterBuilderTest {
+
+ @ParameterizedTest
+ @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+ void testAcceptOnMatchTrue(final TestLevelRangeFilterBuilder builder) throws Exception {
+ final LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, true);
+
+ assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+ assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+ assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+ assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+ void testAcceptOnMatchFalse(final TestLevelRangeFilterBuilder builder) throws Exception {
+ final LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, false);
+
+ assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+ assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+ assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
+ assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
+ assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
+ assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+ assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+ void testAcceptOnMatchNull(final TestLevelRangeFilterBuilder builder) throws Exception {
+ final LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.ERROR, null);
+
+ assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+ assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+ assertResult(Result.NEUTRAL, levelRangeFilter, Level.INFO);
+ assertResult(Result.NEUTRAL, levelRangeFilter, Level.WARN);
+ assertResult(Result.NEUTRAL, levelRangeFilter, Level.ERROR);
+ assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+ assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+ void testMinLevelNull(final TestLevelRangeFilterBuilder builder) throws Exception {
+ final LevelRangeFilter levelRangeFilter = builder.build(null, Level.ERROR, true);
+
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+ assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+ assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+ void testMaxLevelNull(final TestLevelRangeFilterBuilder builder) throws Exception {
+ final LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, null, true);
+
+ assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+ assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+ void testMinMaxLevelSame(final TestLevelRangeFilterBuilder builder) throws Exception {
+ final LevelRangeFilter levelRangeFilter = builder.build(Level.INFO, Level.INFO, true);
+
+ assertResult(Result.DENY, levelRangeFilter, Level.ALL);
+ assertResult(Result.DENY, levelRangeFilter, Level.DEBUG);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+ assertResult(Result.DENY, levelRangeFilter, Level.WARN);
+ assertResult(Result.DENY, levelRangeFilter, Level.ERROR);
+ assertResult(Result.DENY, levelRangeFilter, Level.FATAL);
+ assertResult(Result.DENY, levelRangeFilter, Level.OFF);
+ }
+
+ @ParameterizedTest
+ @ArgumentsSource(TestLevelRangeFilterBuilderProvider.class)
+ void testMinMaxLevelNull(final TestLevelRangeFilterBuilder builder) throws Exception {
+ final LevelRangeFilter levelRangeFilter = builder.build(null, null, true);
+
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.ALL);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.DEBUG);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.INFO);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.WARN);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.ERROR);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.FATAL);
+ assertResult(Result.ACCEPT, levelRangeFilter, Level.OFF);
+ }
+
+ private static void assertResult(final Result expected, final LevelRangeFilter filter, final Level level) {
+ assertSame(expected, filter.filter(null, level, null, (Object) null, null));
+ }
+
+ private static class TestLevelRangeFilterBuilderProvider implements ArgumentsProvider {
+
+ @Override
+ public Stream extends Arguments> provideArguments(final ExtensionContext extensionContext) {
+ return Stream.of(
+ Arguments.of(new TestLevelRangeFilterFromXmlBuilder()),
+ Arguments.of(new TestLevelRangeFilterFromPropertyBuilder()));
+ }
+ }
+
+ private interface TestLevelRangeFilterBuilder {
+
+ LevelRangeFilter build(Level levelMin, Level levelMax, Boolean acceptOnMatch) throws Exception;
+ }
+
+ private static class TestLevelRangeFilterFromXmlBuilder implements TestLevelRangeFilterBuilder {
+
+ @Override
+ public LevelRangeFilter build(final Level levelMin, final Level levelMax, final Boolean acceptOnMatch)
+ throws Exception {
+ final LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder();
+ final Filter filter = builder.parse(generateTestXml(levelMin, levelMax, acceptOnMatch), null);
+ final org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
+ return (LevelRangeFilter) wrappedFilter;
+ }
+
+ private static Element generateTestXml(final Level levelMin, final Level levelMax, final Boolean acceptOnMatch)
+ throws Exception {
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append("\n");
+ if (levelMin != null) {
+ sb.append(String.format(" \n", levelMin));
+ }
+ if (levelMax != null) {
+ sb.append(String.format(" \n", levelMax));
+ }
+ if (acceptOnMatch != null) {
+ sb.append(String.format(" \n", acceptOnMatch));
+ }
+ sb.append(" ");
+
+ return DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder()
+ .parse(new InputSource(new StringReader(sb.toString())))
+ .getDocumentElement();
+ }
+ }
+
+ private static class TestLevelRangeFilterFromPropertyBuilder implements TestLevelRangeFilterBuilder {
+
+ @Override
+ public LevelRangeFilter build(final Level levelMin, final Level levelMax, final Boolean acceptOnMatch) {
+ final Properties properties = new Properties();
+ if (levelMin != null) {
+ properties.setProperty("foobar.levelMin", levelMin.name());
+ }
+ if (levelMax != null) {
+ properties.setProperty("foobar.levelMax", levelMax.name());
+ }
+ if (acceptOnMatch != null) {
+ properties.setProperty("foobar.acceptOnMatch", acceptOnMatch.toString());
+ }
+ final LevelRangeFilterBuilder builder = new LevelRangeFilterBuilder("foobar", properties);
+ final Filter filter = builder.parse(null);
+ final org.apache.logging.log4j.core.Filter wrappedFilter = ((FilterWrapper) filter).getFilter();
+ return (LevelRangeFilter) wrappedFilter;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java
new file mode 100644
index 00000000000..c64dfc3df37
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/builders/layout/PatternLayoutBuilderTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.log4j.builders.layout;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.stream.Stream;
+import org.apache.log4j.bridge.LayoutAdapter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class PatternLayoutBuilderTest {
+
+ static Stream patterns() {
+ return Stream.of(
+ Arguments.of("%p", "%v1Level"),
+ Arguments.of("%100p", "%100v1Level"),
+ Arguments.of("%-100p", "%-100v1Level"),
+ Arguments.of("%x", "%ndc"),
+ Arguments.of("%X", "%properties"),
+ Arguments.of("%.20x", "%.20ndc"),
+ Arguments.of("%pid", "%pid"),
+ Arguments.of("%xEx", "%xEx"),
+ Arguments.of("%XX", "%XX"),
+ Arguments.of("%p id", "%v1Level id"),
+ Arguments.of("%x Ex", "%ndc Ex"),
+ Arguments.of("%X X", "%properties X"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("patterns")
+ void testLevelPatternReplacement(final String v1Pattern, final String v2Pattern) {
+ final PatternLayoutBuilder builder = new PatternLayoutBuilder();
+ final PatternLayout layout = (PatternLayout) LayoutAdapter.adapt(builder.createLayout(v1Pattern, null));
+ assertEquals(v2Pattern, layout.getConversionPattern());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
index 9c973ee2b54..f27722278dc 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationConverterTest.java
@@ -1,44 +1,44 @@
-package org.apache.log4j.config;
-
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
+package org.apache.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.junit.jupiter.api.Test;
+import org.xml.sax.SAXException;
-@RunWith(Parameterized.class)
public abstract class AbstractLog4j1ConfigurationConverterTest {
protected static List getPaths(final String root) throws IOException {
final List paths = new ArrayList<>();
Files.walkFileTree(Paths.get(root), new SimpleFileVisitor() {
@Override
- public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) {
paths.add(file.toAbsolutePath());
return FileVisitResult.CONTINUE;
}
@@ -46,23 +46,34 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr
return paths;
}
- private final Path pathIn;
-
- public AbstractLog4j1ConfigurationConverterTest(final Path path) {
- super();
- this.pathIn = path;
- }
+ public AbstractLog4j1ConfigurationConverterTest() {}
@Test
- public void test() throws IOException {
+ public void test(Path path) throws Exception {
final Path tempFile = Files.createTempFile("log4j2", ".xml");
try {
- final Log4j1ConfigurationConverter.CommandLineArguments cla = new Log4j1ConfigurationConverter.CommandLineArguments();
- cla.setPathIn(pathIn);
+ final Log4j1ConfigurationConverter.CommandLineArguments cla =
+ new Log4j1ConfigurationConverter.CommandLineArguments();
+ cla.setPathIn(path);
cla.setPathOut(tempFile);
Log4j1ConfigurationConverter.run(cla);
+ checkWellFormedXml(tempFile);
+ checkUnnecessaryEscaping(tempFile);
} finally {
Files.deleteIfExists(tempFile);
}
}
+
+ private void checkUnnecessaryEscaping(final Path tempFile) throws IOException {
+ for (String line : Files.readAllLines(tempFile)) {
+ assertFalse(line.endsWith("
"));
+ }
+ }
+
+ private void checkWellFormedXml(final Path xmlFilePath)
+ throws SAXException, IOException, ParserConfigurationException {
+ DocumentBuilderFactory.newInstance()
+ .newDocumentBuilder()
+ .parse(xmlFilePath.toUri().toString());
+ }
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
new file mode 100644
index 00000000000..b9967d0de41
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AbstractLog4j1ConfigurationTest.java
@@ -0,0 +1,662 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter.Adapter;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.bridge.FilterWrapper;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.ConsoleAppender.Target;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.NullAppender;
+import org.apache.logging.log4j.core.appender.OutputStreamManager;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.Filterable;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.jupiter.api.io.TempDir;
+
+abstract class AbstractLog4j1ConfigurationTest {
+
+ @TempDir
+ File tempDir;
+
+ abstract Configuration getConfiguration(String configResourcePrefix) throws URISyntaxException, IOException;
+
+ protected InputStream getResourceAsStream(final String configResource) {
+ final InputStream is = getClass().getClassLoader().getResourceAsStream(configResource);
+ assertNotNull(is);
+ return is;
+ }
+
+ protected LoggerContext configure(final String configResourcePrefix) throws URISyntaxException, IOException {
+ Configurator.reconfigure(getConfiguration(configResourcePrefix));
+ return (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ }
+
+ void testConsoleCapitalization() throws Exception {
+ final Configuration config = getConfiguration("config-1.2/log4j-capitalization");
+ final Appender capitalized = config.getAppender("ConsoleCapitalized");
+ assertNotNull(capitalized);
+ assertEquals(ConsoleAppender.class, capitalized.getClass());
+ final Appender javaStyle = config.getAppender("ConsoleJavaStyle");
+ assertNotNull(javaStyle);
+ assertEquals(ConsoleAppender.class, javaStyle.getClass());
+ testConsoleAppender((ConsoleAppender) capitalized, (ConsoleAppender) javaStyle);
+ }
+
+ private void testConsoleAppender(final ConsoleAppender expected, final ConsoleAppender actual) {
+ assertEquals(expected.getImmediateFlush(), actual.getImmediateFlush(), "immediateFlush");
+ assertEquals(expected.getTarget(), actual.getTarget(), "target");
+ assertEquals(expected.getLayout().getClass(), actual.getLayout().getClass(), "layoutClass");
+ if (expected.getLayout() instanceof PatternLayout) {
+ patternLayoutEquals((PatternLayout) expected.getLayout(), (PatternLayout) actual.getLayout());
+ }
+ }
+
+ private void patternLayoutEquals(final PatternLayout expected, final PatternLayout actual) {
+ assertEquals(expected.getCharset(), actual.getCharset());
+ assertEquals(expected.getConversionPattern(), actual.getConversionPattern());
+ }
+
+ private Layout> testConsole(final String configResource) throws Exception {
+ final Configuration configuration = getConfiguration(configResource);
+ final String name = "Console";
+ final ConsoleAppender appender = configuration.getAppender(name);
+ assertNotNull(
+ appender, "Missing appender '" + name + "' in configuration " + configResource + " → " + configuration);
+ assertTrue(getFollowProperty(appender), "follow");
+ assertEquals(Target.SYSTEM_ERR, appender.getTarget());
+ //
+ final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
+ assertNotNull(loggerConfig);
+ assertEquals(Level.DEBUG, loggerConfig.getLevel());
+ // immediateFlush is always true in Log4j 2.x
+ configuration.start();
+ configuration.stop();
+ return appender.getLayout();
+ }
+
+ void testConsoleTtccLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-TTCCLayout");
+ assertEquals("%d{ISO8601}{CET} %p - %m%n", layout.getConversionPattern());
+ }
+
+ void testRollingFileAppender() throws Exception {
+ testRollingFileAppender("config-1.2/log4j-RollingFileAppender");
+ }
+
+ void testDailyRollingFileAppender() throws Exception {
+ testDailyRollingFileAppender("config-1.2/log4j-DailyRollingFileAppender");
+ }
+
+ void testRollingFileAppenderWithProperties() throws Exception {
+ testRollingFileAppender("config-1.2/log4j-RollingFileAppender-with-props");
+ }
+
+ void testSystemProperties1() throws Exception {
+ final String tempFileName = System.getProperty("java.io.tmpdir") + "/hadoop.log";
+ final Path tempFilePath = new File(tempFileName).toPath();
+ Files.deleteIfExists(tempFilePath);
+ final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-1");
+ try {
+ final RollingFileAppender appender = configuration.getAppender("RFA");
+ assertFalse(getAppendProperty(appender), "append");
+ assertEquals(1000, appender.getManager().getBufferSize(), "bufferSize");
+ assertFalse(appender.getImmediateFlush(), "immediateFlush");
+ final DefaultRolloverStrategy rolloverStrategy =
+ (DefaultRolloverStrategy) appender.getManager().getRolloverStrategy();
+ assertEquals(16, rolloverStrategy.getMaxIndex());
+ final CompositeTriggeringPolicy ctp = appender.getTriggeringPolicy();
+ final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+ assertEquals(1, triggeringPolicies.length);
+ final TriggeringPolicy tp = triggeringPolicies[0];
+ assertInstanceOf(SizeBasedTriggeringPolicy.class, tp, tp.getClass().getName());
+ final SizeBasedTriggeringPolicy sbtp = (SizeBasedTriggeringPolicy) tp;
+ assertEquals(20 * 1024 * 1024, sbtp.getMaxFileSize());
+ appender.stop(10, TimeUnit.SECONDS);
+ assertEquals(tempFileName, appender.getFileName());
+ } finally {
+ configuration.start();
+ configuration.stop();
+ Files.deleteIfExists(tempFilePath);
+ }
+ }
+
+ void testSystemProperties2() throws Exception {
+ final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-2");
+ final RollingFileAppender appender = configuration.getAppender("RFA");
+ final String tmpDir = System.getProperty("java.io.tmpdir");
+ assertEquals(tmpDir + "/hadoop.log", appender.getFileName());
+ appender.stop(10, TimeUnit.SECONDS);
+ // Try to clean up
+ Path path = new File(appender.getFileName()).toPath();
+ Files.deleteIfExists(path);
+ path = new File("${java.io.tmpdir}").toPath();
+ Files.deleteIfExists(path);
+ }
+
+ private void testRollingFileAppender(final String configResource) throws Exception {
+ final Configuration configuration = getConfiguration(configResource);
+ final Appender appender = configuration.getAppender("RFA");
+ assertNotNull(appender);
+ assertEquals("RFA", appender.getName());
+ assertInstanceOf(
+ RollingFileAppender.class, appender, appender.getClass().getName());
+ final RollingFileAppender rfa = (RollingFileAppender) appender;
+
+ assertInstanceOf(
+ DefaultRolloverStrategy.class, rfa.getManager().getRolloverStrategy(), "defaultRolloverStrategy");
+ assertFalse(((DefaultRolloverStrategy) rfa.getManager().getRolloverStrategy()).isUseMax(), "rolloverStrategy");
+ assertFalse(getAppendProperty(rfa), "append");
+ assertEquals(1000, rfa.getManager().getBufferSize(), "bufferSize");
+ assertFalse(rfa.getImmediateFlush(), "immediateFlush");
+ assertEquals("target/hadoop.log", rfa.getFileName());
+ assertEquals("target/hadoop.log.%i", rfa.getFilePattern());
+ final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
+ assertNotNull(triggeringPolicy);
+ assertInstanceOf(
+ CompositeTriggeringPolicy.class,
+ triggeringPolicy,
+ triggeringPolicy.getClass().getName());
+ final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
+ final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+ assertEquals(1, triggeringPolicies.length);
+ final TriggeringPolicy tp = triggeringPolicies[0];
+ assertInstanceOf(SizeBasedTriggeringPolicy.class, tp, tp.getClass().getName());
+ final SizeBasedTriggeringPolicy sbtp = (SizeBasedTriggeringPolicy) tp;
+ assertEquals(256 * 1024 * 1024, sbtp.getMaxFileSize());
+ final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
+ assertInstanceOf(
+ DefaultRolloverStrategy.class,
+ rolloverStrategy,
+ rolloverStrategy.getClass().getName());
+ final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
+ assertEquals(20, drs.getMaxIndex());
+ configuration.start();
+ configuration.stop();
+ }
+
+ private void testDailyRollingFileAppender(final String configResource) throws Exception {
+ final Configuration configuration = getConfiguration(configResource);
+ try {
+ final Appender appender = configuration.getAppender("DRFA");
+ assertNotNull(appender);
+ assertEquals("DRFA", appender.getName());
+ assertInstanceOf(
+ RollingFileAppender.class, appender, appender.getClass().getName());
+ final RollingFileAppender rfa = (RollingFileAppender) appender;
+ assertFalse(getAppendProperty(rfa), "append");
+ assertEquals(1000, rfa.getManager().getBufferSize(), "bufferSize");
+ assertFalse(rfa.getImmediateFlush(), "immediateFlush");
+ assertEquals("target/hadoop.log", rfa.getFileName());
+ assertEquals("target/hadoop.log%d{.dd-MM-yyyy}", rfa.getFilePattern());
+ final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
+ assertNotNull(triggeringPolicy);
+ assertInstanceOf(
+ CompositeTriggeringPolicy.class,
+ triggeringPolicy,
+ triggeringPolicy.getClass().getName());
+ final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
+ final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
+ assertEquals(1, triggeringPolicies.length);
+ final TriggeringPolicy tp = triggeringPolicies[0];
+ assertInstanceOf(TimeBasedTriggeringPolicy.class, tp, tp.getClass().getName());
+ final TimeBasedTriggeringPolicy tbtp = (TimeBasedTriggeringPolicy) tp;
+ assertEquals(1, tbtp.getInterval());
+ final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
+ assertInstanceOf(
+ DefaultRolloverStrategy.class,
+ rolloverStrategy,
+ rolloverStrategy.getClass().getName());
+ final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
+ assertEquals(Integer.MAX_VALUE, drs.getMaxIndex());
+ } finally {
+ configuration.start();
+ configuration.stop();
+ }
+ }
+
+ private Layout> testFile() throws Exception {
+ final Configuration configuration = getConfiguration("config-1.2/log4j-file-SimpleLayout");
+ final FileAppender appender = configuration.getAppender("File");
+ assertNotNull(appender);
+ assertEquals("target/mylog.txt", appender.getFileName());
+ //
+ final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
+ assertNotNull(loggerConfig);
+ assertEquals(Level.DEBUG, loggerConfig.getLevel());
+ assertFalse(getAppendProperty(appender), "append");
+ assertEquals(1000, appender.getManager().getBufferSize(), "bufferSize");
+ assertFalse(appender.getImmediateFlush(), "immediateFlush");
+ configuration.start();
+ configuration.stop();
+ return appender.getLayout();
+ }
+
+ void testConsoleEnhancedPatternLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-EnhancedPatternLayout");
+ // %p, %X and %x converted to their Log4j 1.x bridge equivalent
+ assertEquals("%d{ISO8601} [%t][%c] %-5v1Level %properties %ndc: %m%n", layout.getConversionPattern());
+ }
+
+ void testConsoleHtmlLayout() throws Exception {
+ final HtmlLayout layout = (HtmlLayout) testConsole("config-1.2/log4j-console-HtmlLayout");
+ assertEquals("Headline", layout.getTitle());
+ assertTrue(layout.isLocationInfo());
+ }
+
+ void testConsolePatternLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-PatternLayout");
+ // %p converted to its Log4j 1.x bridge equivalent
+ assertEquals("%d{ISO8601} [%t][%c] %-5v1Level: %m%n", layout.getConversionPattern());
+ }
+
+ void testConsoleSimpleLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-SimpleLayout");
+ assertEquals("%v1Level - %m%n", layout.getConversionPattern());
+ }
+
+ void testFileSimpleLayout() throws Exception {
+ final PatternLayout layout = (PatternLayout) testFile();
+ assertEquals("%v1Level - %m%n", layout.getConversionPattern());
+ }
+
+ void testNullAppender() throws Exception {
+ final Configuration configuration = getConfiguration("config-1.2/log4j-NullAppender");
+ final Appender appender = configuration.getAppender("NullAppender");
+ assertNotNull(appender);
+ assertEquals("NullAppender", appender.getName());
+ assertInstanceOf(NullAppender.class, appender, appender.getClass().getName());
+ }
+
+ private boolean getFollowProperty(final ConsoleAppender consoleAppender) throws Exception {
+ OutputStream outputStream = getOutputStream(consoleAppender.getManager());
+ String className = outputStream.getClass().getName();
+ return className.endsWith("ConsoleAppender$SystemErrStream")
+ || className.endsWith("ConsoleAppender$SystemOutStream");
+ }
+
+ private boolean getAppendProperty(final RollingFileAppender appender) throws Exception {
+ return getAppendProperty((FileOutputStream) getOutputStream(appender.getManager()));
+ }
+
+ private boolean getAppendProperty(final FileAppender appender) throws Exception {
+ return getAppendProperty((FileOutputStream) getOutputStream(appender.getManager()));
+ }
+
+ private boolean getAppendProperty(final FileOutputStream os) throws Exception {
+ // Java 8
+ try {
+ final Field appendField = FileOutputStream.class.getDeclaredField("append");
+ appendField.setAccessible(true);
+ return (Boolean) appendField.get(os);
+ } catch (NoSuchFieldError | NoSuchFieldException e) {
+ // Java 11
+ final Field appendField = FileDescriptor.class.getDeclaredField("append");
+ appendField.setAccessible(true);
+ return (Boolean) appendField.get(os.getFD());
+ }
+ }
+
+ private OutputStream getOutputStream(final OutputStreamManager manager) throws Exception {
+ final Method getOutputStream = OutputStreamManager.class.getDeclaredMethod("getOutputStream");
+ getOutputStream.setAccessible(true);
+ return (OutputStream) getOutputStream.invoke(manager);
+ }
+
+ private Layout> testLayout(final Configuration config, final String appenderName) {
+ final ConsoleAppender appender = config.getAppender(appenderName);
+ assertNotNull(
+ appender,
+ "Missing appender '" + appenderName + "' in configuration " + config.getConfigurationSource());
+ return appender.getLayout();
+ }
+
+ /**
+ * Test if the default values from Log4j 1.x are respected.
+ */
+ void testDefaultValues() throws Exception {
+ final Configuration config = getConfiguration("config-1.2/log4j-defaultValues");
+ // HtmlLayout
+ final HtmlLayout htmlLayout = (HtmlLayout) testLayout(config, "HTMLLayout");
+ assertNotNull(htmlLayout);
+ assertEquals("Log4J Log Messages", htmlLayout.getTitle(), "title");
+ assertFalse(htmlLayout.isLocationInfo(), "locationInfo");
+ // PatternLayout
+ final PatternLayout patternLayout = (PatternLayout) testLayout(config, "PatternLayout");
+ assertNotNull(patternLayout);
+ assertEquals("%m%n", patternLayout.getConversionPattern(), "conversionPattern");
+ // TTCCLayout
+ final PatternLayout ttccLayout = (PatternLayout) testLayout(config, "TTCCLayout");
+ assertNotNull(ttccLayout);
+ assertEquals(
+ "%r [%t] %p %c %notEmpty{%ndc }- %m%n",
+ ttccLayout.getConversionPattern(), "equivalent conversion pattern");
+ // TODO: XMLLayout
+ // final XmlLayout xmlLayout = (XmlLayout) testLayout(config, "XMLLayout");
+ // assertNotNull(xmlLayout);
+ // ConsoleAppender
+ final ConsoleAppender consoleAppender = config.getAppender("ConsoleAppender");
+ assertNotNull(consoleAppender);
+ assertEquals(Target.SYSTEM_OUT, consoleAppender.getTarget(), "target");
+ final boolean follow = getFollowProperty(consoleAppender);
+ assertFalse(follow, "follow");
+ // DailyRollingFileAppender
+ final RollingFileAppender dailyRollingFileAppender = config.getAppender("DailyRollingFileAppender");
+ assertNotNull(dailyRollingFileAppender);
+ assertEquals(
+ "target/dailyRollingFileAppender%d{.yyyy-MM-dd}",
+ dailyRollingFileAppender.getFilePattern(), "equivalent file pattern");
+ assertTrue(getAppendProperty(dailyRollingFileAppender), "append");
+ assertEquals(8192, dailyRollingFileAppender.getManager().getBufferSize(), "bufferSize");
+ assertTrue(dailyRollingFileAppender.getImmediateFlush(), "immediateFlush");
+ // FileAppender
+ final FileAppender fileAppender = config.getAppender("FileAppender");
+ assertNotNull(fileAppender);
+ assertTrue(getAppendProperty(fileAppender), "append");
+ assertEquals(8192, fileAppender.getManager().getBufferSize(), "bufferSize");
+ assertTrue(fileAppender.getImmediateFlush(), "immediateFlush");
+ // RollingFileAppender
+ final RollingFileAppender rollingFileAppender = config.getAppender("RollingFileAppender");
+ assertNotNull(rollingFileAppender);
+ assertEquals("target/rollingFileAppender.%i", rollingFileAppender.getFilePattern(), "equivalent file pattern");
+ final CompositeTriggeringPolicy compositePolicy =
+ rollingFileAppender.getManager().getTriggeringPolicy();
+ assertEquals(1, compositePolicy.getTriggeringPolicies().length);
+ final SizeBasedTriggeringPolicy sizePolicy =
+ (SizeBasedTriggeringPolicy) compositePolicy.getTriggeringPolicies()[0];
+ assertEquals(10 * 1024 * 1024L, sizePolicy.getMaxFileSize(), "maxFileSize");
+ final DefaultRolloverStrategy strategy =
+ (DefaultRolloverStrategy) rollingFileAppender.getManager().getRolloverStrategy();
+ assertEquals(1, strategy.getMaxIndex(), "maxBackupIndex");
+ assertTrue(getAppendProperty(rollingFileAppender), "append");
+ assertEquals(8192, rollingFileAppender.getManager().getBufferSize(), "bufferSize");
+ assertTrue(rollingFileAppender.getImmediateFlush(), "immediateFlush");
+ config.start();
+ config.stop();
+ }
+
+ /**
+ * Checks a hierarchy of filters.
+ *
+ * @param filter A filter
+ * @return the number of filters
+ */
+ private int checkFilters(final org.apache.logging.log4j.core.Filter filter) {
+ int count = 0;
+ if (filter instanceof CompositeFilter) {
+ for (final org.apache.logging.log4j.core.Filter part : ((CompositeFilter) filter).getFiltersArray()) {
+ count += checkFilters(part);
+ }
+ } else if (filter instanceof FilterAdapter) {
+ // Don't create adapters from wrappers
+ assertFalse(
+ ((FilterAdapter) filter).getFilter() instanceof FilterWrapper,
+ "found FilterAdapter of a FilterWrapper");
+ count += checkFilters(((FilterAdapter) filter).getFilter());
+ } else {
+ count++;
+ }
+ return count;
+ }
+
+ /**
+ * Checks a hierarchy of filters.
+ *
+ * @param filter A filter
+ * @return the number of filters
+ */
+ private int checkFilters(final org.apache.log4j.spi.Filter filter) {
+ int count = 0;
+ if (filter instanceof FilterWrapper) {
+ // Don't create wrappers from adapters
+ assertFalse(
+ ((FilterWrapper) filter).getFilter() instanceof FilterAdapter,
+ "found FilterWrapper of a FilterAdapter");
+ count += checkFilters(((FilterWrapper) filter).getFilter());
+ } else {
+ count++;
+ }
+ // We prefer a:
+ // CompositeFilter of native Log4j 2.x filters
+ // over a:
+ // FilterAdapter of a chain of FilterWrappers.
+ assertNull(filter.getNext(), "found chain of Log4j 1.x filters");
+ return count;
+ }
+
+ void testMultipleFilters() throws Exception {
+ System.setProperty("test.tmpDir", tempDir.getCanonicalPath());
+
+ try (final LoggerContext loggerContext = configure("log4j-multipleFilters")) {
+ final Configuration configuration = loggerContext.getConfiguration();
+
+ assertNotNull(configuration);
+
+ // Check only number of filters.
+ final Filterable console = configuration.getAppender("CONSOLE");
+ assertNotNull(console);
+ assertEquals(4, checkFilters(console.getFilter()));
+ final Filterable file = configuration.getAppender("FILE");
+ assertNotNull(file);
+ assertEquals(4, checkFilters(file.getFilter()));
+ final Filterable rfa = configuration.getAppender("RFA");
+ assertNotNull(rfa);
+ assertEquals(4, checkFilters(rfa.getFilter()));
+ final Filterable drfa = configuration.getAppender("DRFA");
+ assertNotNull(drfa);
+ assertEquals(4, checkFilters(drfa.getFilter()));
+ // List appenders
+ final Appender appender = configuration.getAppender("LIST");
+ assertNotNull(appender);
+ assertEquals(3, checkFilters(((Filterable) appender).getFilter()));
+ final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
+ final org.apache.logging.log4j.core.test.appender.ListAppender nativeAppender =
+ configuration.getAppender("LIST2");
+ assertEquals(3, checkFilters(nativeAppender.getFilter()));
+
+ final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+ int expected = 0;
+ // message blocked by Threshold
+ logger.trace("NEUTRAL message");
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message blocked by DenyAll filter
+ logger.warn("NEUTRAL message");
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message accepted by level filter
+ logger.info("NEUTRAL message");
+ expected++;
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message accepted by "StartsWith" filter
+ logger.warn("ACCEPT message");
+ expected++;
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ // message blocked by "StartsWith" filter
+ logger.info("DENY message");
+ assertEquals(expected, legacyAppender.getEvents().size());
+ assertEquals(expected, nativeAppender.getEvents().size());
+ } finally {
+ System.clearProperty("test.tmpDir");
+ }
+ }
+
+ void testGlobalThreshold() throws Exception {
+ try (final LoggerContext ctx = configure("config-1.2/log4j-global-threshold")) {
+ final Configuration config = ctx.getConfiguration();
+ final Filter filter = config.getFilter();
+ assertInstanceOf(ThresholdFilter.class, filter);
+ final ThresholdFilter thresholdFilter = (ThresholdFilter) filter;
+ assertEquals(Level.INFO, thresholdFilter.getLevel());
+ assertEquals(Filter.Result.NEUTRAL, thresholdFilter.getOnMatch());
+ assertEquals(Filter.Result.DENY, thresholdFilter.getOnMismatch());
+
+ final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+ // List appender
+ final Appender appender = config.getAppender("LIST");
+ assertNotNull(appender);
+ final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
+ // Stopped by root logger level
+ logger.trace("TRACE");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // Stopped by global threshold
+ logger.debug("DEBUG");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // Accepted
+ logger.info("INFO");
+ assertEquals(1, legacyAppender.getEvents().size());
+ }
+ }
+
+ protected void testEnhancedRollingFileAppender(final Configuration configuration) {
+ Appender appender;
+ TriggeringPolicy policy;
+ RolloverStrategy strategy;
+ DefaultRolloverStrategy defaultRolloverStrategy;
+ // Time policy with default attributes
+ appender = configuration.getAppender("DEFAULT_TIME");
+ assertInstanceOf(RollingFileAppender.class, appender, "is RollingFileAppender");
+ final RollingFileAppender defaultTime = (RollingFileAppender) appender;
+ assertTrue(defaultTime.getManager().isAppend(), "append");
+ assertEquals(8192, defaultTime.getManager().getBufferSize(), "bufferSize");
+ assertTrue(defaultTime.getImmediateFlush(), "immediateFlush");
+ assertEquals("target/EnhancedRollingFileAppender/defaultTime.log", defaultTime.getFileName(), "fileName");
+ assertEquals(
+ "target/EnhancedRollingFileAppender/defaultTime.%d{yyyy-MM-dd}.log",
+ defaultTime.getFilePattern(), "filePattern");
+ policy = defaultTime.getTriggeringPolicy();
+ assertInstanceOf(TimeBasedTriggeringPolicy.class, policy, "is TimeBasedTriggeringPolicy");
+ // Size policy with default attributes
+ appender = configuration.getAppender("DEFAULT_SIZE");
+ assertInstanceOf(RollingFileAppender.class, appender, "is RollingFileAppender");
+ final RollingFileAppender defaultSize = (RollingFileAppender) appender;
+ assertTrue(defaultSize.getManager().isAppend(), "append");
+ assertEquals(8192, defaultSize.getManager().getBufferSize(), "bufferSize");
+ assertTrue(defaultSize.getImmediateFlush(), "immediateFlush");
+ assertEquals("target/EnhancedRollingFileAppender/defaultSize.log", defaultSize.getFileName(), "fileName");
+ assertEquals(
+ "target/EnhancedRollingFileAppender/defaultSize.%i.log", defaultSize.getFilePattern(), "filePattern");
+ policy = defaultSize.getTriggeringPolicy();
+ assertInstanceOf(SizeBasedTriggeringPolicy.class, policy, "is SizeBasedTriggeringPolicy");
+ assertEquals(10 * 1024 * 1024L, ((SizeBasedTriggeringPolicy) policy).getMaxFileSize());
+ strategy = defaultSize.getManager().getRolloverStrategy();
+ assertInstanceOf(DefaultRolloverStrategy.class, strategy, "is DefaultRolloverStrategy");
+ defaultRolloverStrategy = (DefaultRolloverStrategy) strategy;
+ assertEquals(1, defaultRolloverStrategy.getMinIndex());
+ assertEquals(7, defaultRolloverStrategy.getMaxIndex());
+ // Time policy with custom attributes
+ appender = configuration.getAppender("TIME");
+ assertInstanceOf(RollingFileAppender.class, appender, "is RollingFileAppender");
+ final RollingFileAppender time = (RollingFileAppender) appender;
+ assertFalse(time.getManager().isAppend(), "append");
+ assertEquals(1000, time.getManager().getBufferSize(), "bufferSize");
+ assertFalse(time.getImmediateFlush(), "immediateFlush");
+ assertEquals("target/EnhancedRollingFileAppender/time.log", time.getFileName(), "fileName");
+ assertEquals(
+ "target/EnhancedRollingFileAppender/time.%d{yyyy-MM-dd}.log", time.getFilePattern(), "filePattern");
+ policy = time.getTriggeringPolicy();
+ assertInstanceOf(TimeBasedTriggeringPolicy.class, policy, "is TimeBasedTriggeringPolicy");
+ // Size policy with custom attributes
+ appender = configuration.getAppender("SIZE");
+ assertInstanceOf(RollingFileAppender.class, appender, "is RollingFileAppender");
+ final RollingFileAppender size = (RollingFileAppender) appender;
+ assertFalse(size.getManager().isAppend(), "append");
+ assertEquals(1000, size.getManager().getBufferSize(), "bufferSize");
+ assertFalse(size.getImmediateFlush(), "immediateFlush");
+ assertEquals("target/EnhancedRollingFileAppender/size.log", size.getFileName(), "fileName");
+ assertEquals("target/EnhancedRollingFileAppender/size.%i.log", size.getFilePattern(), "filePattern");
+ policy = size.getTriggeringPolicy();
+ assertInstanceOf(SizeBasedTriggeringPolicy.class, policy, "is SizeBasedTriggeringPolicy");
+ assertEquals(10_000_000L, ((SizeBasedTriggeringPolicy) policy).getMaxFileSize());
+ strategy = size.getManager().getRolloverStrategy();
+ assertInstanceOf(DefaultRolloverStrategy.class, strategy, "is DefaultRolloverStrategy");
+ defaultRolloverStrategy = (DefaultRolloverStrategy) strategy;
+ assertEquals(11, defaultRolloverStrategy.getMinIndex());
+ assertEquals(20, defaultRolloverStrategy.getMaxIndex());
+ }
+
+ protected void testLevelRangeFilter() throws Exception {
+ try (final LoggerContext ctx = configure("config-1.2/log4j-LevelRangeFilter")) {
+ final Configuration config = ctx.getConfiguration();
+ final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+ // List appender
+ final Appender appender = config.getAppender("LIST");
+ assertNotNull(appender);
+ final ListAppender legacyAppender = (ListAppender) ((Adapter) appender).getAppender();
+ // deny
+ logger.trace("TRACE");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // deny
+ logger.debug("DEBUG");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // accept
+ logger.info("INFO");
+ assertEquals(1, legacyAppender.getEvents().size());
+ // accept
+ logger.warn("WARN");
+ assertEquals(2, legacyAppender.getEvents().size());
+ // accept
+ logger.error("ERROR");
+ assertEquals(3, legacyAppender.getEvents().size());
+ // deny
+ logger.fatal("FATAL");
+ assertEquals(3, legacyAppender.getEvents().size());
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java
new file mode 100644
index 00000000000..b74c3365ae7
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AsyncAppenderTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.log4j.config;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Test configuration from XML.
+ */
+@UsingStatusListener
+class AsyncAppenderTest {
+
+ private static long DEFAULT_TIMEOUT_MS = 500;
+
+ static Stream testAsyncAppender() {
+ return Stream.of("/log4j1-async.xml", "/log4j1-async.properties")
+ .map(config -> assertDoesNotThrow(() -> {
+ final URI uri = AsyncAppenderTest.class.getResource(config).toURI();
+ return Paths.get(uri).toString();
+ }));
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void testAsyncAppender(final String configLocation) throws Exception {
+ try (final LoggerContext loggerContext = TestConfigurator.configure(configLocation)) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ final AppenderAdapter.Adapter adapter =
+ loggerContext.getConfiguration().getAppender("list");
+ assertThat(adapter).isNotNull();
+ final ListAppender appender = (ListAppender) adapter.getAppender();
+ final List messages = appender.getMessages(1, DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ assertThat(messages).hasSize(1);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
new file mode 100644
index 00000000000..be551f3d8cd
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/AutoConfigTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test configuration from XML.
+ */
+class AutoConfigTest {
+
+ @BeforeAll
+ static void beforeAll() {
+ System.setProperty(ConfigurationFactory.LOG4J1_EXPERIMENTAL, "true");
+ }
+
+ @Test
+ void testListAppender() {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ final LoggerContext loggerContext = org.apache.logging.log4j.LogManager.getContext(false);
+ final Configuration configuration =
+ ((org.apache.logging.log4j.core.LoggerContext) loggerContext).getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ } else if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(eventAppender, "No Event Appender");
+ assertNotNull(messageAppender, "No Message Appender");
+ final List events = eventAppender.getEvents();
+ assertTrue(events != null && !events.isEmpty(), "No events");
+ final List messages = messageAppender.getMessages();
+ assertTrue(messages != null && !messages.isEmpty(), "No messages");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
index 152f5dd97e6..d8b76f9954d 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterHadoopTest.java
@@ -1,39 +1,40 @@
-package org.apache.log4j.config;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.List;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
+package org.apache.log4j.config;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
-@RunWith(Parameterized.class)
public class Log4j1ConfigurationConverterHadoopTest extends AbstractLog4j1ConfigurationConverterTest {
- @Parameterized.Parameters(name = "{0}")
public static List data() throws IOException {
return getPaths("src/test/resources/config-1.2/hadoop");
}
- public Log4j1ConfigurationConverterHadoopTest(final Path path) {
- super(path);
+ public Log4j1ConfigurationConverterHadoopTest() {
+ super();
}
+ @ParameterizedTest
+ @MethodSource("data")
+ public void test(Path path) throws Exception {
+ super.test(path);
+ }
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
index 2b39d4ff311..29da3c77246 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationConverterSparkTest.java
@@ -1,39 +1,40 @@
-package org.apache.log4j.config;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.List;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
+package org.apache.log4j.config;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
-@RunWith(Parameterized.class)
public class Log4j1ConfigurationConverterSparkTest extends AbstractLog4j1ConfigurationConverterTest {
- @Parameterized.Parameters(name = "{0}")
public static List data() throws IOException {
return getPaths("src/test/resources/config-1.2/spark");
}
- public Log4j1ConfigurationConverterSparkTest(final Path path) {
- super(path);
+ public Log4j1ConfigurationConverterSparkTest() {
+ super();
}
+ @ParameterizedTest
+ @MethodSource("data")
+ public void test(Path path) throws Exception {
+ super.test(path);
+ }
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
index 8d3e4e2b3e7..6636544668f 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/Log4j1ConfigurationFactoryTest.java
@@ -1,63 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.config;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
-import java.io.File;
+import java.io.Serializable;
import java.net.URISyntaxException;
import java.net.URL;
-import java.nio.file.FileSystemException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.concurrent.TimeUnit;
-
import org.apache.log4j.layout.Log4j1XmlLayout;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.ConsoleAppender.Target;
-import org.apache.logging.log4j.core.appender.FileAppender;
-import org.apache.logging.log4j.core.appender.NullAppender;
-import org.apache.logging.log4j.core.appender.RollingFileAppender;
-import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
-import org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy;
-import org.apache.logging.log4j.core.appender.rolling.RolloverStrategy;
-import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
-import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
-import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.apache.logging.log4j.core.layout.HtmlLayout;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
import org.apache.logging.log4j.core.layout.PatternLayout;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+
+class Log4j1ConfigurationFactoryTest extends AbstractLog4j1ConfigurationTest {
-public class Log4j1ConfigurationFactoryTest {
+ private static final String SUFFIX = ".properties";
+
+ @Override
+ protected Configuration getConfiguration(final String configResource) throws URISyntaxException {
+ final URL configLocation = ClassLoader.getSystemResource(configResource + SUFFIX);
+ assertNotNull(configLocation, configResource);
+ final Configuration configuration =
+ new Log4j1ConfigurationFactory().getConfiguration(null, "test", configLocation.toURI());
+ assertNotNull(configuration);
+ return configuration;
+ }
private Layout> testConsole(final String configResource) throws Exception {
final Configuration configuration = getConfiguration(configResource);
final String name = "Console";
final ConsoleAppender appender = configuration.getAppender(name);
- assertNotNull("Missing appender '" + name + "' in configuration " + configResource + " → " + configuration,
- appender);
+ assertNotNull(
+ appender, "Missing appender '" + name + "' in configuration " + configResource + " → " + configuration);
assertEquals(Target.SYSTEM_ERR, appender.getTarget());
//
final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
@@ -68,182 +70,124 @@ private Layout> testConsole(final String configResource) throws Exception {
return appender.getLayout();
}
- private Layout> testFile(final String configResource) throws Exception {
- final Configuration configuration = getConfiguration(configResource);
- final FileAppender appender = configuration.getAppender("File");
- assertNotNull(appender);
- assertEquals("target/mylog.txt", appender.getFileName());
- //
- final LoggerConfig loggerConfig = configuration.getLoggerConfig("com.example.foo");
- assertNotNull(loggerConfig);
- assertEquals(Level.DEBUG, loggerConfig.getLevel());
- configuration.start();
- configuration.stop();
- return appender.getLayout();
- }
-
- private Configuration getConfiguration(final String configResource) throws URISyntaxException {
- final URL configLocation = ClassLoader.getSystemResource(configResource);
- assertNotNull(configResource, configLocation);
- final Configuration configuration = new Log4j1ConfigurationFactory().getConfiguration(null, "test",
- configLocation.toURI());
- assertNotNull(configuration);
- return configuration;
- }
-
- @Test
- public void testConsoleEnhancedPatternLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole(
- "config-1.2/log4j-console-EnhancedPatternLayout.properties");
- assertEquals("%d{ISO8601} [%t][%c] %-5p %properties %ndc: %m%n", layout.getConversionPattern());
- }
-
- @Test
- public void testConsoleHtmlLayout() throws Exception {
- final HtmlLayout layout = (HtmlLayout) testConsole("config-1.2/log4j-console-HtmlLayout.properties");
- assertEquals("Headline", layout.getTitle());
- assertTrue(layout.isLocationInfo());
- }
-
- @Test
- public void testConsolePatternLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-PatternLayout.properties");
- assertEquals("%d{ISO8601} [%t][%c] %-5p: %m%n", layout.getConversionPattern());
- }
-
- @Test
- public void testConsoleSimpleLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-SimpleLayout.properties");
- assertEquals("%level - %m%n", layout.getConversionPattern());
- }
-
- @Test
- public void testConsoleTtccLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testConsole("config-1.2/log4j-console-TTCCLayout.properties");
- assertEquals("%r [%t] %p %notEmpty{%ndc }- %m%n", layout.getConversionPattern());
- }
-
- @Test
- public void testConsoleXmlLayout() throws Exception {
- final Log4j1XmlLayout layout = (Log4j1XmlLayout) testConsole("config-1.2/log4j-console-XmlLayout.properties");
- assertTrue(layout.isLocationInfo());
- assertFalse(layout.isProperties());
- }
-
- @Test
- public void testFileSimpleLayout() throws Exception {
- final PatternLayout layout = (PatternLayout) testFile("config-1.2/log4j-file-SimpleLayout.properties");
- assertEquals("%level - %m%n", layout.getConversionPattern());
- }
-
- @Test
- public void testNullAppender() throws Exception {
- final Configuration configuration = getConfiguration("config-1.2/log4j-NullAppender.properties");
- final Appender appender = configuration.getAppender("NullAppender");
- assertNotNull(appender);
- assertEquals("NullAppender", appender.getName());
- assertTrue(appender.getClass().getName(), appender instanceof NullAppender);
- }
-
- @Test
- public void testRollingFileAppender() throws Exception {
- testRollingFileAppender("config-1.2/log4j-RollingFileAppender.properties", "RFA", "target/hadoop.log.%i");
- }
-
- @Test
- public void testDailyRollingFileAppender() throws Exception {
- testDailyRollingFileAppender("config-1.2/log4j-DailyRollingFileAppender.properties", "DRFA", "target/hadoop.log%d{.yyyy-MM-dd}");
- }
-
- @Test
- public void testRollingFileAppenderWithProperties() throws Exception {
- testRollingFileAppender("config-1.2/log4j-RollingFileAppender-with-props.properties", "RFA", "target/hadoop.log.%i");
- }
-
- @Test
- public void testSystemProperties1() throws Exception {
- final String tempFileName = System.getProperty("java.io.tmpdir") + "/hadoop.log";
- final Path tempFilePath = new File(tempFileName).toPath();
- Files.deleteIfExists(tempFilePath);
+ @Override
+ @Test
+ void testConsoleEnhancedPatternLayout() throws Exception {
+ super.testConsoleEnhancedPatternLayout();
+ }
+
+ @Override
+ @Test
+ void testConsoleHtmlLayout() throws Exception {
+ super.testConsoleHtmlLayout();
+ }
+
+ @Test
+ void testConsolePatternLayout() throws Exception {
+ super.testConsolePatternLayout();
+ }
+
+ @Override
+ @Test
+ void testConsoleSimpleLayout() throws Exception {
+ super.testConsoleSimpleLayout();
+ }
+
+ @Override
+ @Test
+ void testConsoleTtccLayout() throws Exception {
+ super.testConsoleTtccLayout();
+ }
+
+ @Test
+ void testConsoleXmlLayout() throws Exception {
+ final Log4j1XmlLayout layout = (Log4j1XmlLayout) testConsole("config-1.2/log4j-console-XmlLayout");
+ assertTrue(layout.isLocationInfo());
+ assertFalse(layout.isProperties());
+ }
+
+ @Override
+ @Test
+ void testFileSimpleLayout() throws Exception {
+ super.testFileSimpleLayout();
+ }
+
+ @Override
+ @Test
+ void testNullAppender() throws Exception {
+ super.testNullAppender();
+ }
+
+ @Override
+ @Test
+ void testRollingFileAppender() throws Exception {
+ super.testRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ void testDailyRollingFileAppender() throws Exception {
+ super.testDailyRollingFileAppender();
+ }
+
+ @Test
+ void testRollingFileAppenderWithProperties() throws Exception {
+ super.testRollingFileAppenderWithProperties();
+ }
+
+ @Override
+ @Test
+ void testSystemProperties1() throws Exception {
+ super.testSystemProperties1();
+ }
+
+ @Override
+ @Test
+ void testSystemProperties2() throws Exception {
+ super.testSystemProperties2();
+ }
+
+ @Override
+ @Test
+ void testConsoleCapitalization() throws Exception {
+ super.testConsoleCapitalization();
+ }
+
+ @Override
+ @Test
+ void testDefaultValues() throws Exception {
+ super.testDefaultValues();
+ }
+
+ @Test
+ void testUntrimmedValues() throws Exception {
try {
- final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-1.properties");
- final RollingFileAppender appender = configuration.getAppender("RFA");
- appender.stop(10, TimeUnit.SECONDS);
- System.out.println("expected: " + tempFileName + " Actual: " + appender.getFileName());
- assertEquals(tempFileName, appender.getFileName());
- } finally {
- try {
- Files.deleteIfExists(tempFilePath);
- } catch (FileSystemException e) {
- e.printStackTrace();
- }
+ final Configuration config = getConfiguration("config-1.2/log4j-untrimmed");
+ final LoggerConfig rootLogger = config.getRootLogger();
+ assertEquals(Level.DEBUG, rootLogger.getLevel());
+ final Appender appender = config.getAppender("Console");
+ assertInstanceOf(ConsoleAppender.class, appender);
+ final Layout extends Serializable> layout = appender.getLayout();
+ assertInstanceOf(PatternLayout.class, layout);
+ assertEquals("%v1Level - %m%n", ((PatternLayout) layout).getConversionPattern());
+ // No filter support
+ config.start();
+ config.stop();
+ } catch (NoClassDefFoundError e) {
+ fail(e.getMessage());
}
- }
-
- @Test
- public void testSystemProperties2() throws Exception {
- final Configuration configuration = getConfiguration("config-1.2/log4j-system-properties-2.properties");
- final RollingFileAppender appender = configuration.getAppender("RFA");
- assertEquals("${java.io.tmpdir}/hadoop.log", appender.getFileName());
- appender.stop(10, TimeUnit.SECONDS);
- Path path = new File(appender.getFileName()).toPath();
- Files.deleteIfExists(path);
- path = new File("${java.io.tmpdir}").toPath();
- Files.deleteIfExists(path);
- }
-
- private void testRollingFileAppender(final String configResource, final String name, final String filePattern) throws URISyntaxException {
- final Configuration configuration = getConfiguration(configResource);
- final Appender appender = configuration.getAppender(name);
- assertNotNull(appender);
- assertEquals(name, appender.getName());
- assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
- final RollingFileAppender rfa = (RollingFileAppender) appender;
- assertEquals("target/hadoop.log", rfa.getFileName());
- assertEquals(filePattern, rfa.getFilePattern());
- final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
- assertNotNull(triggeringPolicy);
- assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
- final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
- final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
- assertEquals(1, triggeringPolicies.length);
- final TriggeringPolicy tp = triggeringPolicies[0];
- assertTrue(tp.getClass().getName(), tp instanceof SizeBasedTriggeringPolicy);
- final SizeBasedTriggeringPolicy sbtp = (SizeBasedTriggeringPolicy) tp;
- assertEquals(256 * 1024 * 1024, sbtp.getMaxFileSize());
- final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
- assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
- final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
- assertEquals(20, drs.getMaxIndex());
- configuration.start();
- configuration.stop();
- }
-
- private void testDailyRollingFileAppender(final String configResource, final String name, final String filePattern) throws URISyntaxException {
- final Configuration configuration = getConfiguration(configResource);
- final Appender appender = configuration.getAppender(name);
- assertNotNull(appender);
- assertEquals(name, appender.getName());
- assertTrue(appender.getClass().getName(), appender instanceof RollingFileAppender);
- final RollingFileAppender rfa = (RollingFileAppender) appender;
- assertEquals("target/hadoop.log", rfa.getFileName());
- assertEquals(filePattern, rfa.getFilePattern());
- final TriggeringPolicy triggeringPolicy = rfa.getTriggeringPolicy();
- assertNotNull(triggeringPolicy);
- assertTrue(triggeringPolicy.getClass().getName(), triggeringPolicy instanceof CompositeTriggeringPolicy);
- final CompositeTriggeringPolicy ctp = (CompositeTriggeringPolicy) triggeringPolicy;
- final TriggeringPolicy[] triggeringPolicies = ctp.getTriggeringPolicies();
- assertEquals(1, triggeringPolicies.length);
- final TriggeringPolicy tp = triggeringPolicies[0];
- assertTrue(tp.getClass().getName(), tp instanceof TimeBasedTriggeringPolicy);
- final TimeBasedTriggeringPolicy tbtp = (TimeBasedTriggeringPolicy) tp;
- assertEquals(1, tbtp.getInterval());
- final RolloverStrategy rolloverStrategy = rfa.getManager().getRolloverStrategy();
- assertTrue(rolloverStrategy.getClass().getName(), rolloverStrategy instanceof DefaultRolloverStrategy);
- final DefaultRolloverStrategy drs = (DefaultRolloverStrategy) rolloverStrategy;
- assertEquals(Integer.MAX_VALUE, drs.getMaxIndex());
- configuration.start();
- configuration.stop();
- }
+ }
+ @Test
+ void testGlobalThreshold() throws Exception {
+ try (final LoggerContext ctx = configure("config-1.2/log4j-global-threshold")) {
+ final Configuration config = ctx.getConfiguration();
+ final Filter filter = config.getFilter();
+ assertInstanceOf(ThresholdFilter.class, filter);
+ final ThresholdFilter thresholdFilter = (ThresholdFilter) filter;
+ assertEquals(Level.INFO, thresholdFilter.getLevel());
+ assertEquals(Filter.Result.NEUTRAL, thresholdFilter.getOnMatch());
+ assertEquals(Filter.Result.DENY, thresholdFilter.getOnMismatch());
+ }
+ }
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
new file mode 100644
index 00000000000..bcae05d6bee
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/MapRewriteAppenderTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test RewriteAppender
+ */
+class MapRewriteAppenderTest {
+
+ @BeforeAll
+ static void beforeAll() {
+ System.setProperty(
+ ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-mapRewrite.xml");
+ }
+
+ @AfterEach
+ void after() {
+ ThreadContext.clearMap();
+ }
+
+ @Test
+ void testRewrite() {
+ final Logger logger = LogManager.getLogger("test");
+ final Map map = new HashMap<>();
+ map.put("message", "This is a test");
+ map.put("hello", "world");
+ logger.debug(map);
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ final Configuration configuration = context.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ for (Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(eventAppender, "No Event Appender");
+ final List events = eventAppender.getEvents();
+ assertTrue(events != null && !events.isEmpty(), "No events");
+ assertNotNull(events.get(0).getProperties(), "No properties in the event");
+ assertTrue(events.get(0).getProperties().containsKey("hello"), "Key was not inserted");
+ assertEquals("world", events.get(0).getProperties().get("hello"), "Key value is incorrect");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/NeutralFilterFixture.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/NeutralFilterFixture.java
new file mode 100644
index 00000000000..d787acd1983
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/NeutralFilterFixture.java
@@ -0,0 +1,31 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A test fixture used by {@code src/test/resources/LOG4J2-3247.properties}.
+ */
+public class NeutralFilterFixture extends Filter {
+
+ @Override
+ public int decide(final LoggingEvent event) {
+ return NEUTRAL;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
new file mode 100644
index 00000000000..7b14885786d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationFactoryTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test configuration from Properties.
+ */
+class PropertiesConfigurationFactoryTest {
+
+ @BeforeAll
+ static void beforeAll() {
+ System.setProperty(
+ ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY,
+ "target/test-classes/log4j1-file-1.properties");
+ }
+
+ @Test
+ void testProperties() {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue(file.exists(), "File A1 was not created");
+ assertTrue(file.length() > 0, "File A1 is empty");
+ file = new File("target/temp.A2");
+ assertTrue(file.exists(), "File A2 was not created");
+ assertTrue(file.length() > 0, "File A2 is empty");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
new file mode 100644
index 00000000000..fb8ed815671
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesConfigurationTest.java
@@ -0,0 +1,368 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.FilterAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.ConsoleAppender;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
+import org.apache.logging.log4j.core.filter.CompositeFilter;
+import org.apache.logging.log4j.core.filter.DenyAllFilter;
+import org.apache.logging.log4j.core.filter.Filterable;
+import org.apache.logging.log4j.core.filter.LevelRangeFilter;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test configuration from Properties.
+ */
+class PropertiesConfigurationTest extends AbstractLog4j1ConfigurationTest {
+
+ private static final String TEST_KEY = "log4j.test.tmpdir";
+ private static final String SUFFIX = ".properties";
+
+ @Override
+ Configuration getConfiguration(final String configResourcePrefix) throws IOException {
+ final String configResource = configResourcePrefix + SUFFIX;
+ final InputStream inputStream = getResourceAsStream(configResource);
+ final ConfigurationSource source = new ConfigurationSource(inputStream);
+ final LoggerContext context = LoggerContext.getContext(false);
+ final Configuration configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+ assertNotNull(configuration, "No configuration created");
+ configuration.initialize();
+ return configuration;
+ }
+
+ @Test
+ void testConfigureNullPointerException() throws Exception {
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/LOG4J2-3247.properties")) {
+ // [LOG4J2-3247] configure() should not throw an NPE.
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CONSOLE");
+ assertNotNull(appender);
+ }
+ }
+
+ @Test
+ void testConsoleAppenderFilter() throws Exception {
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/LOG4J2-3247.properties")) {
+ // LOG4J2-3281 PropertiesConfiguration.buildAppender not adding filters to appender
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CONSOLE");
+ assertNotNull(appender);
+ final Filterable filterable = (Filterable) appender;
+ final FilterAdapter filter = (FilterAdapter) filterable.getFilter();
+ assertNotNull(filter);
+ assertInstanceOf(NeutralFilterFixture.class, filter.getFilter());
+ }
+ }
+
+ @Test
+ void testCustomAppenderFilter() throws Exception {
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/LOG4J2-3281.properties")) {
+ // LOG4J2-3281 PropertiesConfiguration.buildAppender not adding filters to appender
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CUSTOM");
+ assertNotNull(appender);
+ final Filterable filterable = (Filterable) appender;
+ final FilterAdapter filter = (FilterAdapter) filterable.getFilter();
+ assertNotNull(filter);
+ assertInstanceOf(NeutralFilterFixture.class, filter.getFilter());
+ }
+ }
+
+ @Test
+ void testConsoleAppenderLevelRangeFilter() throws Exception {
+ PluginManager.addPackage("org.apache.log4j.builders.filter");
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/LOG4J2-3326.properties")) {
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final Appender appender = configuration.getAppender("CUSTOM");
+ assertNotNull(appender);
+ final Filterable filterable = (Filterable) appender;
+ final CompositeFilter filter = (CompositeFilter) filterable.getFilter();
+ final org.apache.logging.log4j.core.Filter[] filters = filter.getFiltersArray();
+ final LevelRangeFilter filter1 = (LevelRangeFilter) filters[0];
+ // XXX: LOG4J2-2315
+ assertEquals(Level.OFF, filter1.getMinLevel());
+ assertEquals(Level.ALL, filter1.getMaxLevel());
+ final LevelRangeFilter filter2 = (LevelRangeFilter) filters[1];
+ assertEquals(Level.ERROR, filter2.getMinLevel());
+ assertEquals(Level.INFO, filter2.getMaxLevel());
+ final LevelRangeFilter filter3 = (LevelRangeFilter) filters[2];
+ assertEquals(Level.OFF, filter3.getMinLevel());
+ assertEquals(Level.ALL, filter3.getMaxLevel());
+
+ final ListAppender legacyAppender = (ListAppender) ((AppenderAdapter.Adapter) appender).getAppender();
+ final Logger logger = LogManager.getLogger(PropertiesConfigurationTest.class);
+
+ // deny
+ logger.trace("TRACE");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // deny
+ logger.debug("DEBUG");
+ assertEquals(0, legacyAppender.getEvents().size());
+ // accept
+ logger.info("INFO");
+ assertEquals(1, legacyAppender.getEvents().size());
+ // accept
+ logger.warn("WARN");
+ assertEquals(2, legacyAppender.getEvents().size());
+ // accept
+ logger.error("ERROR");
+ assertEquals(3, legacyAppender.getEvents().size());
+ // deny
+ logger.fatal("FATAL");
+ assertEquals(3, legacyAppender.getEvents().size());
+ }
+ }
+
+ @Test
+ void testConfigureAppenderDoesNotExist() throws Exception {
+ // Verify that we tolerate a logger which specifies an appender that does not exist.
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/LOG4J2-3407.properties")) {
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ }
+ }
+
+ @Test
+ void testListAppender() throws Exception {
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/log4j1-list.properties")) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ final Configuration configuration = loggerContext.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (final Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ } else if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(eventAppender, "No Event Appender");
+ assertNotNull(messageAppender, "No Message Appender");
+ final List events = eventAppender.getEvents();
+ assertTrue(events != null && !events.isEmpty(), "No events");
+ final List messages = messageAppender.getMessages();
+ assertTrue(messages != null && !messages.isEmpty(), "No messages");
+ }
+ }
+
+ @Test
+ void testProperties() throws Exception {
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/log4j1-file-1.properties")) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue(file.exists(), "File A1 was not created");
+ assertTrue(file.length() > 0, "File A1 is empty");
+ file = new File("target/temp.A2");
+ assertTrue(file.exists(), "File A2 was not created");
+ assertTrue(file.length() > 0, "File A2 is empty");
+ }
+ }
+
+ @Test
+ void testSystemProperties() throws Exception {
+ final String testPathLocation = "target";
+ System.setProperty(TEST_KEY, testPathLocation);
+ try (final LoggerContext loggerContext =
+ TestConfigurator.configure("target/test-classes/config-1.2/log4j-FileAppender-with-props.properties")) {
+ // [LOG4J2-3312] Bridge does not convert properties.
+ final Configuration configuration = loggerContext.getConfiguration();
+ assertNotNull(configuration);
+ final String name = "FILE_APPENDER";
+ final Appender appender = configuration.getAppender(name);
+ assertNotNull(appender, name);
+ assertInstanceOf(FileAppender.class, appender, appender.getClass().getName());
+ final FileAppender fileAppender = (FileAppender) appender;
+ // Two slashes because that's how the config file is setup.
+ assertEquals(testPathLocation + "/hadoop.log", fileAppender.getFileName());
+ } finally {
+ System.clearProperty(TEST_KEY);
+ }
+ }
+
+ @Override
+ @Test
+ public void testConsoleEnhancedPatternLayout() throws Exception {
+ super.testConsoleEnhancedPatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleHtmlLayout() throws Exception {
+ super.testConsoleHtmlLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsolePatternLayout() throws Exception {
+ super.testConsolePatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleSimpleLayout() throws Exception {
+ super.testConsoleSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testFileSimpleLayout() throws Exception {
+ super.testFileSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testNullAppender() throws Exception {
+ super.testNullAppender();
+ }
+
+ @Override
+ @Test
+ public void testConsoleCapitalization() throws Exception {
+ super.testConsoleCapitalization();
+ }
+
+ @Override
+ @Test
+ public void testConsoleTtccLayout() throws Exception {
+ super.testConsoleTtccLayout();
+ }
+
+ @Override
+ @Test
+ public void testRollingFileAppender() throws Exception {
+ super.testRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testDailyRollingFileAppender() throws Exception {
+ super.testDailyRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testRollingFileAppenderWithProperties() throws Exception {
+ super.testRollingFileAppenderWithProperties();
+ }
+
+ @Override
+ @Test
+ public void testSystemProperties1() throws Exception {
+ super.testSystemProperties1();
+ }
+
+ @Override
+ @Test
+ public void testSystemProperties2() throws Exception {
+ super.testSystemProperties2();
+ }
+
+ @Override
+ @Test
+ public void testDefaultValues() throws Exception {
+ super.testDefaultValues();
+ }
+
+ @Override
+ @Test
+ public void testMultipleFilters() throws Exception {
+ super.testMultipleFilters();
+ }
+
+ @Test
+ void testUntrimmedValues() throws Exception {
+ try {
+ final Configuration config = getConfiguration("config-1.2/log4j-untrimmed");
+ final LoggerConfig rootLogger = config.getRootLogger();
+ assertEquals(Level.DEBUG, rootLogger.getLevel());
+ final Appender appender = config.getAppender("Console");
+ assertInstanceOf(ConsoleAppender.class, appender);
+ final Layout extends Serializable> layout = appender.getLayout();
+ assertInstanceOf(PatternLayout.class, layout);
+ assertEquals("%v1Level - %m%n", ((PatternLayout) layout).getConversionPattern());
+ final Filter filter = ((Filterable) appender).getFilter();
+ assertInstanceOf(DenyAllFilter.class, filter);
+ config.start();
+ config.stop();
+ } catch (NoClassDefFoundError e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Override
+ @Test
+ public void testGlobalThreshold() throws Exception {
+ super.testGlobalThreshold();
+ }
+
+ @Test
+ void testEnhancedRollingFileAppender() throws Exception {
+ try (final LoggerContext ctx = configure("config-1.2/log4j-EnhancedRollingFileAppender")) {
+ final Configuration configuration = ctx.getConfiguration();
+ assertNotNull(configuration);
+ testEnhancedRollingFileAppender(configuration);
+ }
+ }
+
+ @Override
+ @Test
+ public void testLevelRangeFilter() throws Exception {
+ super.testLevelRangeFilter();
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java
new file mode 100644
index 00000000000..dbdca87084c
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesReconfigurationTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.function.FailableConsumer;
+import org.apache.log4j.CustomFileAppender;
+import org.apache.log4j.CustomNoopAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.FileManager;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationListener;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test reconfiguring with an XML configuration.
+ */
+class PropertiesReconfigurationTest {
+
+ private class TestListener implements ConfigurationListener {
+
+ @Override
+ public synchronized void onChange(final Reconfigurable reconfigurable) {
+ toggle.countDown();
+ }
+ }
+
+ private static final String CONFIG_CUSTOM_APPENDERS_1 = "target/test-classes/log4j1-appenders-custom-1.properties";
+ private static final String CONFIG_CUSTOM_APPENDERS_2 = "target/test-classes/log4j1-appenders-custom-2.properties";
+
+ private static final String CONFIG_FILE_APPENDER_1 = "target/test-classes/log4j1-file-1.properties";
+ private static final String CONFIG_FILE_APPENDER_2 = "target/test-classes/log4j1-file-2.properties";
+
+ private static final Duration FIVE_MINUTES = Duration.ofMinutes(5);
+
+ private final CountDownLatch toggle = new CountDownLatch(1);
+
+ private void assertCustomFileAppender(
+ final org.apache.log4j.Appender appender,
+ final boolean expectBoolean,
+ final int expectInt,
+ final String expectString) {
+ final CustomFileAppender customAppender = (CustomFileAppender) appender;
+ assertEquals(expectBoolean, customAppender.getBooleanA());
+ assertEquals(expectInt, customAppender.getIntA());
+ assertEquals(expectString, customAppender.getStringA());
+ }
+
+ private void assertCustomNoopAppender(
+ final org.apache.log4j.Appender appender,
+ final boolean expectBoolean,
+ final int expectInt,
+ final String expectString) {
+ final CustomNoopAppender customAppender = (CustomNoopAppender) appender;
+ assertEquals(expectBoolean, customAppender.getBooleanA());
+ assertEquals(expectInt, customAppender.getIntA());
+ assertEquals(expectString, customAppender.getStringA());
+ }
+
+ private void checkConfigureCustomAppenders(
+ final String configPath,
+ final boolean expectAppend,
+ final int expectInt,
+ final String expectString,
+ final FailableConsumer configurator)
+ throws IOException {
+ final File file = new File(configPath);
+ assertTrue(file.exists(), "No Config file");
+ try (final LoggerContext context = TestConfigurator.configure(file.toString())) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ // V1
+ checkCustomAppender("A1", expectAppend, expectInt, expectString);
+ checkCustomFileAppender("A2", expectAppend, expectInt, expectString);
+ }
+ }
+
+ private void checkConfigureFileAppender(final String configPath, final boolean expectAppend) throws IOException {
+ final File file = new File(configPath);
+ assertTrue(file.exists(), "No Config file");
+ try (final LoggerContext context = TestConfigurator.configure(file.toString())) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ final Configuration configuration = context.getConfiguration();
+ // Core
+ checkCoreFileAppender(expectAppend, configuration, "A1");
+ checkCoreFileAppender(expectAppend, configuration, "A2");
+ // V1
+ checkFileAppender(expectAppend, "A1");
+ checkFileAppender(expectAppend, "A2");
+ }
+ }
+
+ private void checkCoreFileAppender(final boolean expectAppend, final Appender appender) {
+ assertNotNull(appender);
+ final FileAppender fileAppender = (FileAppender) appender;
+ @SuppressWarnings("resource")
+ final FileManager manager = fileAppender.getManager();
+ assertNotNull(manager);
+ assertEquals(expectAppend, manager.isAppend());
+ }
+
+ private void checkCoreFileAppender(
+ final boolean expectAppend, final Configuration configuration, final String appenderName) {
+ checkCoreFileAppender(expectAppend, configuration.getAppender(appenderName));
+ }
+
+ private void checkCustomAppender(
+ final String appenderName, final boolean expectBoolean, final int expectInt, final String expectString) {
+ final Logger logger = LogManager.getRootLogger();
+ final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
+ assertNotNull(appender);
+ assertCustomNoopAppender(appender, expectBoolean, expectInt, expectString);
+ assertCustomNoopAppender(getAppenderFromContext(appenderName), expectBoolean, expectInt, expectString);
+ }
+
+ private void checkCustomFileAppender(
+ final String appenderName, final boolean expectBoolean, final int expectInt, final String expectString) {
+ final Logger logger = LogManager.getRootLogger();
+ final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
+ assertNotNull(appender);
+ assertCustomFileAppender(appender, expectBoolean, expectInt, expectString);
+ assertCustomFileAppender(getAppenderFromContext(appenderName), expectBoolean, expectInt, expectString);
+ }
+
+ private void checkFileAppender(final boolean expectAppend, final String appenderName) {
+ final Logger logger = LogManager.getRootLogger();
+ final org.apache.log4j.Appender appender = logger.getAppender(appenderName);
+ assertNotNull(appender);
+ final AppenderWrapper appenderWrapper = (AppenderWrapper) appender;
+ checkCoreFileAppender(expectAppend, appenderWrapper.getAppender());
+ }
+
+ @SuppressWarnings("unchecked")
+ private T getAppenderFromContext(final String appenderName) {
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ final LoggerConfig loggerConfig = context.getConfiguration().getRootLogger();
+ final AppenderAdapter.Adapter adapter =
+ (AppenderAdapter.Adapter) loggerConfig.getAppenders().get(appenderName);
+ return adapter != null ? (T) adapter.getAppender() : null;
+ }
+
+ /**
+ * Tests that configuring and reconfiguring CUSTOM appenders properly pick up different settings.
+ */
+ @Test
+ void testCustomAppenders_TestConfigurator() throws IOException {
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", TestConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", TestConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", TestConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", TestConfigurator::configure);
+ }
+
+ /**
+ * Tests that configuring and reconfiguring CUSTOM appenders properly pick up different settings.
+ */
+ @Test
+ void testCustomAppenders_PropertyConfigurator() throws IOException {
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", PropertyConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", PropertyConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_1, true, 1, "A", PropertyConfigurator::configure);
+ checkConfigureCustomAppenders(CONFIG_CUSTOM_APPENDERS_2, false, 2, "B", PropertyConfigurator::configure);
+ }
+
+ /**
+ * Tests that configuring and reconfiguring STOCK file appenders properly pick up different settings.
+ */
+ @Test
+ void testFileAppenders() throws Exception {
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_1, false);
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_2, true);
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_1, false);
+ checkConfigureFileAppender(CONFIG_FILE_APPENDER_2, true);
+ }
+
+ @Test
+ void testTestListener() throws Exception {
+ System.setProperty(Log4j1Configuration.MONITOR_INTERVAL, "1");
+ final File file = new File(CONFIG_FILE_APPENDER_1);
+ assertTrue(file.exists(), "No Config file");
+ final long configMillis = file.lastModified();
+ assertTrue(file.setLastModified(configMillis - FIVE_MINUTES.toMillis()), "Unable to modified file time");
+ try (final LoggerContext context = TestConfigurator.configure(file.toString())) {
+ final Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ final Configuration original = context.getConfiguration();
+ final TestListener listener = new TestListener();
+ original.addListener(listener);
+ file.setLastModified(System.currentTimeMillis());
+ try {
+ if (!toggle.await(3, TimeUnit.SECONDS)) {
+ fail("Reconfiguration timed out");
+ }
+ // Allow reconfiguration to complete.
+ Thread.sleep(500);
+ } catch (final InterruptedException ie) {
+ fail("Reconfiguration interupted");
+ }
+ final Configuration updated = context.getConfiguration();
+ assertNotEquals(original, updated, "Configurations are the same");
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesRollingWithPropertiesTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesRollingWithPropertiesTest.java
new file mode 100644
index 00000000000..ea740d93d4f
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/PropertiesRollingWithPropertiesTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test configuration from Properties.
+ */
+class PropertiesRollingWithPropertiesTest {
+
+ private static final String TEST_DIR = "target/" + PropertiesRollingWithPropertiesTest.class.getSimpleName();
+
+ @BeforeAll
+ static void setupSystemProperties() {
+ // Set system properties as a replacement for SystemPropertyTestRule
+ System.setProperty("test.directory", TEST_DIR);
+ System.setProperty("log4j.configuration", "target/test-classes/log4j1-rolling-properties.properties");
+ }
+
+ @Test
+ void testProperties() throws Exception {
+ final Path path = Paths.get(TEST_DIR, "somefile.log");
+ Files.deleteIfExists(path);
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ assertTrue(Files.exists(path), "Log file was not created");
+ assertTrue(Files.size(path) > 0, "Log file is empty");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
new file mode 100644
index 00000000000..86af9aea741
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test RewriteAppender
+ */
+class RewriteAppenderTest {
+
+ @BeforeAll
+ static void beforeAll() {
+ System.setProperty(
+ ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-rewrite.xml");
+ }
+
+ @AfterEach
+ void after() {
+ ThreadContext.clearMap();
+ }
+
+ @Test
+ void testRewrite() {
+ final Logger logger = LogManager.getLogger("test");
+ ThreadContext.put("key1", "This is a test");
+ ThreadContext.put("hello", "world");
+ final long logTime = System.currentTimeMillis();
+ logger.debug("Say hello");
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ final Configuration configuration = context.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ for (Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(eventAppender, "No Event Appender");
+ final List events = eventAppender.getEvents();
+ assertTrue(events != null && !events.isEmpty(), "No events");
+ assertNotNull(events.get(0).getProperties(), "No properties in the event");
+ assertTrue(events.get(0).getProperties().containsKey("key2"), "Key was not inserted");
+ assertEquals("Log4j", events.get(0).getProperties().get("key2"), "Key value is incorrect");
+ assertTrue(events.get(0).getTimeStamp() >= logTime, "Timestamp is before point of logging");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
new file mode 100644
index 00000000000..2294c556d18
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SocketAppenderConfigurationTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.util.Map;
+import org.apache.log4j.layout.Log4j1XmlLayout;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.SocketAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.core.net.TcpSocketManager;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests configuring a Syslog appender.
+ */
+class SocketAppenderConfigurationTest {
+
+ private SocketAppender check(final Protocol expected, final Configuration configuration) {
+ final Map appenders = configuration.getAppenders();
+ assertNotNull(appenders);
+ final String appenderName = "socket";
+ final Appender appender = appenders.get(appenderName);
+ assertNotNull(appender, "Missing appender " + appenderName);
+ final SocketAppender socketAppender = (SocketAppender) appender;
+ @SuppressWarnings("resource")
+ final TcpSocketManager manager = (TcpSocketManager) socketAppender.getManager();
+ final String prefix = expected + ":";
+ assertTrue(
+ manager.getName().startsWith(prefix),
+ () -> String.format("'%s' does not start with '%s'", manager.getName(), prefix));
+ // Threshold
+ final ThresholdFilter filter = (ThresholdFilter) socketAppender.getFilter();
+ assertEquals(Level.DEBUG, filter.getLevel());
+ // Host
+ assertEquals("localhost", manager.getHost());
+ // Port
+ assertEquals(9999, manager.getPort());
+ // Port
+ assertEquals(100, manager.getReconnectionDelayMillis());
+ return socketAppender;
+ }
+
+ private void checkProtocolPropertiesConfig(final Protocol expected, final String xmlPath) throws IOException {
+ check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ private SocketAppender checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
+ return check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ @Test
+ void testProperties() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.properties");
+ }
+
+ @Test
+ void testPropertiesXmlLayout() throws Exception {
+ final SocketAppender socketAppender =
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket-xml-layout.properties");
+ assertInstanceOf(Log4j1XmlLayout.class, socketAppender.getLayout());
+ }
+
+ @Test
+ void testXml() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-socket.xml");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java
new file mode 100644
index 00000000000..7ad3774deab
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/StartsWithFilter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A Filter used in tests.
+ */
+public class StartsWithFilter extends Filter {
+
+ @Override
+ public int decide(final LoggingEvent event) {
+ final String message = String.valueOf(event.getMessage());
+ if (message.startsWith("DENY")) {
+ return DENY;
+ } else if (message.startsWith("ACCEPT")) {
+ return ACCEPT;
+ }
+ return NEUTRAL;
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java
new file mode 100644
index 00000000000..1313cee2c44
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderConfigurationTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.Map;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.SyslogAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.filter.ThresholdFilter;
+import org.apache.logging.log4j.core.net.AbstractSocketManager;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.WritesSystemProperty;
+
+/**
+ * Tests configuring a Syslog appender.
+ */
+@UsingStatusListener
+@WritesSystemProperty
+class SyslogAppenderConfigurationTest {
+
+ private static ServerSocket tcpSocket;
+
+ @BeforeAll
+ static void setup() throws IOException {
+ // TCP appenders log an error if there is no server socket
+ tcpSocket = new ServerSocket(0);
+ System.setProperty("syslog.port", Integer.toString(tcpSocket.getLocalPort()));
+ }
+
+ @AfterAll
+ static void cleanup() throws IOException {
+ System.clearProperty("syslog.port");
+ tcpSocket.close();
+ }
+
+ private void check(final Protocol expected, final Configuration configuration) {
+ final Map appenders = configuration.getAppenders();
+ assertNotNull(appenders);
+ final String appenderName = "syslog";
+ final Appender appender = appenders.get(appenderName);
+ assertNotNull(appender, "Missing appender " + appenderName);
+ final SyslogAppender syslogAppender = (SyslogAppender) appender;
+ @SuppressWarnings("resource")
+ final AbstractSocketManager manager = syslogAppender.getManager();
+ final String prefix = expected + ":";
+ assertTrue(
+ manager.getName().startsWith(prefix),
+ () -> String.format("'%s' does not start with '%s'", manager.getName(), prefix));
+ // Threshold
+ final ThresholdFilter filter = (ThresholdFilter) syslogAppender.getFilter();
+ assertEquals(Level.DEBUG, filter.getLevel());
+ // Host
+ assertEquals("localhost", manager.getHost());
+ // Port
+ assertEquals(tcpSocket.getLocalPort(), manager.getPort());
+ }
+
+ private void checkProtocolPropertiesConfig(final Protocol expected, final String xmlPath) throws IOException {
+ check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ private void checkProtocolXmlConfig(final Protocol expected, final String xmlPath) throws IOException {
+ check(expected, TestConfigurator.configure(xmlPath).getConfiguration());
+ }
+
+ @Test
+ void testPropertiesProtocolDefault() throws Exception {
+ checkProtocolPropertiesConfig(Protocol.TCP, "target/test-classes/log4j1-syslog-protocol-default.properties");
+ }
+
+ @Test
+ void testPropertiesProtocolTcp() throws Exception {
+ checkProtocolPropertiesConfig(Protocol.TCP, "target/test-classes/log4j1-syslog-protocol-tcp.properties");
+ }
+
+ @Test
+ void testPropertiesProtocolUdp() throws Exception {
+ checkProtocolPropertiesConfig(Protocol.UDP, "target/test-classes/log4j1-syslog-protocol-udp.properties");
+ }
+
+ @Test
+ void testXmlProtocolDefault() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-syslog.xml");
+ }
+
+ @Test
+ void testXmlProtocolTcp() throws Exception {
+ checkProtocolXmlConfig(Protocol.TCP, "target/test-classes/log4j1-syslog-protocol-tcp.xml");
+ }
+
+ @Test
+ void testXmlProtocolUdp() throws Exception {
+ checkProtocolXmlConfig(Protocol.UDP, "target/test-classes/log4j1-syslog-protocol-udp.xml");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
new file mode 100644
index 00000000000..720577bc86e
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.log4j.config;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+
+import java.io.IOException;
+import java.util.List;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.apache.logging.log4j.core.test.net.mock.MockSyslogServer;
+import org.apache.logging.log4j.core.test.net.mock.MockSyslogServerFactory;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+class SyslogAppenderTest {
+
+ private static MockSyslogServer syslogServer;
+
+ @BeforeAll
+ static void beforeAll() throws IOException {
+ initTCPTestEnvironment(null);
+ System.setProperty("syslog.port", Integer.toString(syslogServer.getLocalPort()));
+ System.setProperty(
+ ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-syslog.xml");
+ }
+
+ @AfterAll
+ static void afterAll() {
+ System.clearProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY);
+ syslogServer.shutdown();
+ }
+
+ @Test
+ void sendMessage() throws Exception {
+ final Logger logger = LogManager.getLogger(SyslogAppenderTest.class);
+ logger.info("This is a test");
+ List messages = null;
+ for (int i = 0; i < 5; ++i) {
+ Thread.sleep(250);
+ messages = syslogServer.getMessageList();
+ if (messages != null && !messages.isEmpty()) {
+ break;
+ }
+ }
+ assertThat(messages, hasSize(1));
+ }
+
+ protected static void initTCPTestEnvironment(final String messageFormat) throws IOException {
+ syslogServer = MockSyslogServerFactory.createTCPSyslogServer();
+ syslogServer.start();
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/TestConfigurator.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/TestConfigurator.java
new file mode 100644
index 00000000000..8ee8b2b9f70
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/TestConfigurator.java
@@ -0,0 +1,53 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+
+public class TestConfigurator {
+
+ public static LoggerContext configure(final String configLocation) throws IOException {
+ final Path path = Paths.get(configLocation);
+ try (final InputStream inputStream = Files.newInputStream(path)) {
+ final ConfigurationSource source = new ConfigurationSource(inputStream, path);
+ final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = null;
+ if (configLocation.endsWith(PropertiesConfigurationFactory.FILE_EXTENSION)) {
+ configuration = new PropertiesConfigurationFactory().getConfiguration(context, source);
+ } else if (configLocation.endsWith(XmlConfigurationFactory.FILE_EXTENSION)) {
+ configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ } else {
+ fail("Test infra does not support " + configLocation);
+ }
+ assertNotNull(configuration, "No configuration created");
+ Configurator.reconfigure(configuration);
+ return context;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
new file mode 100644
index 00000000000..e28121da899
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationFactoryTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.core.config.ConfigurationFactory;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test configuration from XML.
+ */
+class XmlConfigurationFactoryTest {
+
+ @BeforeAll
+ static void beforeAll() {
+ System.setProperty(
+ ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-file.xml");
+ }
+
+ @Test
+ void testXML() {
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue(file.exists(), "File A1 was not created");
+ assertTrue(file.length() > 0, "File A1 is empty");
+ file = new File("target/temp.A2");
+ assertTrue(file.exists(), "File A2 was not created");
+ assertTrue(file.length() > 0, "File A2 is empty");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
new file mode 100644
index 00000000000..95a260ed862
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import org.apache.log4j.ListAppender;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.bridge.AppenderAdapter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
+import org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test configuration from XML.
+ */
+class XmlConfigurationTest extends AbstractLog4j1ConfigurationTest {
+
+ private static final String SUFFIX = ".xml";
+
+ @Override
+ Configuration getConfiguration(final String configResourcePrefix) throws IOException {
+ final String configResource = configResourcePrefix + SUFFIX;
+ final InputStream inputStream = getResourceAsStream(configResource);
+ final ConfigurationSource source = new ConfigurationSource(inputStream);
+ final LoggerContext context = LoggerContext.getContext(false);
+ final Configuration configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+ assertNotNull(configuration, "No configuration created");
+ configuration.initialize();
+ return configuration;
+ }
+
+ @Test
+ void testListAppender() throws Exception {
+ final LoggerContext loggerContext = TestConfigurator.configure("target/test-classes/log4j1-list.xml");
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ final Configuration configuration = loggerContext.getConfiguration();
+ final Map appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ ListAppender messageAppender = null;
+ for (final Map.Entry entry : appenders.entrySet()) {
+ if (entry.getKey().equals("list")) {
+ messageAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ } else if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull(eventAppender, "No Event Appender");
+ assertNotNull(messageAppender, "No Message Appender");
+ final List events = eventAppender.getEvents();
+ assertTrue(events != null && !events.isEmpty(), "No events");
+ final List messages = messageAppender.getMessages();
+ assertTrue(messages != null && !messages.isEmpty(), "No messages");
+ }
+
+ @Test
+ void testXML() throws Exception {
+ TestConfigurator.configure("target/test-classes/log4j1-file.xml");
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ File file = new File("target/temp.A1");
+ assertTrue(file.exists(), "File A1 was not created");
+ assertTrue(file.length() > 0, "File A1 is empty");
+ file = new File("target/temp.A2");
+ assertTrue(file.exists(), "File A2 was not created");
+ assertTrue(file.length() > 0, "File A2 is empty");
+ }
+
+ @Override
+ @Test
+ public void testConsoleEnhancedPatternLayout() throws Exception {
+ super.testConsoleEnhancedPatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleHtmlLayout() throws Exception {
+ super.testConsoleHtmlLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsolePatternLayout() throws Exception {
+ super.testConsolePatternLayout();
+ }
+
+ @Override
+ @Test
+ public void testConsoleSimpleLayout() throws Exception {
+ super.testConsoleSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testFileSimpleLayout() throws Exception {
+ super.testFileSimpleLayout();
+ }
+
+ @Override
+ @Test
+ public void testNullAppender() throws Exception {
+ super.testNullAppender();
+ }
+
+ @Override
+ @Test
+ public void testConsoleCapitalization() throws Exception {
+ super.testConsoleCapitalization();
+ }
+
+ @Override
+ @Test
+ public void testConsoleTtccLayout() throws Exception {
+ super.testConsoleTtccLayout();
+ }
+
+ @Override
+ @Test
+ public void testRollingFileAppender() throws Exception {
+ super.testRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testDailyRollingFileAppender() throws Exception {
+ super.testDailyRollingFileAppender();
+ }
+
+ @Override
+ @Test
+ public void testSystemProperties1() throws Exception {
+ super.testSystemProperties1();
+ }
+
+ @Override
+ @Test
+ public void testDefaultValues() throws Exception {
+ super.testDefaultValues();
+ }
+
+ @Override
+ @Test
+ public void testMultipleFilters() throws Exception {
+ super.testMultipleFilters();
+ }
+
+ @Override
+ @Test
+ public void testGlobalThreshold() throws Exception {
+ super.testGlobalThreshold();
+ }
+
+ @Test
+ void testEnhancedRollingFileAppender() throws Exception {
+ try (final LoggerContext ctx = configure("config-1.2/log4j-EnhancedRollingFileAppender")) {
+ final Configuration configuration = ctx.getConfiguration();
+ assertNotNull(configuration);
+ testEnhancedRollingFileAppender(configuration);
+ // Only supported through XML configuration
+ final Appender appender = configuration.getAppender("MIXED");
+ assertInstanceOf(RollingFileAppender.class, appender, "is RollingFileAppender");
+ final TriggeringPolicy policy = ((RollingFileAppender) appender).getTriggeringPolicy();
+ assertInstanceOf(CompositeTriggeringPolicy.class, policy, "is CompositeTriggeringPolicy");
+ final TriggeringPolicy[] policies = ((CompositeTriggeringPolicy) policy).getTriggeringPolicies();
+ assertEquals(2, policies.length);
+ assertInstanceOf(TimeBasedTriggeringPolicy.class, policies[0], "is TimeBasedTriggeringPolicy");
+ assertInstanceOf(SizeBasedTriggeringPolicy.class, policies[1], "is SizeBasedTriggeringPolicy");
+ }
+ }
+
+ @Override
+ @Test
+ public void testLevelRangeFilter() throws Exception {
+ super.testLevelRangeFilter();
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlReconfigurationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlReconfigurationTest.java
new file mode 100644
index 00000000000..ee9dbfcdb9a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlReconfigurationTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.File;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationListener;
+import org.apache.logging.log4j.core.config.Reconfigurable;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test reconfiguring with an XML configuration.
+ */
+class XmlReconfigurationTest {
+
+ private static final String CONFIG = "target/test-classes/log4j1-file.xml";
+ private static final long FIVE_MINUTES = 5 * 60 * 1000;
+
+ private final CountDownLatch toggle = new CountDownLatch(1);
+
+ @Test
+ void testReconfiguration() throws Exception {
+ System.setProperty(Log4j1Configuration.MONITOR_INTERVAL, "1");
+ final File file = new File(CONFIG);
+ assertNotNull(file, "No Config file");
+ final long configMillis = file.lastModified();
+ assertTrue(file.setLastModified(configMillis - FIVE_MINUTES), "Unable to modified file time");
+ final LoggerContext context = TestConfigurator.configure(file.toString());
+ final Logger logger = LogManager.getLogger("test");
+ logger.info("Hello");
+ final Configuration original = context.getConfiguration();
+ final TestListener listener = new TestListener();
+ original.addListener(listener);
+ file.setLastModified(System.currentTimeMillis());
+ try {
+ if (!toggle.await(3, TimeUnit.SECONDS)) {
+ fail("Reconfiguration timed out");
+ }
+ // Allow reconfiguration to complete.
+ Thread.sleep(500);
+ } catch (InterruptedException ie) {
+ fail("Reconfiguration interupted");
+ }
+ final Configuration updated = context.getConfiguration();
+ assertNotEquals(original, updated, "Configurations are the same");
+ }
+
+ private class TestListener implements ConfigurationListener {
+
+ public synchronized void onChange(final Reconfigurable reconfigurable) {
+ toggle.countDown();
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlRollingWithPropertiesTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlRollingWithPropertiesTest.java
new file mode 100644
index 00000000000..0847a3cc93e
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlRollingWithPropertiesTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test configuration from Properties.
+ */
+class XmlRollingWithPropertiesTest {
+
+ private static final String TEST_DIR = "target/" + XmlRollingWithPropertiesTest.class.getSimpleName();
+
+ @BeforeAll
+ static void setupSystemProperties() {
+ System.setProperty("test.directory", TEST_DIR);
+ System.setProperty("log4j.configuration", "target/test-classes/log4j1-rolling-properties.xml");
+ }
+
+ @Test
+ void testProperties() throws Exception {
+ // ${test.directory}/logs/etl.log
+ final Path path = Paths.get(TEST_DIR, "logs/etl.log");
+ Files.deleteIfExists(path);
+ final Logger logger = LogManager.getLogger("test");
+ logger.debug("This is a test of the root logger");
+ assertTrue(Files.exists(path), "Log file was not created " + path);
+ assertTrue(Files.size(path) > 0, "Log file is empty " + path);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
new file mode 100644
index 00000000000..d45b21448e0
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
@@ -0,0 +1,229 @@
+/*
+ * 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.log4j.helpers;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Test {@link BoundedFIFO}.
+ *
+ * @since 0.9.1
+ */
+public class BoundedFIFOTestCase extends TestCase {
+ static Logger cat = Logger.getLogger("x");
+
+ static int MAX = 1000;
+
+ static LoggingEvent[] e = new LoggingEvent[MAX];
+
+ public static Test suite() {
+ final TestSuite suite = new TestSuite();
+ suite.addTest(new BoundedFIFOTestCase("test1"));
+ suite.addTest(new BoundedFIFOTestCase("test2"));
+ suite.addTest(new BoundedFIFOTestCase("testResize1"));
+ suite.addTest(new BoundedFIFOTestCase("testResize2"));
+ suite.addTest(new BoundedFIFOTestCase("testResize3"));
+ return suite;
+ }
+
+ {
+ for (int i = 0; i < MAX; i++) {
+ e[i] = new LoggingEvent("", cat, Level.DEBUG, "e" + i, null);
+ }
+ }
+
+ public BoundedFIFOTestCase(final String name) {
+ super(name);
+ }
+
+ int min(final int a, final int b) {
+ return Math.min(a, b);
+ }
+
+ @Override
+ public void setUp() {}
+
+ /**
+ * Pattern: +++++..-----..
+ */
+ public void test1() {
+ for (int size = 1; size <= 128; size *= 2) {
+ final BoundedFIFO bf = new BoundedFIFO(size);
+
+ assertEquals(bf.getMaxSize(), size);
+ assertNull(bf.get());
+
+ int i;
+ int j;
+ int k;
+
+ for (i = 1; i < 2 * size; i++) {
+ for (j = 0; j < i; j++) {
+ // System.out.println("Putting "+e[j]);
+ bf.put(e[j]);
+ assertEquals(bf.length(), j < size ? j + 1 : size);
+ }
+ final int max = Math.min(size, j);
+ j--;
+ for (k = 0; k <= j; k++) {
+ // System.out.println("max="+max+", j="+j+", k="+k);
+ assertEquals(bf.length(), Math.max(max - k, 0));
+ final Object r = bf.get();
+ // System.out.println("Got "+r);
+ if (k >= size) {
+ assertNull(r);
+ } else {
+ assertEquals(r, e[k]);
+ }
+ }
+ }
+ // System.out.println("Passed size="+size);
+ }
+ }
+
+ /**
+ * Pattern: ++++--++--++
+ */
+ public void test2() {
+ final int size = 3;
+ final BoundedFIFO bf = new BoundedFIFO(size);
+
+ bf.put(e[0]);
+ assertEquals(bf.get(), e[0]);
+ assertNull(bf.get());
+
+ bf.put(e[1]);
+ assertEquals(1, bf.length());
+ bf.put(e[2]);
+ assertEquals(2, bf.length());
+ bf.put(e[3]);
+ assertEquals(3, bf.length());
+ assertEquals(bf.get(), e[1]);
+ assertEquals(2, bf.length());
+ assertEquals(bf.get(), e[2]);
+ assertEquals(1, bf.length());
+ assertEquals(bf.get(), e[3]);
+ assertEquals(0, bf.length());
+ assertNull(bf.get());
+ assertEquals(0, bf.length());
+ }
+
+ /**
+ * Pattern ++++++++++++++++++++ (insert only);
+ */
+ public void testResize1() {
+ final int size = 10;
+
+ for (int n = 1; n < size * 2; n++) {
+ for (int i = 0; i < size * 2; i++) {
+
+ final BoundedFIFO bf = new BoundedFIFO(size);
+ for (int f = 0; f < i; f++) {
+ bf.put(e[f]);
+ }
+
+ bf.resize(n);
+ final int expectedSize = min(n, min(i, size));
+ assertEquals(bf.length(), expectedSize);
+ for (int c = 0; c < expectedSize; c++) {
+ assertEquals(bf.get(), e[c]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Pattern ++...+ --...-
+ */
+ public void testResize2() {
+ final int size = 10;
+
+ for (int n = 1; n < size * 2; n++) {
+ for (int i = 0; i < size * 2; i++) {
+ for (int d = 0; d < min(i, size); d++) {
+
+ final BoundedFIFO bf = new BoundedFIFO(size);
+ for (int p = 0; p < i; p++) {
+ bf.put(e[p]);
+ }
+
+ for (int g = 0; g < d; g++) {
+ bf.get();
+ }
+
+ // x = the number of elems in
+ final int x = bf.length();
+
+ bf.resize(n);
+
+ final int expectedSize = min(n, x);
+ assertEquals(bf.length(), expectedSize);
+
+ for (int c = 0; c < expectedSize; c++) {
+ assertEquals(bf.get(), e[c + d]);
+ }
+ assertNull(bf.get());
+ }
+ }
+ }
+ }
+
+ /**
+ * Pattern: i inserts, d deletes, r inserts
+ */
+ public void testResize3() {
+ final int size = 10;
+
+ for (int n = 1; n < size * 2; n++) {
+ for (int i = 0; i < size; i++) {
+ for (int d = 0; d < i; d++) {
+ for (int r = 0; r < d; r++) {
+
+ final BoundedFIFO bf = new BoundedFIFO(size);
+ for (int p0 = 0; p0 < i; p0++) {
+ bf.put(e[p0]);
+ }
+
+ for (int g = 0; g < d; g++) {
+ bf.get();
+ }
+ for (int p1 = 0; p1 < r; p1++) {
+ bf.put(e[i + p1]);
+ }
+
+ final int x = bf.length();
+
+ bf.resize(n);
+
+ final int expectedSize = min(n, x);
+ assertEquals(bf.length(), expectedSize);
+
+ for (int c = 0; c < expectedSize; c++) {
+ assertEquals(bf.get(), e[c + d]);
+ }
+ // assertNull(bf.get());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
new file mode 100644
index 00000000000..864e400e30a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
@@ -0,0 +1,147 @@
+/*
+ * 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.log4j.helpers;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Tests {@link CyclicBuffer}.
+ */
+public class CyclicBufferTestCase extends TestCase {
+
+ static Logger cat = Logger.getLogger("x");
+
+ static int MAX = 1000;
+
+ static LoggingEvent[] e = new LoggingEvent[MAX];
+
+ public static Test suite() {
+ final TestSuite suite = new TestSuite();
+ suite.addTest(new CyclicBufferTestCase("test0"));
+ suite.addTest(new CyclicBufferTestCase("test1"));
+ suite.addTest(new CyclicBufferTestCase("testResize"));
+ return suite;
+ }
+
+ {
+ for (int i = 0; i < MAX; i++) {
+ e[i] = new LoggingEvent("", cat, Level.DEBUG, "e" + i, null);
+ }
+ }
+
+ public CyclicBufferTestCase(final String name) {
+ super(name);
+ }
+
+ void doTest1(final int size) {
+ // System.out.println("Doing test with size = "+size);
+ final CyclicBuffer cb = new CyclicBuffer(size);
+
+ assertEquals(cb.getMaxSize(), size);
+
+ for (int i = -(size + 10); i < (size + 10); i++) {
+ assertNull(cb.get(i));
+ }
+
+ for (int i = 0; i < MAX; i++) {
+ cb.add(e[i]);
+ final int limit = Math.min(i, size - 1);
+
+ // System.out.println("\nLimit is " + limit + ", i="+i);
+
+ for (int j = limit; j >= 0; j--) {
+ // System.out.println("i= "+i+", j="+j);
+ assertEquals(cb.get(j), e[i - (limit - j)]);
+ }
+ assertNull(cb.get(-1));
+ assertNull(cb.get(limit + 1));
+ }
+ }
+
+ void doTestResize(final int initialSize, final int numberOfAdds, final int newSize) {
+ // System.out.println("initialSize = "+initialSize+", numberOfAdds="
+ // +numberOfAdds+", newSize="+newSize);
+ final CyclicBuffer cb = new CyclicBuffer(initialSize);
+ for (int i = 0; i < numberOfAdds; i++) {
+ cb.add(e[i]);
+ }
+ cb.resize(newSize);
+
+ int offset = numberOfAdds - initialSize;
+ if (offset < 0) {
+ offset = 0;
+ }
+
+ int len = Math.min(newSize, numberOfAdds);
+ len = Math.min(len, initialSize);
+ // System.out.println("Len = "+len+", offset="+offset);
+ for (int j = 0; j < len; j++) {
+ assertEquals(cb.get(j), e[offset + j]);
+ }
+ }
+
+ @Override
+ public void setUp() {}
+
+ public void test0() {
+ final int size = 2;
+
+ CyclicBuffer cb = new CyclicBuffer(size);
+ assertEquals(size, cb.getMaxSize());
+
+ cb.add(e[0]);
+ assertEquals(1, cb.length());
+ assertEquals(cb.get(), e[0]);
+ assertEquals(0, cb.length());
+ assertNull(cb.get());
+ assertEquals(0, cb.length());
+
+ cb = new CyclicBuffer(size);
+ cb.add(e[0]);
+ cb.add(e[1]);
+ assertEquals(2, cb.length());
+ assertEquals(cb.get(), e[0]);
+ assertEquals(1, cb.length());
+ assertEquals(cb.get(), e[1]);
+ assertEquals(0, cb.length());
+ assertNull(cb.get());
+ assertEquals(0, cb.length());
+ }
+
+ /**
+ * Test a buffer of size 1,2,4,8,..,128
+ */
+ public void test1() {
+ for (int bufSize = 1; bufSize <= 128; bufSize *= 2) {
+ doTest1(bufSize);
+ }
+ }
+
+ public void testResize() {
+ for (int isize = 1; isize <= 128; isize *= 2) {
+ doTestResize(isize, isize / 2 + 1, isize / 2 + 1);
+ doTestResize(isize, isize / 2 + 1, isize + 10);
+ doTestResize(isize, isize + 10, isize / 2 + 1);
+ doTestResize(isize, isize + 10, isize + 10);
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
new file mode 100644
index 00000000000..8ec4d21d7a4
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
@@ -0,0 +1,288 @@
+/*
+ * 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.log4j.helpers;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+import org.apache.log4j.Layout;
+import org.apache.log4j.LayoutTest;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Tests {@link DateLayout}.
+ */
+public class DateLayoutTest extends LayoutTest {
+
+ /**
+ * Construct a new instance of LayoutTest.
+ *
+ * @param testName test name.
+ */
+ public DateLayoutTest(final String testName) {
+ super(testName);
+ }
+
+ /**
+ * Constructor for use by derived tests.
+ *
+ * @param testName name of test.
+ * @param expectedContentType expected value for getContentType().
+ * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+ * @param expectedHeader expected value for getHeader().
+ * @param expectedFooter expected value for getFooter().
+ */
+ protected DateLayoutTest(
+ final String testName,
+ final String expectedContentType,
+ final boolean expectedIgnoresThrowable,
+ final String expectedHeader,
+ final String expectedFooter) {
+ super(testName, expectedContentType, expectedIgnoresThrowable, expectedHeader, expectedFooter);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected Layout createLayout() {
+ return new MockLayout();
+ }
+
+ /**
+ * Tests DateLayout.NULL_DATE_FORMAT constant.
+ */
+ public void testNullDateFormat() {
+ assertEquals("NULL", DateLayout.NULL_DATE_FORMAT);
+ }
+
+ /**
+ * Tests DateLayout.RELATIVE constant.
+ */
+ public void testRelativeTimeDateFormat() {
+ assertEquals("RELATIVE", DateLayout.RELATIVE_TIME_DATE_FORMAT);
+ }
+
+ /**
+ * Tests DateLayout.DATE_FORMAT_OPTION constant.
+ *
+ * @deprecated since constant is deprecated
+ */
+ public void testDateFormatOption() {
+ assertEquals("DateFormat", DateLayout.DATE_FORMAT_OPTION);
+ }
+
+ /**
+ * Tests DateLayout.TIMEZONE_OPTION constant.
+ *
+ * @deprecated since constant is deprecated
+ */
+ public void testTimeZoneOption() {
+ assertEquals("TimeZone", DateLayout.TIMEZONE_OPTION);
+ }
+
+ /**
+ * Tests getOptionStrings().
+ *
+ * @deprecated since getOptionStrings is deprecated.
+ *
+ */
+ public void testGetOptionStrings() {
+ final String[] options = ((DateLayout) createLayout()).getOptionStrings();
+ assertEquals(2, options.length);
+ }
+
+ /**
+ * Tests setting DateFormat through setOption method.
+ *
+ * @deprecated since setOption is deprecated.
+ */
+ public void testSetOptionDateFormat() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setOption("dAtefOrmat", "foobar");
+ assertEquals("FOOBAR", layout.getDateFormat());
+ }
+
+ /**
+ * Tests setting TimeZone through setOption method.
+ *
+ * @deprecated since setOption is deprecated.
+ */
+ public void testSetOptionTimeZone() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setOption("tImezOne", "+05:00");
+ assertEquals("+05:00", layout.getTimeZone());
+ }
+
+ /**
+ * Tests setDateFormat.
+ */
+ public void testSetDateFormat() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("ABSOLUTE");
+ assertEquals("ABSOLUTE", layout.getDateFormat());
+ }
+
+ /**
+ * Tests setTimeZone.
+ */
+ public void testSetTimeZone() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setTimeZone("+05:00");
+ assertEquals("+05:00", layout.getTimeZone());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with null.
+ */
+ public void testSetDateFormatNull() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat((String) null, null);
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "NULL".
+ */
+ public void testSetDateFormatNullString() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("NuLL", null);
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "RELATIVE".
+ */
+ public void testSetDateFormatRelative() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("rElatIve", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "ABSOLUTE".
+ */
+ public void testSetDateFormatAbsolute() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("aBsolUte", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "DATETIME".
+ */
+ public void testSetDateFormatDateTime() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("dAte", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "ISO8601".
+ */
+ public void testSetDateFormatISO8601() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("iSo8601", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests 2 parameter setDateFormat with "HH:mm:ss".
+ */
+ public void testSetDateFormatSimple() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("HH:mm:ss", TimeZone.getDefault());
+ }
+
+ /**
+ * Tests activateOptions.
+ */
+ public void testActivateOptions() {
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat("HH:mm:ss");
+ layout.setTimeZone("+05:00");
+ layout.activateOptions();
+ }
+
+ /**
+ * Tests setDateFormat(DateFormat, TimeZone).
+ */
+ public void testSetDateFormatWithFormat() {
+ final DateFormat format = new SimpleDateFormat("HH:mm");
+ final DateLayout layout = (DateLayout) createLayout();
+ layout.setDateFormat(format, TimeZone.getDefault());
+ }
+
+ /**
+ * Tests IS08601DateFormat class.
+ *
+ * @deprecated since ISO8601DateFormat is deprecated
+ */
+ public void testISO8601Format() {
+ final DateFormat format = new ISO8601DateFormat();
+ final Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.set(1970, 0, 1, 0, 0, 0);
+ final String actual = format.format(calendar.getTime());
+ assertEquals("1970-01-01 00:00:00,000", actual);
+ }
+
+ /**
+ * Tests DateTimeDateFormat class.
+ *
+ * @deprecated since DateTimeDateFormat is deprecated
+ */
+ public void testDateTimeFormat() {
+ final DateFormat format = new DateTimeDateFormat();
+ final Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.set(1970, 0, 1, 0, 0, 0);
+ final String actual = format.format(calendar.getTime());
+ final SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,SSS");
+ final String expected = df.format(calendar.getTime());
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Concrete Layout class for tests.
+ */
+ private static final class MockLayout extends DateLayout {
+ /**
+ * Create new instance of MockLayout.
+ */
+ public MockLayout() {
+ //
+ // checks that protected fields are properly initialized
+ assertNotNull(pos);
+ assertNotNull(date);
+ assertNull(dateFormat);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String format(final LoggingEvent event) {
+ return "Mock";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void activateOptions() {}
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean ignoresThrowable() {
+ return true;
+ }
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java
new file mode 100644
index 00000000000..f852023fe59
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/LogLogTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.log4j.helpers;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link LogLog}.
+ */
+public class LogLogTest extends TestCase {
+
+ /**
+ * Create new instance of LogLogTest.
+ *
+ * @param testName test name
+ */
+ public LogLogTest(final String testName) {
+ super(testName);
+ }
+
+ /**
+ * Check value of CONFIG_DEBUG_KEY.
+ *
+ * @deprecated since constant is deprecated
+ */
+ @Deprecated
+ public void testConfigDebugKey() {
+ assertEquals("log4j.configDebug", LogLog.CONFIG_DEBUG_KEY);
+ }
+
+ /**
+ * Check value of DEBUG_KEY.
+ */
+ public void testDebugKey() {
+ assertEquals("log4j.debug", LogLog.DEBUG_KEY);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterLevelTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterLevelTest.java
new file mode 100644
index 00000000000..2156540c111
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/OptionConverterLevelTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.log4j.helpers;
+
+import static org.apache.log4j.helpers.OptionConverter.toLog4j1Level;
+import static org.apache.log4j.helpers.OptionConverter.toLog4j2Level;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.bridge.LogEventAdapter;
+import org.apache.logging.log4j.spi.StandardLevel;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class OptionConverterLevelTest {
+
+ static Stream standardLevels() {
+ return Arrays.stream(StandardLevel.values())
+ .map(Enum::name)
+ .map(name -> Arguments.of(Level.toLevel(name), org.apache.logging.log4j.Level.toLevel(name)));
+ }
+
+ /**
+ * Test if the standard levels are transformed correctly.
+ */
+ @ParameterizedTest
+ @MethodSource("standardLevels")
+ void testStandardLevelConversion(final Level log4j1Level, final org.apache.logging.log4j.Level log4j2Level) {
+ assertThat(log4j2Level).isSameAs(OptionConverter.convertLevel(log4j1Level));
+ assertThat(log4j1Level).isSameAs(OptionConverter.convertLevel(log4j2Level));
+ assertThat(OptionConverter.toLevel(org.apache.logging.log4j.Level.class.getName(), log4j2Level.name(), null))
+ .isSameAs(OptionConverter.convertLevel(log4j2Level));
+ }
+
+ /**
+ * Test if the conversion works at an integer level.
+ */
+ @ParameterizedTest
+ @MethodSource("standardLevels")
+ void testStandardIntLevelConversion(final Level log4j1Level, final org.apache.logging.log4j.Level log4j2Level) {
+ assertEquals(log4j2Level.intLevel(), toLog4j2Level(log4j1Level.toInt()));
+ assertEquals(log4j1Level.toInt(), toLog4j1Level(log4j2Level.intLevel()));
+ }
+
+ @Test
+ void testMaxMinCutoff() {
+ // The cutoff values are transformed into ALL and OFF
+ assertEquals(StandardLevel.ALL.intLevel(), toLog4j2Level(OptionConverter.MIN_CUTOFF_LEVEL));
+ assertEquals(StandardLevel.OFF.intLevel(), toLog4j2Level(OptionConverter.MAX_CUTOFF_LEVEL));
+ // Maximal and minimal Log4j 1.x values different from ALL or OFF
+ int minTransformed = toLog4j1Level(toLog4j2Level(OptionConverter.MIN_CUTOFF_LEVEL + 1));
+ assertEquals(OptionConverter.MIN_CUTOFF_LEVEL + 1, minTransformed);
+ int maxTransformed = toLog4j1Level(toLog4j2Level(OptionConverter.MAX_CUTOFF_LEVEL - 1));
+ assertEquals(OptionConverter.MAX_CUTOFF_LEVEL - 1, maxTransformed);
+ // Maximal and minimal Log4j 2.x value different from ALL or OFF
+ minTransformed = toLog4j2Level(toLog4j1Level(StandardLevel.OFF.intLevel() + 1));
+ assertEquals(StandardLevel.OFF.intLevel() + 1, minTransformed);
+ maxTransformed = toLog4j2Level(toLog4j1Level(StandardLevel.ALL.intLevel() - 1));
+ assertEquals(StandardLevel.ALL.intLevel() - 1, maxTransformed);
+ }
+
+ /**
+ * Test if the values in at least the TRACE to FATAL range are transformed
+ * correctly.
+ */
+ @Test
+ void testUsefulRange() {
+ for (int intLevel = StandardLevel.OFF.intLevel(); intLevel <= StandardLevel.TRACE.intLevel(); intLevel++) {
+ assertEquals(intLevel, toLog4j2Level(toLog4j1Level(intLevel)));
+ }
+ for (int intLevel = Level.TRACE_INT; intLevel < OptionConverter.MAX_CUTOFF_LEVEL; intLevel = intLevel + 100) {
+ assertEquals(intLevel, toLog4j1Level(toLog4j2Level(intLevel)));
+ }
+ }
+
+ /**
+ * Levels defined in Log4j 2.x should have an equivalent in Log4j 1.x. Those are
+ * used in {@link LogEventAdapter}.
+ */
+ @Test
+ void testCustomLog4j2Levels() {
+ final int infoDebug = (StandardLevel.INFO.intLevel() + StandardLevel.DEBUG.intLevel()) / 2;
+ final org.apache.logging.log4j.Level v2Level = org.apache.logging.log4j.Level.forName("INFO_DEBUG", infoDebug);
+ final Level v1Level =
+ OptionConverter.toLevel("INFO_DEBUG#" + org.apache.logging.log4j.Level.class.getName(), null);
+ assertNotNull(v1Level);
+ assertEquals(v2Level, v1Level.getVersion2Level());
+ final int expectedLevel = (Priority.INFO_INT + Priority.DEBUG_INT) / 2;
+ assertEquals(expectedLevel, v1Level.toInt());
+ // convertLevel
+ assertEquals(v1Level, OptionConverter.convertLevel(v2Level));
+ // Non-existent level
+ assertNull(OptionConverter.toLevel("WARN_INFO#" + org.apache.logging.log4j.Level.class.getName(), null));
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
new file mode 100644
index 00000000000..2e41de6b142
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.log4j.helpers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.stream.Stream;
+import org.apache.log4j.Level;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Unit tests for UtilLoggingLevel.
+ */
+public class UtilLoggingLevelTest {
+
+ /**
+ * Test toLevel("fiNeSt").
+ */
+ @Test
+ public void testToLevelFINEST() {
+ assertEquals(UtilLoggingLevel.FINEST, UtilLoggingLevel.toLevel("fiNeSt"));
+ }
+
+ static Stream namesAndLevels() {
+ return UtilLoggingLevel.getAllPossibleLevels().stream()
+ .map(level -> Arguments.of(level.toString() + "#" + UtilLoggingLevel.class.getName(), level));
+ }
+
+ @ParameterizedTest
+ @MethodSource("namesAndLevels")
+ void testOptionConverterToLevel(final String name, final UtilLoggingLevel level) {
+ assertEquals(level, OptionConverter.toLevel(name, Level.ALL), "get v1 level by name");
+ // Comparison of Log4j 2.x levels
+ assertEquals(level.getVersion2Level(), org.apache.logging.log4j.Level.getLevel(name), "get v2 level by name");
+ // Test convertLevel
+ assertEquals(level, OptionConverter.convertLevel(level.getVersion2Level()), "convert level v2 -> v1");
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1SyslogLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1SyslogLayoutTest.java
new file mode 100644
index 00000000000..e684c3a5b94
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1SyslogLayoutTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.log4j.layout;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.StringLayout;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.time.MutableInstant;
+import org.apache.logging.log4j.core.util.NetUtils;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class Log4j1SyslogLayoutTest {
+
+ private static final SimpleMessage MESSAGE = new SimpleMessage("Hello world!");
+ private static final long TIMESTAMP = LocalDateTime.of(2022, 4, 5, 12, 34, 56)
+ .atZone(ZoneId.systemDefault())
+ .toEpochSecond();
+ private static final String localhostName = NetUtils.getLocalHostname();
+
+ private static LogEvent createLogEvent() {
+ final MutableInstant instant = new MutableInstant();
+ instant.initFromEpochSecond(TIMESTAMP, 0);
+ final LogEvent event = mock(LogEvent.class);
+ when(event.getInstant()).thenReturn(instant);
+ when(event.getMessage()).thenReturn(MESSAGE);
+ when(event.getLevel()).thenReturn(Level.INFO);
+ return event;
+ }
+
+ static Stream configurations() {
+ return Stream.of(
+ Arguments.of("<30>Hello world!", Facility.DAEMON, false, false),
+ Arguments.of("<30>Apr 5 12:34:56 %s Hello world!", Facility.DAEMON, true, false),
+ Arguments.of("<30>daemon:Hello world!", Facility.DAEMON, false, true),
+ Arguments.of("<30>Apr 5 12:34:56 %s daemon:Hello world!", Facility.DAEMON, true, true))
+ .map(args -> {
+ final Object[] objs = args.get();
+ objs[0] = String.format((String) objs[0], localhostName);
+ return Arguments.of(objs);
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("configurations")
+ void testSimpleLayout(
+ final String expected, final Facility facility, final boolean header, final boolean facilityPrinting) {
+ final LogEvent logEvent = createLogEvent();
+ StringLayout appenderLayout = Log4j1SyslogLayout.newBuilder()
+ .setFacility(facility)
+ .setHeader(header)
+ .setFacilityPrinting(facilityPrinting)
+ .build();
+ assertEquals(expected, appenderLayout.toSerializable(logEvent));
+ final StringLayout messageLayout =
+ PatternLayout.newBuilder().setPattern("%m").build();
+ appenderLayout = Log4j1SyslogLayout.newBuilder()
+ .setFacility(facility)
+ .setHeader(header)
+ .setFacilityPrinting(facilityPrinting)
+ .setMessageLayout(messageLayout)
+ .build();
+ assertEquals(expected, appenderLayout.toSerializable(logEvent));
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
index 395cf3b3e98..1af12167a60 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/layout/Log4j1XmlLayoutTest.java
@@ -1,34 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.layout;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.test.junit.UsingThreadContextStack;
import org.apache.logging.log4j.util.StringMap;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-public class Log4j1XmlLayoutTest {
+@UsingThreadContextStack
+class Log4j1XmlLayoutTest {
@Test
- public void testWithoutThrown() {
+ void testWithoutThrown() {
final Log4j1XmlLayout layout = Log4j1XmlLayout.createLayout(false, true);
final Log4jLogEvent event = Log4jLogEvent.newBuilder()
@@ -40,16 +42,16 @@ public void testWithoutThrown() {
final String result = layout.toSerializable(event);
- final String expected =
- "\r\n" +
- " \r\n" +
- " \r\n\r\n";
+ final String expected = "\r\n"
+ + " \r\n"
+ + " \r\n\r\n";
assertEquals(expected, result);
}
@Test
- public void testWithPropertiesAndLocationInfo() {
+ void testWithPropertiesAndLocationInfo() {
final Log4j1XmlLayout layout = Log4j1XmlLayout.createLayout(true, true);
final StringMap contextMap = ContextDataFactory.createContextData(2);
@@ -67,17 +69,16 @@ public void testWithPropertiesAndLocationInfo() {
final String result = layout.toSerializable(event);
- final String expected =
- "\r\n" +
- " \r\n" +
- "\r\n" +
- "\r\n" +
- "\r\n" +
- "\r\n" +
- " \r\n"+
- " \r\n\r\n";
+ final String expected = "\r\n"
+ + " \r\n"
+ + "\r\n"
+ + "\r\n"
+ + "\r\n"
+ + "\r\n"
+ + " \r\n"
+ + " \r\n\r\n";
assertEquals(expected, result);
}
-
}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
new file mode 100644
index 00000000000..291224674ea
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.log4j.pattern;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for FormattingInfo.
+ *
+ * @author Curt Arnold
+ *
+ */
+public class FormattingInfoTest extends TestCase {
+ /**
+ * Create a new instance.
+ *
+ * @param name test name
+ */
+ public FormattingInfoTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Check constructor
+ *
+ */
+ public void testConstructor() {
+ final FormattingInfo field = new FormattingInfo(true, 3, 6);
+ assertNotNull(field);
+ assertEquals(3, field.getMinLength());
+ assertEquals(6, field.getMaxLength());
+ assertTrue(field.isLeftAligned());
+ }
+
+ /**
+ * Check that getDefault does not return null.
+ *
+ */
+ public void testGetDefault() {
+ final FormattingInfo field = FormattingInfo.getDefault();
+ assertNotNull(field);
+ assertEquals(0, field.getMinLength());
+ assertEquals(Integer.MAX_VALUE, field.getMaxLength());
+ assertFalse(field.isLeftAligned());
+ }
+
+ /**
+ * Add padding to left since field is not minimum width.
+ */
+ public void testPadLeft() {
+ final StringBuffer buf = new StringBuffer("foobar");
+ final FormattingInfo field = new FormattingInfo(false, 5, 10);
+ field.format(2, buf);
+ assertEquals("fo obar", buf.toString());
+ }
+
+ /**
+ * Add padding to right since field is not minimum width.
+ */
+ public void testPadRight() {
+ final StringBuffer buf = new StringBuffer("foobar");
+ final FormattingInfo field = new FormattingInfo(true, 5, 10);
+ field.format(2, buf);
+ assertEquals("foobar ", buf.toString());
+ }
+
+ /**
+ * Field exceeds maximum width
+ */
+ public void testTruncate() {
+ final StringBuffer buf = new StringBuffer("foobar");
+ final FormattingInfo field = new FormattingInfo(true, 0, 3);
+ field.format(2, buf);
+ assertEquals("fobar", buf.toString());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java
new file mode 100644
index 00000000000..d6bcbb50fe2
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1LevelPatternConverterTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.log4j.pattern;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.log4j.Level;
+import org.apache.logging.log4j.core.LogEvent;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+class Log4j1LevelPatternConverterTest {
+
+ /**
+ * Tests if the converter returns the Log4j 1.x {@code toString()} value of
+ * custom Log4j 1.x levels.
+ *
+ * @param level a Log4j 1.x level
+ */
+ @ParameterizedTest
+ @MethodSource("org.apache.log4j.helpers.UtilLoggingLevel#getAllPossibleLevels")
+ void testUtilLoggingLevels(final Level level) {
+ final Log4j1LevelPatternConverter converter = Log4j1LevelPatternConverter.newInstance(null);
+ final LogEvent logEvent = mock(LogEvent.class);
+ when(logEvent.getLevel()).thenReturn(level.getVersion2Level());
+ final StringBuilder result = new StringBuilder();
+ converter.format(logEvent, result);
+ assertEquals(level.toString(), result.toString());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1MdcPatternConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1MdcPatternConverterTest.java
index c1d5b83254b..7b791c1442d 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1MdcPatternConverterTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1MdcPatternConverterTest.java
@@ -1,22 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.pattern;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.LogEvent;
@@ -24,19 +24,19 @@
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.util.StringMap;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-public class Log4j1MdcPatternConverterTest {
+class Log4j1MdcPatternConverterTest {
@Test
- public void testConverter0() {
+ void testConverter0() {
final StringMap contextMap = ContextDataFactory.createContextData(0);
final String expected = "{}";
test(contextMap, expected, null);
}
@Test
- public void testConverter1() {
+ void testConverter1() {
final StringMap contextMap = ContextDataFactory.createContextData(1);
contextMap.putValue("key1", "value1");
final String expected = "{{key1,value1}}";
@@ -44,7 +44,7 @@ public void testConverter1() {
}
@Test
- public void testConverter2() {
+ void testConverter2() {
final StringMap contextMap = ContextDataFactory.createContextData(2);
contextMap.putValue("key1", "value1");
contextMap.putValue("key2", "value2");
@@ -53,7 +53,7 @@ public void testConverter2() {
}
@Test
- public void testConverterWithKey() {
+ void testConverterWithKey() {
final StringMap contextMap = ContextDataFactory.createContextData(2);
contextMap.putValue("key1", "value1");
contextMap.putValue("key2", "value2");
@@ -73,6 +73,4 @@ private void test(final StringMap contextMap, final String expected, final Strin
converter.format(event, sb);
assertEquals(expected, sb.toString());
}
-
}
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java
index 2f0b80f78ab..a0b3da20d40 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/Log4j1NdcPatternConverterTest.java
@@ -1,57 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.log4j.pattern;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
-import org.apache.logging.log4j.junit.ThreadContextStackRule;
import org.apache.logging.log4j.message.SimpleMessage;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class Log4j1NdcPatternConverterTest {
+import org.apache.logging.log4j.test.junit.UsingThreadContextStack;
+import org.junit.jupiter.api.Test;
- @Rule
- public final ThreadContextStackRule threadContextRule = new ThreadContextStackRule();
+@UsingThreadContextStack
+class Log4j1NdcPatternConverterTest {
@Test
- public void testEmpty() {
+ void testEmpty() {
testConverter("");
}
@Test
- public void test1() {
+ void test1() {
ThreadContext.push("foo");
testConverter("foo");
}
@Test
- public void test2() {
+ void test2() {
ThreadContext.push("foo");
ThreadContext.push("bar");
testConverter("foo bar");
}
@Test
- public void test3() {
+ void test3() {
ThreadContext.push("foo");
ThreadContext.push("bar");
ThreadContext.push("baz");
@@ -69,6 +66,4 @@ private void testConverter(final String expected) {
converter.format(event, sb);
assertEquals(expected, sb.toString());
}
-
}
-
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
new file mode 100644
index 00000000000..06f2e9698c2
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
@@ -0,0 +1,326 @@
+/*
+ * 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.log4j.pattern;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for NameAbbrevator.
+ *
+ */
+public class NameAbbreviatorTest extends TestCase {
+ /**
+ * Create a new instance.
+ *
+ * @param name test name
+ */
+ public NameAbbreviatorTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Check that getAbbreviator(" ") returns default abbreviator.
+ *
+ */
+ public void testBlank() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator(" ");
+ final NameAbbreviator defaultAbbrev = NameAbbreviator.getDefaultAbbreviator();
+ assertSame(abbrev, defaultAbbrev);
+ }
+
+ /**
+ * Check that blanks are trimmed in evaluating abbreviation pattern.
+ */
+ public void testBlankOne() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator(" 1 ");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+ }
+
+ /**
+ * Check that getDefaultAbbreviator does not return null.
+ *
+ */
+ public void testGetDefault() {
+ final NameAbbreviator abbrev = NameAbbreviator.getDefaultAbbreviator();
+ assertNotNull(abbrev);
+ }
+
+ /**
+ * Check that getAbbreviator("-1").abbreviate() drops first name element.
+ *
+ */
+ public void testMinusOne() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("-1");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - example.foo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("1.*.2").abbreviate drops all but the first character from the first element, uses all of
+ * the second element and drops all but the first two characters of the rest of the non-final elements.
+ *
+ */
+ public void testMulti() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1.*.2");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.example.fo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("org.example.foo.");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.example.fo.", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - f.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - .", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("1").abbreviate() drops all but the final name element.
+ *
+ */
+ public void testOne() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("1.").abbreviate abbreviates non-final elements to one character.
+ *
+ */
+ public void testOneDot() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1.");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.e.f.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("org.example.foo.");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.e.f.", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - f.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - .", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("1~.").abbreviate abbreviates non-final elements to one character and a tilde.
+ *
+ */
+ public void testOneTildeDot() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("1~.");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o~.e~.f~.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("org.example.foo.");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o~.e~.f~.", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - f~.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append(".");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - .", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("o.e.f.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - o.e.f.bar", buf.toString());
+ }
+
+ /**
+ * Check that getAbbreviator("2").abbreviate drops all but the last two elements.
+ *
+ */
+ public void testTwo() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("2");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - foo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - foo.bar", buf.toString());
+
+ buf.setLength(0);
+ buf.append("DEBUG - ");
+ fieldStart = buf.length();
+ buf.append("bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - bar", buf.toString());
+ }
+
+ /**
+ * Check that "0" drops all name content.
+ *
+ */
+ public void testZero() {
+ final NameAbbreviator abbrev = NameAbbreviator.getAbbreviator("0");
+ final StringBuffer buf = new StringBuffer("DEBUG - ");
+ final int fieldStart = buf.length();
+ buf.append("org.example.foo.bar");
+ abbrev.abbreviate(fieldStart, buf);
+ assertEquals("DEBUG - ", buf.toString());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
new file mode 100644
index 00000000000..0679424cc63
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.log4j.spi;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for LocationInfo.
+ */
+public class LocationInfoTest extends TestCase {
+
+ /**
+ * Tests four parameter constructor.
+ */
+ public void testFourParamConstructor() {
+ final String className = LocationInfoTest.class.getName();
+ final String methodName = "testFourParamConstructor";
+ final String fileName = "LocationInfoTest.java";
+ final String lineNumber = "41";
+ final LocationInfo li = new LocationInfo(fileName, className, methodName, lineNumber);
+ assertEquals(className, li.getClassName());
+ assertEquals(methodName, li.getMethodName());
+ assertEquals(fileName, li.getFileName());
+ assertEquals(lineNumber, li.getLineNumber());
+ assertEquals(className + "." + methodName + "(" + fileName + ":" + lineNumber + ")", li.fullInfo);
+ }
+
+ /**
+ * Class with name that is a substring of its caller.
+ */
+ private static class NameSubstring {
+ /**
+ * Construct a LocationInfo. Location should be immediate caller of this method.
+ *
+ * @return location info.
+ */
+ public static LocationInfo getInfo() {
+ return new LocationInfo(new Throwable(), NameSubstring.class.getName());
+ }
+ }
+
+ /**
+ * Class whose name is contains the name of the class that obtains the LocationInfo.
+ */
+ private static class NameSubstringCaller {
+ /**
+ * Construct a locationInfo. Location should be this location.
+ *
+ * @return location info.
+ */
+ public static LocationInfo getInfo() {
+ return NameSubstring.getInfo();
+ }
+ }
+
+ /**
+ * Tests creation of location info when the logger class name is a substring of one of the other classes in the stack
+ * trace. See bug 44888.
+ */
+ public void testLocationInfo() {
+ final LocationInfo li = NameSubstringCaller.getInfo();
+ assertEquals(NameSubstringCaller.class.getName(), li.getClassName());
+ assertEquals("getInfo", li.getMethodName());
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
new file mode 100644
index 00000000000..bd8549c0a2d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
@@ -0,0 +1,329 @@
+/*
+ * 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.log4j.spi;
+
+import java.io.PrintWriter;
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link ThrowableInformation}.
+ */
+public class ThrowableInformationTest extends TestCase {
+
+ /**
+ * Create ThrowableInformationTest.
+ *
+ * @param name test name.
+ */
+ public ThrowableInformationTest(final String name) {
+ super(name);
+ }
+
+ /**
+ * Custom throwable that only calls methods overridden by VectorWriter in log4j 1.2.14 and earlier.
+ */
+ private static final class OverriddenThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public OverriddenThrowable() {}
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print((Object) "print(Object)");
+ s.print("print(char[])".toCharArray());
+ s.print("print(String)");
+ s.println((Object) "println(Object)");
+ s.println("println(char[])".toCharArray());
+ s.println("println(String)");
+ s.write("write(char[])".toCharArray());
+ s.write("write(char[], int, int)".toCharArray(), 2, 8);
+ s.write("write(String, int, int)", 2, 8);
+ }
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that only uses the PrintWriter methods overridden in log4j 1.2.14 and
+ * earlier.
+ */
+ public void testOverriddenBehavior() {
+ final ThrowableInformation ti = new ThrowableInformation(new OverriddenThrowable());
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(4, rep.length);
+ assertEquals("print(Object)print(char[])print(String)println(Object)", rep[0]);
+ assertEquals("println(char[])", rep[1]);
+ assertEquals("println(String)", rep[2]);
+ assertEquals("write(char[])ite(charite(Stri", rep[3]);
+ }
+
+ /**
+ * Custom throwable that calls methods not overridden by VectorWriter in log4j 1.2.14 and earlier.
+ */
+ private static final class NotOverriddenThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public NotOverriddenThrowable() {}
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print(true);
+ s.print('a');
+ s.print(1);
+ s.print(2L);
+ s.print(Float.MAX_VALUE);
+ s.print(Double.MIN_VALUE);
+ s.println(true);
+ s.println('a');
+ s.println(1);
+ s.println(2L);
+ s.println(Float.MAX_VALUE);
+ s.println(Double.MIN_VALUE);
+ s.write('C');
+ }
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that uses the PrintWriter methods not overridden in log4j 1.2.14 and
+ * earlier.
+ */
+ public void testNotOverriddenBehavior() {
+ final ThrowableInformation ti = new ThrowableInformation(new NotOverriddenThrowable());
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(7, rep.length);
+ final StringBuffer buf = new StringBuffer(String.valueOf(true));
+ buf.append('a');
+ buf.append(1);
+ buf.append(2L);
+ buf.append(Float.MAX_VALUE);
+ buf.append(Double.MIN_VALUE);
+ buf.append(true);
+ assertEquals(buf.toString(), rep[0]);
+ assertEquals("a", rep[1]);
+ assertEquals(String.valueOf(1), rep[2]);
+ assertEquals(String.valueOf(2L), rep[3]);
+ assertEquals(String.valueOf(Float.MAX_VALUE), rep[4]);
+ assertEquals(String.valueOf(Double.MIN_VALUE), rep[5]);
+ assertEquals("C", rep[6]);
+ }
+
+ /**
+ * Custom throwable that calls methods of VectorWriter with null.
+ */
+ private static final class NullThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public NullThrowable() {}
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print((Object) null);
+ s.print((String) null);
+ s.println((Object) null);
+ s.println((String) null);
+ }
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that passes null to PrintWriter methods.
+ */
+ public void testNull() {
+ final ThrowableInformation ti = new ThrowableInformation(new NullThrowable());
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(2, rep.length);
+ final String nullStr = String.valueOf((Object) null);
+ assertEquals(nullStr + nullStr + nullStr, rep[0]);
+ assertEquals(nullStr, rep[1]);
+ }
+
+ /**
+ * Custom throwable that does nothing in printStackTrace.
+ */
+ private static final class EmptyThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public EmptyThrowable() {}
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {}
+ }
+
+ /**
+ * Test capturing stack trace from a throwable that does nothing on a call to printStackTrace.
+ */
+ public void testEmpty() {
+ final ThrowableInformation ti = new ThrowableInformation(new EmptyThrowable());
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(0, rep.length);
+ }
+
+ /**
+ * Custom throwable that emits a specified string in printStackTrace.
+ */
+ private static final class StringThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+ /**
+ * Stack trace.
+ */
+ private final String stackTrace;
+
+ /**
+ * Create new instance.
+ *
+ * @param trace stack trace.
+ */
+ public StringThrowable(final String trace) {
+ stackTrace = trace;
+ }
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print(stackTrace);
+ }
+ }
+
+ /**
+ * Test capturing stack trace from throwable that just has a line feed.
+ */
+ public void testLineFeed() {
+ final ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n"));
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(1, rep.length);
+ assertEquals("", rep[0]);
+ }
+
+ /**
+ * Test capturing stack trace from throwable that just has a carriage return.
+ */
+ public void testCarriageReturn() {
+ final ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\r"));
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(1, rep.length);
+ assertEquals("", rep[0]);
+ }
+
+ /**
+ * Test parsing of line breaks.
+ */
+ public void testParsing() {
+ final ThrowableInformation ti =
+ new ThrowableInformation(new StringThrowable("Line1\rLine2\nLine3\r\nLine4\n\rLine6"));
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(6, rep.length);
+ assertEquals("Line1", rep[0]);
+ assertEquals("Line2", rep[1]);
+ assertEquals("Line3", rep[2]);
+ assertEquals("Line4", rep[3]);
+ assertEquals("", rep[4]);
+ assertEquals("Line6", rep[5]);
+ }
+
+ /**
+ * Test capturing stack trace from throwable that a line feed followed by blank.
+ */
+ public void testLineFeedBlank() {
+ final ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n "));
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals(2, rep.length);
+ assertEquals("", rep[0]);
+ assertEquals(" ", rep[1]);
+ }
+
+ /**
+ * Test that getThrowable returns the throwable provided to the constructor.
+ */
+ public void testGetThrowable() {
+ final Throwable t = new StringThrowable("Hello, World");
+ final ThrowableInformation ti = new ThrowableInformation(t);
+ assertSame(t, ti.getThrowable());
+ }
+
+ /**
+ * Tests isolation of returned string representation from internal state of ThrowableInformation. log4j 1.2.15 and
+ * earlier did not isolate initial call. See bug 44032.
+ */
+ public void testIsolation() {
+ final ThrowableInformation ti = new ThrowableInformation(new StringThrowable("Hello, World"));
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals("Hello, World", rep[0]);
+ rep[0] = "Bonjour, Monde";
+ final String[] rep2 = ti.getThrowableStrRep();
+ assertEquals("Hello, World", rep2[0]);
+ }
+
+ /**
+ * Custom throwable that throws a runtime exception when printStackTrace is called.
+ */
+ private static final class NastyThrowable extends Throwable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create new instance.
+ */
+ public NastyThrowable() {}
+
+ /**
+ * Print stack trace.
+ *
+ * @param s print writer.
+ */
+ public void printStackTrace(final PrintWriter s) {
+ s.print("NastyException");
+ throw new RuntimeException("Intentional exception");
+ }
+ }
+
+ /**
+ * Tests that a failure in printStackTrace does not percolate out of getThrowableStrRep().
+ *
+ */
+ public void testNastyException() {
+ final ThrowableInformation ti = new ThrowableInformation(new NastyThrowable());
+ final String[] rep = ti.getThrowableStrRep();
+ assertEquals("NastyException", rep[0]);
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
index 057646370e3..e58fa6436e2 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
@@ -2,7 +2,7 @@
* 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 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
*
@@ -14,11 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.log4j.util;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -27,68 +26,23 @@
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
-
import org.apache.commons.io.FileUtils;
-
/**
* Utiities for serialization tests.
*/
-public class SerializationTestHelper {
- /**
- * Private constructor.
- */
- private SerializationTestHelper() {
- }
-
+public final class SerializationTestHelper {
/**
- * Creates a clone by serializing object and
- * deserializing byte stream.
+ * Checks the serialization of an object against an file containing the expected serialization.
*
- * @param obj object to serialize and deserialize.
- * @return clone
- * @throws IOException on IO error.
- * @throws ClassNotFoundException if class not found.
- */
- public static Object serializeClone(final Object obj)
- throws IOException, ClassNotFoundException {
- final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
- try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
- objOut.writeObject(obj);
- }
-
- final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
- final ObjectInputStream objIs = new ObjectInputStream(src);
-
- return objIs.readObject();
- }
-
- /**
- * Deserializes a specified file.
- *
- * @param witness serialization file, may not be null.
- * @return deserialized object.
- * @throws Exception thrown on IO or deserialization exception.
- */
- public static Object deserializeStream(final String witness) throws Exception {
- try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
- return objIs.readObject();
- }
- }
-
- /**
- * Checks the serialization of an object against an file
- * containing the expected serialization.
- *
- * @param witness name of file containing expected serialization.
- * @param obj object to be serialized.
- * @param skip positions in serialized stream that should not be compared.
+ * @param witness name of file containing expected serialization.
+ * @param obj object to be serialized.
+ * @param skip positions in serialized stream that should not be compared.
* @param endCompare position to stop comparison.
* @throws Exception thrown on IO or serialization exception.
*/
public static void assertSerializationEquals(
- final String witness, final Object obj, final int[] skip,
- final int endCompare) throws Exception {
+ final String witness, final Object obj, final int[] skip, final int endCompare) throws Exception {
final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
objOut.writeObject(obj);
@@ -100,15 +54,14 @@ public static void assertSerializationEquals(
/**
* Asserts the serialized form of an object.
*
- * @param witness file name of expected serialization.
- * @param actual byte array of actual serialization.
- * @param skip positions to skip comparison.
+ * @param witness file name of expected serialization.
+ * @param actual byte array of actual serialization.
+ * @param skip positions to skip comparison.
* @param endCompare position to stop comparison.
* @throws IOException thrown on IO or serialization exception.
*/
public static void assertStreamEquals(
- final String witness, final byte[] actual, final int[] skip,
- final int endCompare) throws IOException {
+ final String witness, final byte[] actual, final int[] skip, final int endCompare) throws IOException {
final File witnessFile = new File(witness);
if (witnessFile.exists()) {
@@ -129,20 +82,54 @@ public static void assertStreamEquals(
for (int i = 0; i < endScan; i++) {
if ((skipIndex < skip.length) && (skip[skipIndex] == i)) {
skipIndex++;
- } else {
- if (expected[i] != actual[i]) {
- assertEquals(
- "Difference at offset " + i, expected[i], actual[i]);
- }
+ } else if (expected[i] != actual[i]) {
+ assertEquals(expected[i], actual[i], "Difference at offset " + i);
}
}
} else {
//
- // if the file doesn't exist then
- // assume that we are setting up and need to write it
+ // if the file doesn't exist then
+ // assume that we are setting up and need to write it
FileUtils.writeByteArrayToFile(witnessFile, actual);
fail("Writing witness file " + witness);
}
}
-}
+ /**
+ * Deserializes a specified file.
+ *
+ * @param witness serialization file, may not be null.
+ * @return deserialized object.
+ * @throws Exception thrown on IO or deserialization exception.
+ */
+ public static Object deserializeStream(final String witness) throws Exception {
+ try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
+ return objIs.readObject();
+ }
+ }
+
+ /**
+ * Creates a clone by serializing object and deserializing byte stream.
+ *
+ * @param obj object to serialize and deserialize.
+ * @return clone
+ * @throws IOException on IO error.
+ * @throws ClassNotFoundException if class not found.
+ */
+ public static Object serializeClone(final Object obj) throws IOException, ClassNotFoundException {
+ final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
+ try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
+ objOut.writeObject(obj);
+ }
+
+ final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
+ final ObjectInputStream objIs = new ObjectInputStream(src);
+
+ return objIs.readObject();
+ }
+
+ /**
+ * Private constructor.
+ */
+ private SerializationTestHelper() {}
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
new file mode 100644
index 00000000000..695d32179ff
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
@@ -0,0 +1,170 @@
+/*
+ * 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.log4j.xml;
+
+import static java.util.Objects.requireNonNull;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.VectorAppender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@SetTestProperty(key = "log4j1.compatibility", value = "true")
+class DOMTestCase {
+
+ Logger root;
+
+ Logger logger;
+
+ @BeforeEach
+ void setUp() {
+ root = Logger.getRootLogger();
+ logger = Logger.getLogger(DOMTestCase.class);
+ }
+
+ @AfterEach
+ void tearDown() {
+ root.getLoggerRepository().resetConfiguration();
+ }
+
+ /**
+ * Test checks that configureAndWatch does initial configuration, see bug 33502.
+ *
+ * @throws Exception if IO error.
+ */
+ @Test
+ void testConfigureAndWatch() throws Exception {
+ final URL url = requireNonNull(DOMTestCase.class.getResource("/DOMTestCase/DOMTestCase1.xml"));
+ DOMConfigurator.configureAndWatch(Paths.get(url.toURI()).toString());
+ assertNotNull(Logger.getRootLogger().getAppender("A1"));
+ }
+
+ /**
+ * Test for bug 47465. configure(URL) did not close opened JarURLConnection.
+ *
+ * @throws IOException if IOException creating properties jar.
+ */
+ @Test
+ void testJarURL() throws Exception {
+ final URL url = requireNonNull(DOMTestCase.class.getResource("/DOMTestCase/defaultInit.xml"));
+ final File input = Paths.get(url.toURI()).toFile();
+ System.out.println(input.getAbsolutePath());
+ final File configJar = new File("target/output/xml.jar");
+ final File dir = new File("target/output");
+ Files.createDirectories(dir.toPath());
+ try (final InputStream inputStream = Files.newInputStream(input.toPath());
+ final FileOutputStream out = new FileOutputStream(configJar);
+ final ZipOutputStream zos = new ZipOutputStream(out)) {
+ zos.putNextEntry(new ZipEntry("log4j.xml"));
+ int len;
+ final byte[] buf = new byte[1024];
+ while ((len = inputStream.read(buf)) > 0) {
+ zos.write(buf, 0, len);
+ }
+ zos.closeEntry();
+ }
+ final URL urlInJar = new URL("jar:" + configJar.toURI() + "!/log4j.xml");
+ DOMConfigurator.configure(urlInJar);
+ assertTrue(configJar.delete());
+ assertFalse(configJar.exists());
+ }
+
+ /**
+ * This test checks that the subst method of an extending class is checked when evaluating parameters. See bug 43325.
+ *
+ */
+ @Test
+ void testOverrideSubst() {
+ final DOMConfigurator configurator = new DOMConfigurator();
+ configurator.doConfigure(
+ DOMTestCase.class.getResource("/DOMTestCase/DOMTestCase1.xml"), LogManager.getLoggerRepository());
+ final String name = "A1";
+ final Appender appender = Logger.getRootLogger().getAppender(name);
+ assertNotNull(appender, name);
+ final AppenderWrapper wrapper = (AppenderWrapper) appender;
+ assertNotNull(wrapper, name);
+ final org.apache.logging.log4j.core.appender.FileAppender a1 =
+ (org.apache.logging.log4j.core.appender.FileAppender) wrapper.getAppender();
+ assertNotNull(a1, wrapper.toString());
+ final String file = a1.getFileName();
+ assertNotNull(file, a1.toString());
+ }
+
+ /**
+ * Tests that reset="true" on log4j:configuration element resets repository before configuration.
+ *
+ */
+ @Test
+ void testReset() {
+ final VectorAppender appender = new VectorAppender();
+ appender.setName("V1");
+ Logger.getRootLogger().addAppender(appender);
+ DOMConfigurator.configure(DOMTestCase.class.getResource("/DOMTestCase/testReset.xml"));
+ assertNull(Logger.getRootLogger().getAppender("V1"));
+ }
+
+ /**
+ * Test of log4j.throwableRenderer support. See bug 45721.
+ */
+ @Test
+ @UsingStatusListener
+ void testThrowableRenderer(ListStatusListener listener) {
+ DOMConfigurator.configure(DOMTestCase.class.getResource("/DOMTestCase/testThrowableRenderer.xml"));
+ assertThat(listener.findStatusData(org.apache.logging.log4j.Level.WARN))
+ .anySatisfy(status -> assertThat(status.getMessage().getFormattedMessage())
+ .contains("Log4j 1 throwable renderers are not supported"));
+ }
+
+ @Test
+ @SetTestProperty(key = "log4j1.compatibility", value = "false")
+ void when_compatibility_disabled_configurator_is_no_op() {
+ final Logger rootLogger = Logger.getRootLogger();
+ final Logger logger = Logger.getLogger("org.apache.log4j.xml");
+ assertThat(logger.getLevel()).isNull();
+ final URL configURL = DOMTestCase.class.getResource("/DOMTestCase/DOMTestCase1.xml");
+ DOMConfigurator.configure(configURL);
+
+ assertThat(rootLogger.getAppender("A1")).isNull();
+ assertThat(rootLogger.getAppender("A2")).isNull();
+ assertThat(rootLogger.getLevel()).isNotEqualTo(Level.TRACE);
+
+ assertThat(logger.getAppender("A1")).isNull();
+ assertThat(logger.getLevel()).isNull();
+ }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
new file mode 100644
index 00000000000..58e9d8afb73
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j.xml;
+
+import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
+
+import org.apache.log4j.Level;
+
+/**
+ * This class introduces a new level called TRACE. TRACE has lower level than DEBUG.
+ */
+public class XLevel extends Level {
+ private static final long serialVersionUID = 7288304330257085144L;
+
+ public static final int TRACE_INT = Level.DEBUG_INT - 1;
+ public static final int LETHAL_INT = Level.FATAL_INT + 1;
+
+ private static final String TRACE_STR = "TRACE";
+ private static final String LETHAL_STR = "LETHAL";
+
+ public static final XLevel TRACE = new XLevel(TRACE_INT, TRACE_STR, 7);
+ public static final XLevel LETHAL = new XLevel(LETHAL_INT, LETHAL_STR, 0);
+
+ public static Level toLevel(final int i) throws IllegalArgumentException {
+ switch (i) {
+ case TRACE_INT:
+ return XLevel.TRACE;
+ case LETHAL_INT:
+ return XLevel.LETHAL;
+ }
+ return Level.toLevel(i);
+ }
+
+ /**
+ * Convert the string passed as argument to a level. If the conversion fails, then this method returns {@link #TRACE}.
+ */
+ public static Level toLevel(final String sArg) {
+ return toLevel(sArg, XLevel.TRACE);
+ }
+
+ public static Level toLevel(final String sArg, final Level defaultValue) {
+
+ if (sArg == null) {
+ return defaultValue;
+ }
+ final String stringVal = toRootUpperCase(sArg);
+
+ if (stringVal.equals(TRACE_STR)) {
+ return XLevel.TRACE;
+ } else if (stringVal.equals(LETHAL_STR)) {
+ return XLevel.LETHAL;
+ }
+
+ return Level.toLevel(sArg, defaultValue);
+ }
+
+ protected XLevel(final int level, final String strLevel, final int syslogEquiv) {
+ super(level, strLevel, syslogEquiv);
+ }
+}
diff --git a/log4j-1.2-api/src/test/resources/DOMTestCase/DOMTestCase1.xml b/log4j-1.2-api/src/test/resources/DOMTestCase/DOMTestCase1.xml
new file mode 100644
index 00000000000..d637afad3b9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/DOMTestCase/DOMTestCase1.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/DOMTestCase/defaultInit.xml b/log4j-1.2-api/src/test/resources/DOMTestCase/defaultInit.xml
new file mode 100644
index 00000000000..868e66ae8de
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/DOMTestCase/defaultInit.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/DOMTestCase/testReset.xml b/log4j-1.2-api/src/test/resources/DOMTestCase/testReset.xml
new file mode 100644
index 00000000000..b0e565008e0
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/DOMTestCase/testReset.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/DOMTestCase/testThrowableRenderer.xml b/log4j-1.2-api/src/test/resources/DOMTestCase/testThrowableRenderer.xml
new file mode 100644
index 00000000000..50813462379
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/DOMTestCase/testThrowableRenderer.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/L7D_en_US.properties b/log4j-1.2-api/src/test/resources/L7D_en_US.properties
index c3c2802d525..0121fbe931c 100644
--- a/log4j-1.2-api/src/test/resources/L7D_en_US.properties
+++ b/log4j-1.2-api/src/test/resources/L7D_en_US.properties
@@ -1,7 +1,8 @@
+#
# 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 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
#
@@ -12,6 +13,8 @@
# 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.
+#
+
test=This is the English, US test.
hello_world=Hello world.
msg1=This is test number {0} with string argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/L7D_fr.properties b/log4j-1.2-api/src/test/resources/L7D_fr.properties
index 25b878aebc6..1c68863f210 100644
--- a/log4j-1.2-api/src/test/resources/L7D_fr.properties
+++ b/log4j-1.2-api/src/test/resources/L7D_fr.properties
@@ -1,7 +1,8 @@
+#
# 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 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
#
@@ -12,6 +13,8 @@
# 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.
+#
+
test=Ceci est le test en francais pour la France.
hello_world=Bonjour la France.
msg1=Ceci est le test numero {0} contenant l''argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties b/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties
index ba9b1ffbfc6..a6af2511118 100644
--- a/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties
+++ b/log4j-1.2-api/src/test/resources/L7D_fr_CH.properties
@@ -1,7 +1,8 @@
+#
# 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 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
#
@@ -12,5 +13,7 @@
# 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.
+#
+
test=Ceci est le test en francais pour la p'tite Suisse.
hello world=Salut le monde.
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3247.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3247.properties
new file mode 100644
index 00000000000..9593823b24b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3247.properties
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.filter.1=org.apache.log4j.config.NeutralFilterFixture
+log4j.appender.CONSOLE.filter.1.onMatch=neutral
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=target/temp.A1
+log4j.appender.A1.Append=false
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-5p %c{2} - %m%n
+log4j.appender.A2=org.apache.log4j.FileAppender
+log4j.appender.A2.File=target/temp.A2
+log4j.appender.A2.Append=false
+log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+log4j.appender.A2.layout.DateFormat=ISO8601
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, CONSOLE, A1, A2
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3281.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3281.properties
new file mode 100644
index 00000000000..78d3af5a423
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3281.properties
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+log4j.appender.CUSTOM=org.apache.log4j.CustomNoopAppender
+log4j.appender.CUSTOM.filter.1=org.apache.log4j.config.NeutralFilterFixture
+log4j.appender.CUSTOM.filter.1.onMatch=neutral
+log4j.appender.CUSTOM.Target=System.out
+log4j.appender.CUSTOM.layout=org.apache.log4j.PatternLayout
+log4j.appender.CUSTOM.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+log4j.logger.org.apache.log4j.xml=trace, CUSTOM
+log4j.rootLogger=trace, CUSTOM
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
new file mode 100644
index 00000000000..9fc31300b5f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3326.properties
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+log4j.appender.CUSTOM=org.apache.log4j.ListAppender
+log4j.appender.CUSTOM.filter.1=org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.CUSTOM.filter.1.levelMin=ALL
+log4j.appender.CUSTOM.filter.2=org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.CUSTOM.filter.2.levelMin=INFO
+log4j.appender.CUSTOM.filter.2.levelMax=ERROR
+log4j.appender.CUSTOM.filter.3=org.apache.log4j.varia.LevelRangeFilter
+
+log4j.rootLogger=trace, CUSTOM
diff --git a/log4j-1.2-api/src/test/resources/LOG4J2-3407.properties b/log4j-1.2-api/src/test/resources/LOG4J2-3407.properties
new file mode 100644
index 00000000000..b0634222d3c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/LOG4J2-3407.properties
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.filter.1=org.apache.log4j.config.NeutralFilterFixture
+log4j.appender.CONSOLE.filter.1.onMatch=neutral
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, CONSOLE
diff --git a/log4j-1.2-api/src/test/resources/PropertyConfiguratorTest/badEscape.properties b/log4j-1.2-api/src/test/resources/PropertyConfiguratorTest/badEscape.properties
new file mode 100644
index 00000000000..14f70635008
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/PropertyConfiguratorTest/badEscape.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+##
+# This file is intentionally broken
+log4j.rootLogger=\uXX41
diff --git a/log4j-1.2-api/src/test/resources/PropertyConfiguratorTest/filter.properties b/log4j-1.2-api/src/test/resources/PropertyConfiguratorTest/filter.properties
new file mode 100644
index 00000000000..0a65181c088
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/PropertyConfiguratorTest/filter.properties
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+log4j.appender.ROLLING=org.apache.log4j.PropertyConfiguratorTest$RollingFileAppender
+log4j.appender.ROLLING.append=false
+log4j.appender.ROLLING.rollingPolicy=org.apache.log4j.PropertyConfiguratorTest$FixedWindowRollingPolicy
+log4j.appender.ROLLING.rollingPolicy.activeFileName=filterBase-test1.log
+log4j.appender.ROLLING.rollingPolicy.fileNamePattern=filterBased-test1.%i
+log4j.appender.ROLLING.rollingPolicy.minIndex=0
+log4j.appender.ROLLING.triggeringPolicy=org.apache.log4j.PropertyConfiguratorTest$FilterBasedTriggeringPolicy
+log4j.appender.ROLLING.triggeringPolicy.filter=org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.ROLLING.triggeringPolicy.filter.levelMin=info
+log4j.appender.ROLLING.layout=org.apache.log4j.PatternLayout
+log4j.appender.ROLLING.layout.ConversionPattern=%m%n
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%m%n
+log4j.logger.org.apache.log4j.PropertyConfiguratorTest=debug, ROLLING
+log4j.additivity.org.apache.log4j.rolling.FilterBasedRollingTest=false
+log4j.rootLogger=info, CONSOLE
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
index 5fa402026c2..1fbe7b51a3b 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-auth-examples/src/main/resources/log4j.properties
@@ -1,16 +1,20 @@
#
-# Licensed 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
+# 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
+# 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. See accompanying LICENSE file.
+# limitations under the License.
#
+
log4j.appender.test=org.apache.log4j.ConsoleAppender
log4j.appender.test.Target=System.out
log4j.appender.test.layout=org.apache.log4j.PatternLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
index b08514c1703..fc525df71b7 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/main/conf/log4j.properties
@@ -1,18 +1,19 @@
-# 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
+# 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.
+#
# Define some default values that can be overridden by system properties
hadoop.root.logger=INFO,console
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
index ced0687caad..a40de161bd2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-common/src/test/resources/log4j.properties
@@ -1,14 +1,20 @@
-# Licensed 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
+# 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
#
-# 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.
+# 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.
+#
+
# log4j configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
index b347d275e2f..d57e5846ee1 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-kms/src/test/resources/log4j.properties
@@ -1,11 +1,10 @@
#
-# 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
+# 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
#
@@ -28,4 +27,4 @@ log4j.logger.org.apache.hadoop.crytpo.key.kms.server=ALL
log4j.logger.com.sun.jersey.server.wadl.generators.WadlGeneratorJAXBGrammarGenerator=OFF
log4j.logger.org.apache.hadoop.security=OFF
log4j.logger.org.apache.directory.server.core=OFF
-log4j.logger.org.apache.hadoop.util.NativeCodeLoader=OFF
\ No newline at end of file
+log4j.logger.org.apache.hadoop.util.NativeCodeLoader=OFF
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
index 9efd671a087..29f00dda73e 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-minikdc/src/main/resources/log4j.properties
@@ -1,11 +1,10 @@
#
-# 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
+# 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
#
@@ -28,4 +27,4 @@ log4j.rootLogger=INFO, stdout
log4j.logger.org.apache.directory=OFF
log4j.logger.org.apache.directory.server.kerberos=INFO, stdout
log4j.additivity.org.apache.directory=false
-log4j.logger.net.sf.ehcache=INFO, stdout
\ No newline at end of file
+log4j.logger.net.sf.ehcache=INFO, stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
index ced0687caad..a40de161bd2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-common-project/hadoop-nfs/src/test/resources/log4j.properties
@@ -1,14 +1,20 @@
-# Licensed 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
+# 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
#
-# 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.
+# 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.
+#
+
# log4j configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
index 73788467112..6c2fa4cb336 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs-client/src/test/resources/log4j.properties
@@ -1,19 +1,20 @@
#
-# 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
+# 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
+# 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.
+# 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.
#
+
# log4j configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
index 52aac432644..58db1c8842d 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/contrib/bkjournal/src/test/resources/log4j.properties
@@ -1,22 +1,18 @@
#
-#
-# 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.
-#
+# 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.
#
#
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
index 73788467112..6c2fa4cb336 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/log4j.properties
@@ -1,19 +1,20 @@
#
-# 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
+# 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
+# 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.
+# 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.
#
+
# log4j configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-shuffle/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
index 1330ed1aef3..396985af1e5 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-aws/src/test/resources/log4j.properties
@@ -1,14 +1,20 @@
-# Licensed 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
+# 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
#
-# 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.
+# 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.
+#
+
# log4j configuration used during build and unit tests
log4j.rootLogger=info,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties
index 73ee3f9c6ce..3bd94a6e2c2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-azure/src/test/resources/log4j.properties
@@ -1,19 +1,20 @@
#
-# 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
+# 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
+# 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.
+# 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.
#
+
# log4j configuration used during build and unit tests
log4j.rootLogger=INFO,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
index 6aeb41dcdd0..83dd3f2fd9a 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-openstack/src/test/resources/log4j.properties
@@ -1,34 +1,20 @@
#
-# 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
+# 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
+# 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.
+# 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.
#
-# Licensed 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.
-# log4j configuration used during build and unit tests
-
log4j.rootLogger=INFO,stdout
log4j.threshold=ALL
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
index cfd405b16e7..3672426f7e6 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-tools/hadoop-sls/src/main/sample-conf/log4j.properties
@@ -1,16 +1,20 @@
#
-# Licensed 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
+# 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
+# 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. See accompanying LICENSE file.
+# limitations under the License.
#
+
log4j.appender.test=org.apache.log4j.ConsoleAppender
log4j.appender.test.Target=System.out
log4j.appender.test.layout=org.apache.log4j.PatternLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
index e46856e7209..898a262af83 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/test/resources/log4j.properties
@@ -1,33 +1,20 @@
#
-# 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
+# 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
+# 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.
+# 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.
#
-# Licensed 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.
-
# log4j configuration used during build and unit tests
log4j.rootLogger=INFO,stdout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
index bed1abcbd08..cd739b65be0 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/test/resources/log4j.properties
@@ -1,18 +1,19 @@
-# 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
@@ -60,4 +61,4 @@ log4j.logger.org.apache.curator.framework.imps=WARN
log4j.logger.org.apache.curator.framework.state.ConnectionStateManager=ERROR
log4j.logger.org.apache.directory.api.ldap=ERROR
-log4j.logger.org.apache.directory.server=ERROR
\ No newline at end of file
+log4j.logger.org.apache.directory.server=ERROR
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
index c088bb7fdff..898a262af83 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-tests/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-hbase-tests/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
index 81a3f6ad5d2..b7ca6914295 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/hadoop/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice/src/test/resources/log4j.properties
@@ -1,14 +1,19 @@
-# Licensed 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
+# 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.
#
-# 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.
# log4j configuration used during build and unit tests
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
index 123a51db0b3..d7d2c2527b7 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -13,10 +30,14 @@ log4j.rootLogger=TRACE, DRFA
#
log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.Append=false
+log4j.appender.DRFA.BufferedIO=true
+log4j.appender.DRFA.BufferSize=1000
log4j.appender.DRFA.File=${hadoop.log.dir}/${hadoop.log.file}
+log4j.appender.DRFA.ImmediateFlush=false
# Rollover at midnight
-log4j.appender.DRFA.DatePattern=.yyyy-MM-dd
+log4j.appender.DRFA.DatePattern=.dd-MM-yyyy
log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.xml
new file mode 100644
index 00000000000..a2a9f6e98c7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-DailyRollingFileAppender.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-EnhancedRollingFileAppender.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-EnhancedRollingFileAppender.properties
new file mode 100644
index 00000000000..a0ef860d21c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-EnhancedRollingFileAppender.properties
@@ -0,0 +1,65 @@
+#
+# 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.
+#
+
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, DEFAULT_TIME, DEFAULT_SIZE, TIME, SIZE
+
+log4j.appender.DEFAULT_TIME = org.apache.log4j.rolling.RollingFileAppender
+log4j.appender.DEFAULT_TIME.layout = org.apache.log4j.SimpleLayout
+log4j.appender.DEFAULT_TIME.File = target/EnhancedRollingFileAppender/defaultTime.log
+log4j.appender.DEFAULT_TIME.rollingPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy
+log4j.appender.DEFAULT_TIME.rollingPolicy.FileNamePattern = target/EnhancedRollingFileAppender/defaultTime.%d{yyyy-MM-dd}.log
+
+log4j.appender.DEFAULT_SIZE = org.apache.log4j.rolling.RollingFileAppender
+log4j.appender.DEFAULT_SIZE.File = target/EnhancedRollingFileAppender/defaultSize.log
+log4j.appender.DEFAULT_SIZE.layout = org.apache.log4j.SimpleLayout
+log4j.appender.DEFAULT_SIZE.triggeringPolicy = org.apache.log4j.rolling.SizeBasedTriggeringPolicy
+log4j.appender.DEFAULT_SIZE.rollingPolicy = org.apache.log4j.rolling.FixedWindowRollingPolicy
+log4j.appender.DEFAULT_SIZE.rollingPolicy.FileNamePattern = target/EnhancedRollingFileAppender/defaultSize.%i.log
+
+log4j.appender.TIME = org.apache.log4j.rolling.RollingFileAppender
+log4j.appender.TIME.Append = false
+log4j.appender.TIME.BufferedIO = true
+log4j.appender.TIME.BufferSize = 1000
+log4j.appender.TIME.File = target/EnhancedRollingFileAppender/ignoredTime.log
+log4j.appender.TIME.ImmediateFlush = false
+log4j.appender.TIME.layout = org.apache.log4j.SimpleLayout
+log4j.appender.TIME.triggeringPolicy = org.apache.log4j.rolling.TimeBasedRollingPolicy
+# It is explicitly not a TimeBasedRolling
+log4j.appender.TIME.rollingPolicy = org.apache.log4j.rolling.FixedWindowRollingPolicy
+log4j.appender.TIME.rollingPolicy.ActiveFileName = target/EnhancedRollingFileAppender/time.log
+log4j.appender.TIME.rollingPolicy.FileNamePattern = target/EnhancedRollingFileAppender/time.%d{yyyy-MM-dd}.log
+
+log4j.appender.SIZE = org.apache.log4j.rolling.RollingFileAppender
+log4j.appender.SIZE.Append = false
+log4j.appender.SIZE.BufferedIO = true
+log4j.appender.SIZE.BufferSize = 1000
+log4j.appender.SIZE.File = target/EnhancedRollingFileAppender/ignoredSize.log
+log4j.appender.SIZE.ImmediateFlush = false
+log4j.appender.SIZE.layout = org.apache.log4j.SimpleLayout
+log4j.appender.SIZE.FileName = target/EnhancedRollingFileAppender/size.log
+log4j.appender.SIZE.triggeringPolicy = org.apache.log4j.rolling.SizeBasedTriggeringPolicy
+log4j.appender.SIZE.triggeringPolicy.MaxFileSize = 10000000
+log4j.appender.SIZE.rollingPolicy = org.apache.log4j.rolling.FixedWindowRollingPolicy
+log4j.appender.SIZE.rollingPolicy.ActiveFileName = target/EnhancedRollingFileAppender/size.log
+log4j.appender.SIZE.rollingPolicy.FileNamePattern = target/EnhancedRollingFileAppender/size.%i.log
+log4j.appender.SIZE.rollingPolicy.MinIndex = 11
+log4j.appender.SIZE.rollingPolicy.MaxIndex = 20
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-EnhancedRollingFileAppender.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-EnhancedRollingFileAppender.xml
new file mode 100644
index 00000000000..a568dc21ca2
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-EnhancedRollingFileAppender.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-FileAppender-with-props.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-FileAppender-with-props.properties
new file mode 100644
index 00000000000..56de9df9711
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-FileAppender-with-props.properties
@@ -0,0 +1,41 @@
+#
+# 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.
+#
+
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+hadoop.log.file=hadoop.log
+
+log4j.rootLogger=TRACE, FILE_APPENDER
+
+#
+# Rolling File Appender
+#
+hadoop.log.maxfilesize=256MB
+hadoop.log.maxbackupindex=20
+log4j.appender.FILE_APPENDER=org.apache.log4j.FileAppender
+log4j.appender.FILE_APPENDER.Append=false
+log4j.appender.FILE_APPENDER.BufferedIO=true
+log4j.appender.FILE_APPENDER.BufferSize=1000
+log4j.appender.FILE_APPENDER.File=${log4j.test.tmpdir}/${hadoop.log.file}
+log4j.appender.FILE_APPENDER.ImmediateFlush=false
+log4j.appender.FILE_APPENDER.layout=org.apache.log4j.PatternLayout
+
+# Pattern format: Date LogLevel LoggerName LogMessage
+log4j.appender.FILE_APPENDER.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.properties
new file mode 100644
index 00000000000..b84788b417d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.appender.LIST = org.apache.log4j.ListAppender
+log4j.appender.LIST.filter.1 = org.apache.log4j.varia.LevelRangeFilter
+log4j.appender.LIST.filter.1.LevelMin = INFO
+log4j.appender.LIST.filter.1.LevelMax = ERROR
+log4j.appender.LIST.filter.1.AcceptOnMatch = false
+log4j.rootLogger = debug, LIST
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.xml
new file mode 100644
index 00000000000..ab4cdd7e5a4
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-LevelRangeFilter.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties
index d89a4f4eadf..bb3bc83654b 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.xml
new file mode 100644
index 00000000000..f030cb88371
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-NullAppender.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
index b664bb8ccd0..9b897aed81f 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender-with-props.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -14,8 +31,11 @@ log4j.rootLogger=TRACE, RFA
hadoop.log.maxfilesize=256MB
hadoop.log.maxbackupindex=20
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=${hadoop.log.dir}/${hadoop.log.file}
-
+log4j.appender.RFA.ImmediateFlush=false
log4j.appender.RFA.MaxFileSize=${hadoop.log.maxfilesize}
log4j.appender.RFA.MaxBackupIndex=${hadoop.log.maxbackupindex}
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
index 55234bab0ba..911ab683653 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -9,8 +26,11 @@ log4j.rootLogger=TRACE, RFA
# Rolling File Appender - cap space usage at 5gb.
#
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=target/hadoop.log
-
+log4j.appender.RFA.ImmediateFlush=false
log4j.appender.RFA.MaxFileSize=256MB
log4j.appender.RFA.MaxBackupIndex=20
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.xml
new file mode 100644
index 00000000000..2ef6d18cbdd
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-RollingFileAppender.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.properties
new file mode 100644
index 00000000000..c57d69690bf
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.properties
@@ -0,0 +1,44 @@
+#
+# 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.
+#
+
+###############################################################################
+#
+# Log4J 1.2 Configuration.
+#
+
+log4j.rootLogger=TRACE, ConsoleCapitalized, ConsoleJavaStyle
+
+##############################################################################
+#
+# The Console log
+#
+
+log4j.appender.ConsoleCapitalized=org.apache.log4j.ConsoleAppender
+log4j.appender.ConsoleCapitalized.Encoding=ISO-8859-1
+log4j.appender.ConsoleCapitalized.Follow=true
+log4j.appender.ConsoleCapitalized.ImmediateFlush=false
+log4j.appender.ConsoleCapitalized.Target=System.err
+log4j.appender.ConsoleCapitalized.layout=org.apache.log4j.PatternLayout
+log4j.appender.ConsoleCapitalized.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n
+
+log4j.appender.ConsoleJavaStyle=org.apache.log4j.ConsoleAppender
+log4j.appender.ConsoleJavaStyle.Encoding=ISO-8859-1
+log4j.appender.ConsoleJavaStyle.Follow=true
+log4j.appender.ConsoleJavaStyle.ImmediateFlush=false
+log4j.appender.ConsoleJavaStyle.Target=System.err
+log4j.appender.ConsoleJavaStyle.layout=org.apache.log4j.PatternLayout
+log4j.appender.ConsoleJavaStyle.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.xml
new file mode 100644
index 00000000000..272b79fc8d0
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-capitalization.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
index 6793eb26ec2..82bc2b82084 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p %X %x: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.xml
new file mode 100644
index 00000000000..65c64d7fac1
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-EnhancedPatternLayout.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
index 216a12ebf58..400c9e2172b 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.HTMLLayout
log4j.appender.Console.layout.Title=Headline
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.xml
new file mode 100644
index 00000000000..36aeeb3938e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-HtmlLayout.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
index 810a494eb6a..139d25bde2e 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d{ISO8601} [%t][%c] %-5p: %m%n
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.xml
new file mode 100644
index 00000000000..e1dd2a91c87
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-PatternLayout.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
index 5a8ac4eb9a0..fce9cd926a2 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.SimpleLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.xml
new file mode 100644
index 00000000000..7d498484322
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-SimpleLayout.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
index 80d38c2a53e..011cef594b3 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,9 +28,13 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.TTCCLayout
-log4j.appender.Console.layout.ThreadPrinting=true
+log4j.appender.Console.layout.ThreadPrinting=false
log4j.appender.Console.layout.CategoryPrefixing=false
+log4j.appender.Console.layout.ContextPrinting=false
+log4j.appender.Console.layout.DateFormat=ISO8601
+log4j.appender.Console.layout.TimeZone=CET
log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.xml
new file mode 100644
index 00000000000..574716eb5c9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-TTCCLayout.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
index c8190ecd8bd..bcf117148aa 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-console-XmlLayout.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,6 +28,7 @@ log4j.rootLogger=TRACE, Console
#
log4j.appender.Console=org.apache.log4j.ConsoleAppender
+log4j.appender.Console.Follow=true
log4j.appender.Console.Target=System.err
log4j.appender.Console.layout=org.apache.log4j.xml.XMLLayout
log4j.appender.Console.layout.LocationInfo=true
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.properties
new file mode 100644
index 00000000000..b8815d325dd
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.properties
@@ -0,0 +1,82 @@
+#
+# 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.
+#
+
+##############################################################################
+#
+# Configuration file with minimal number of non-default values
+#
+log4j.rootLogger = TRACE, HTMLLayout, PatternLayout, TTCCLayout, XMLLayout,\
+ConsoleAppender, DailyRollingFileAppender, FileAppender, RollingFileAppender
+
+##############################################################################
+#
+# HTMLLayout
+#
+log4j.appender.HTMLLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.HTMLLayout.layout=org.apache.log4j.HTMLLayout
+
+##############################################################################
+#
+# PatternLayout
+#
+log4j.appender.PatternLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.PatternLayout.layout=org.apache.log4j.PatternLayout
+
+##############################################################################
+#
+# TTCCLayout
+#
+log4j.appender.TTCCLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.TTCCLayout.layout=org.apache.log4j.TTCCLayout
+
+##############################################################################
+#
+# XMLLayout
+#
+log4j.appender.XMLLayout=org.apache.log4j.ConsoleAppender
+log4j.appender.XMLLayout.layout=org.apache.log4j.xml.XMLLayout
+
+##############################################################################
+#
+# ConsoleAppender
+#
+log4j.appender.ConsoleAppender=org.apache.log4j.ConsoleAppender
+log4j.appender.ConsoleAppender.layout=org.apache.log4j.SimpleLayout
+
+##############################################################################
+#
+# DailyRollingFileAppender
+#
+log4j.appender.DailyRollingFileAppender=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DailyRollingFileAppender.File=target/dailyRollingFileAppender
+log4j.appender.DailyRollingFileAppender.layout=org.apache.log4j.SimpleLayout
+
+##############################################################################
+#
+# FileAppender
+#
+log4j.appender.FileAppender=org.apache.log4j.FileAppender
+log4j.appender.FileAppender.File=target/fileAppender
+log4j.appender.FileAppender.layout=org.apache.log4j.SimpleLayout
+
+##############################################################################
+#
+# RollingFileAppender
+#
+log4j.appender.RollingFileAppender=org.apache.log4j.RollingFileAppender
+log4j.appender.RollingFileAppender.File=target/rollingFileAppender
+log4j.appender.RollingFileAppender.layout=org.apache.log4j.SimpleLayout
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.xml
new file mode 100644
index 00000000000..2def3f6d6a3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-defaultValues.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
index 4d3ec0d6af3..5cf42b5b637 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,7 +28,11 @@ log4j.rootLogger=TRACE, File
#
log4j.appender.File=org.apache.log4j.FileAppender
+log4j.appender.File.Append=false
+log4j.appender.File.BufferedIO=true
+log4j.appender.File.BufferSize=1000
log4j.appender.File.File=target/mylog.txt
+log4j.appender.File.ImmediateFlush=false
log4j.appender.File.layout=org.apache.log4j.SimpleLayout
log4j.logger.com.example.foo = DEBUG
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.xml
new file mode 100644
index 00000000000..90b3f3095ff
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-file-SimpleLayout.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.properties
new file mode 100644
index 00000000000..3171c4066c6
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+log4j.threshold = info
+log4j.appender.LIST = org.apache.log4j.ListAppender
+log4j.rootLogger = debug, LIST
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.xml
new file mode 100644
index 00000000000..82c48a0dfb7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-global-threshold.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties
index a82c4c37e9b..4aebc427819 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -11,4 +28,10 @@ log4j.rootLogger=TRACE, RFA
# Rolling File Appender
#
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=${java.io.tmpdir}/${hadoop.log.file}
+log4j.appender.RFA.ImmediateFlush=false
+log4j.appender.RFA.MaxBackupIndex=16
+log4j.appender.RFA.MaxFileSize=20 MB
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.xml b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.xml
new file mode 100644
index 00000000000..2ed5554337e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-1.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties
index 9228434054e..f635f194276 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-system-properties-2.properties
@@ -1,3 +1,20 @@
+#
+# 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.
+#
+
###############################################################################
#
# Log4J 1.2 Configuration.
@@ -12,4 +29,10 @@ log4j.rootLogger=TRACE, RFA
# Rolling File Appender
#
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Append=false
+log4j.appender.RFA.BufferedIO=true
+log4j.appender.RFA.BufferSize=1000
log4j.appender.RFA.File=${hadoop.log.dir}/${hadoop.log.file}
+log4j.appender.RFA.ImmediateFlush=false
+log4j.appender.RFA.MaxBackupIndex=16
+log4j.appender.RFA.MaxBackupSize=20 MB
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/log4j-untrimmed.properties b/log4j-1.2-api/src/test/resources/config-1.2/log4j-untrimmed.properties
new file mode 100644
index 00000000000..116859ae741
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/config-1.2/log4j-untrimmed.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+###
+# Warning: this file contains INTENTIONAL trailing spaces on all properties
+#
+
+log4j.threshold = INFO
+
+log4j.appender.Console = org.apache.log4j.ConsoleAppender
+log4j.appender.Console.layout = org.apache.log4j.SimpleLayout
+log4j.appender.Console.filter.1 = org.apache.log4j.varia.DenyAllFilter
+
+log4j.rootLogger = DEBUG , Console
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties
index cce8d9152d3..6365f63b8bd 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/R/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
index e8da774f7ca..e9feeb64f04 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-common/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties
index e73978908b6..e8abecea806 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/common/network-shuffle/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
index fb9d9851cb4..d919270cf2b 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/core/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
index 1e3f163f95c..bad5156b0f1 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume-sink/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
@@ -25,4 +25,3 @@ log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{
# Ignore messages below warning level from Jetty, because it's a bit verbose
log4j.logger.org.spark_project.jetty=WARN
-
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
index fd51f8faf56..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/flume/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
@@ -25,4 +25,3 @@ log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{
# Ignore messages below warning level from Jetty, because it's a bit verbose
log4j.logger.org.spark_project.jetty=WARN
-
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
index 3706a6e3613..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/java8-tests/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
index 75e3b53a093..49618ae7b54 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-10/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
@@ -25,4 +25,3 @@ log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{
# Ignore messages below warning level from Jetty, because it's a bit verbose
log4j.logger.org.spark-project.jetty=WARN
-
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
index fd51f8faf56..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kafka-0-8/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
@@ -25,4 +25,3 @@ log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{
# Ignore messages below warning level from Jetty, because it's a bit verbose
log4j.logger.org.spark_project.jetty=WARN
-
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
index 4f5ea7bafe4..798457fae65 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/main/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
index 3706a6e3613..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/external/kinesis-asl/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
index 3706a6e3613..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/graphx/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
index 744c456cb29..3e7f668307e 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/launcher/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
index fd51f8faf56..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/mllib/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
@@ -25,4 +25,3 @@ log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{
# Ignore messages below warning level from Jetty, because it's a bit verbose
log4j.logger.org.spark_project.jetty=WARN
-
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
index 7665bd5e7c0..2472d7dd173 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/repl/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
index 3706a6e3613..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/catalyst/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
index 33b9ecf1e28..c216bec215d 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/core/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
index fea3404769d..4f31a6aca58 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/sql/hive/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
index fd51f8faf56..001d0482aca 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/streaming/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
@@ -25,4 +25,3 @@ log4j.appender.file.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss.SSS} %t %p %c{
# Ignore messages below warning level from Jetty, because it's a bit verbose
log4j.logger.org.spark_project.jetty=WARN
-
diff --git a/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties b/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
index d13454d5ae5..29ec90539be 100644
--- a/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
+++ b/log4j-1.2-api/src/test/resources/config-1.2/spark/yarn/src/test/resources/log4j.properties
@@ -2,11 +2,11 @@
# 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 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
+# 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,
diff --git a/log4j-1.2-api/src/test/resources/hello.vm b/log4j-1.2-api/src/test/resources/hello.vm
deleted file mode 100644
index 5ce97550285..00000000000
--- a/log4j-1.2-api/src/test/resources/hello.vm
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- #set( $foo = "Velocity" )
-Hello $foo World!
-
-
\ No newline at end of file
diff --git a/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml b/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml
index eb7e8a57ddf..7de6461c4a8 100644
--- a/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml
+++ b/log4j-1.2-api/src/test/resources/log-RouteWithMDC.xml
@@ -1,22 +1,21 @@
-
+
@@ -44,4 +43,4 @@
-
\ No newline at end of file
+
diff --git a/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties
new file mode 100644
index 00000000000..556a4c1bd69
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.properties
@@ -0,0 +1,69 @@
+#
+# 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.
+#
+
+log4j.appender.LIST=org.apache.log4j.ListAppender
+log4j.appender.LIST.Threshold=DEBUG
+log4j.appender.LIST.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.LIST.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.LIST.filter.2.LevelToMatch=INFO
+log4j.appender.LIST.filter.2.AcceptOnMatch=true
+log4j.appender.LIST.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.LIST2=org.apache.logging.log4j.test.appender.ListAppender
+log4j.appender.LIST2.Threshold=DEBUG
+log4j.appender.LIST2.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.LIST2.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.LIST2.filter.2.LevelToMatch=INFO
+log4j.appender.LIST2.filter.2.AcceptOnMatch=true
+log4j.appender.LIST2.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=DEBUG
+log4j.appender.CONSOLE.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.CONSOLE.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.CONSOLE.filter.2.LevelToMatch=INFO
+log4j.appender.CONSOLE.filter.2.AcceptOnMatch=true
+log4j.appender.CONSOLE.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.FILE=org.apache.log4j.FileAppender
+log4j.appender.FILE.Threshold=DEBUG
+log4j.appender.FILE.File=${test.tmpDir}/file-appender.log
+log4j.appender.FILE.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.FILE.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.FILE.filter.2.LevelToMatch=INFO
+log4j.appender.FILE.filter.2.AcceptOnMatch=true
+log4j.appender.FILE.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.Threshold=DEBUG
+log4j.appender.RFA.File=${test.tmpDir}/rolling-file-appender.log
+log4j.appender.RFA.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.RFA.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.RFA.filter.2.LevelToMatch=INFO
+log4j.appender.RFA.filter.2.AcceptOnMatch=true
+log4j.appender.RFA.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.DRFA.Threshold=DEBUG
+log4j.appender.DRFA.File=${test.tmpDir}/daily-rolling-file-appender.log
+log4j.appender.DRFA.filter.1=org.apache.log4j.config.StartsWithFilter
+log4j.appender.DRFA.filter.2=org.apache.log4j.varia.LevelMatchFilter
+log4j.appender.DRFA.filter.2.LevelToMatch=INFO
+log4j.appender.DRFA.filter.2.AcceptOnMatch=true
+log4j.appender.DRFA.filter.3=org.apache.log4j.varia.DenyAllFilter
+
+log4j.rootLogger=TRACE, LIST, LIST2, CONSOLE, FILE, RFA, DRFA
diff --git a/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml
new file mode 100644
index 00000000000..092d342f549
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j-multipleFilters.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j.xml b/log4j-1.2-api/src/test/resources/log4j.xml
new file mode 100644
index 00000000000..478a92e0311
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/RFA1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/RFA1.properties
new file mode 100644
index 00000000000..1a81ab8d13d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/RFA1.properties
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.RollingFileAppender
+log4j.appender.testAppender.file=output/RFA-test1.log
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%m\n
+log4j.appender.testAppender.maxFileSize=100
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/fallback1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/fallback1.properties
new file mode 100644
index 00000000000..fb4bfc8cc2c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/fallback1.properties
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+log4j.debug=true
+log4j.appender.PRIMARY=org.apache.log4j.FileAppender
+log4j.appender.PRIMARY.errorhandler=org.apache.log4j.varia.FallbackErrorHandler
+log4j.appender.PRIMARY.errorhandler.root-ref=true
+log4j.appender.PRIMARY.errorhandler.appender-ref=FALLBACK
+log4j.appender.PRIMARY.file=/xyz/:x.log
+log4j.appender.PRIMARY.append=false
+log4j.appender.PRIMARY.layout=org.apache.log4j.PatternLayout
+log4j.appender.PRIMARY.layout.conversionPattern=%-5p %c{2} - %m%n
+
+log4j.appender.FALLBACK=org.apache.log4j.FileAppender
+log4j.appender.FALLBACK.File=output/temp
+log4j.appender.FALLBACK.Append=false
+log4j.appender.FALLBACK.layout=org.apache.log4j.PatternLayout
+log4j.appender.FALLBACK.layout.ConversionPattern=FALLBACK - %c - %m%n
+
+log4j.rootLogger=DEBUG, PRIMARY
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold1.properties
new file mode 100644
index 00000000000..e9260a12c5e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold1.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=OFF
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold2.properties
new file mode 100644
index 00000000000..ff042bf71fa
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold2.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=FATAL
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold3.properties
new file mode 100644
index 00000000000..ce35952b4d7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold3.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=ERROR
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold4.properties
new file mode 100644
index 00000000000..e503cf83521
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold4.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=WARN
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold5.properties
new file mode 100644
index 00000000000..d870d80d18d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold5.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=INFO
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold6.properties
new file mode 100644
index 00000000000..a77dae331bf
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold6.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=DEBUG
+log4j.rootLogger=,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold7.properties
new file mode 100644
index 00000000000..8602ac3c11c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold7.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=TRACE#org.apache.log4j.xml.XLevel
+log4j.rootLogger=ALL,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold8.properties
new file mode 100644
index 00000000000..6408afd4512
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/hierarchyThreshold8.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.threshold=ALL
+log4j.rootLogger=ALL,A
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.File=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%p [%t] %c{2} = %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout.mdc.1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout.mdc.1.properties
new file mode 100644
index 00000000000..e228b49dbb0
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout.mdc.1.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m %X%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout1.properties
new file mode 100644
index 00000000000..3c2b837eb27
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout1.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.file=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout10.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout10.properties
new file mode 100644
index 00000000000..e8eef911699
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout10.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %l: %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout11.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout11.properties
new file mode 100644
index 00000000000..67bb7177627
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout11.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p [%t] %c{2}: %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout12.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout12.properties
new file mode 100644
index 00000000000..d1753a9d953
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout12.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C.%M(%F:%L): %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout13.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout13.properties
new file mode 100644
index 00000000000..6998d503b55
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout13.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C{3}.%M(%F:%L): %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout14.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout14.properties
new file mode 100644
index 00000000000..c7691decbe8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout14.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p [%t] %c{1.}: %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout15.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout15.properties
new file mode 100644
index 00000000000..e9b58b1678e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout15.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedMyPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%5p %-4# - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout16.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout16.properties
new file mode 100644
index 00000000000..327175c15b7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout16.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/patternLayout16.log
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}{GMT}Z %d{yyyy-MM-dd HH:mm:ss}{GMT-6}-0600 - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout2.properties
new file mode 100644
index 00000000000..a863c9d5379
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout2.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout3.properties
new file mode 100644
index 00000000000..093faca9f5a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout3.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout4.properties
new file mode 100644
index 00000000000..5d09b4aa5dc
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout4.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{DATE} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout5.properties
new file mode 100644
index 00000000000..5b0f3ceda41
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout5.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout6.properties
new file mode 100644
index 00000000000..cd4f7c5f864
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout6.properties
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ABSOLUTE} [%t] %-5p %.16c - %m%n
+
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout7.properties
new file mode 100644
index 00000000000..00b3f7131a2
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout7.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout8.properties
new file mode 100644
index 00000000000..67cde918108
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout8.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%r [%t] %-5p %.16c - %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout9.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout9.properties
new file mode 100644
index 00000000000..7ca09b8e4d1
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/pattern/enhancedPatternLayout9.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.EnhancedPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %.16c : %m%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout.mdc.1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout.mdc.1.properties
new file mode 100644
index 00000000000..8e9b136261e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout.mdc.1.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+log4j.rootCategory=DEBUG, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m %X%n
+
+# Prevent internal log4j DEBUG messages from polluting the output.
+log4j.logger.org.apache.log4j.PropertyConfigurator=INFO
+log4j.logger.org.apache.log4j.config.PropertySetter=INFO
+log4j.logger.org.apache.log4j.FileAppender=INFO
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout1.properties
new file mode 100644
index 00000000000..aed43bbf02a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout1.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout10.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout10.properties
new file mode 100644
index 00000000000..73bdba89c4e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout10.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File= output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %l: %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout11.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout11.properties
new file mode 100644
index 00000000000..8d985f8149f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout11.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%-5p [%t] %c{2}: %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout12.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout12.properties
new file mode 100644
index 00000000000..85c9c4b8365
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout12.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C.%M(%F:%L): %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout13.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout13.properties
new file mode 100644
index 00000000000..211a430e38d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout13.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File= output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %C{3}.%M(%F:%L): %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout14.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout14.properties
new file mode 100644
index 00000000000..495775ba424
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout14.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File= output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.MyPatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%5p %-4# - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout2.properties
new file mode 100644
index 00000000000..c6dc606e75e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout2.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append= false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout3.properties
new file mode 100644
index 00000000000..5a413b4e81f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout3.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout4.properties
new file mode 100644
index 00000000000..f03a494202a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout4.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{DATE} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout5.properties
new file mode 100644
index 00000000000..20176088407
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout5.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout6.properties
new file mode 100644
index 00000000000..aaa7c220838
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout6.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{ABSOLUTE} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout7.properties
new file mode 100644
index 00000000000..7c0c6f33b3a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout7.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout8.properties
new file mode 100644
index 00000000000..03272e5cf1f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout8.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=%r [%t] %-5p %.16c - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout9.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout9.properties
new file mode 100644
index 00000000000..b59c7a45bd9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/patternLayout9.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.rootCategory=TRACE, testAppender
+log4j.appender.testAppender=org.apache.log4j.FileAppender
+log4j.appender.testAppender.File=output/temp
+log4j.appender.testAppender.Append=false
+log4j.appender.testAppender.layout=org.apache.log4j.PatternLayout
+log4j.appender.testAppender.layout.ConversionPattern=[%t] %-5p %.16c : %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer1.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer1.properties
new file mode 100644
index 00000000000..e4fea1cf1b5
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer1.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x [%t] %c %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer2.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer2.properties
new file mode 100644
index 00000000000..9121f610039
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer2.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x [%t] %C (%F:%L) %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer3.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer3.properties
new file mode 100644
index 00000000000..3cbea3c040f
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer3.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x [%t] %C (%F:%L) %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer4.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer4.properties
new file mode 100644
index 00000000000..f5fb662484e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer4.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{key1}%X{key4} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer5.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer5.properties
new file mode 100644
index 00000000000..4640cd3016c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer5.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{key1}%X{key5} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer6.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer6.properties
new file mode 100644
index 00000000000..a5cb07d919a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer6.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{hostID} %X{key6} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer7.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer7.properties
new file mode 100644
index 00000000000..fd07c4ef21a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer7.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{hostID} %X{key7} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer8.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer8.properties
new file mode 100644
index 00000000000..87b9cd03406
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/socketServer8.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=TRACE, A
+log4j.Logger.org.apache.log4j.test.ShortSocketServer=WARN
+log4j.Logger.org.apache.log4j.net.SocketNode=WARN
+log4j.appender.A=org.apache.log4j.FileAppender
+log4j.appender.A.file=output/temp
+log4j.appender.A.Append=false
+log4j.appender.A.layout=org.apache.log4j.PatternLayout
+log4j.appender.A.layout.ConversionPattern=%5p %x %X{hostID} %X{key8} [%t] %c{1} - %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_en_US.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_en_US.properties
new file mode 100644
index 00000000000..0121fbe931c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_en_US.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+test=This is the English, US test.
+hello_world=Hello world.
+msg1=This is test number {0} with string argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr.properties
new file mode 100644
index 00000000000..1c68863f210
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+test=Ceci est le test en francais pour la France.
+hello_world=Bonjour la France.
+msg1=Ceci est le test numero {0} contenant l''argument {1}.
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr_CH.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr_CH.properties
new file mode 100644
index 00000000000..a6af2511118
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/L7D_fr_CH.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+test=Ceci est le test en francais pour la p'tite Suisse.
+hello world=Salut le monde.
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/TestLogSFPatterns.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/TestLogSFPatterns.properties
new file mode 100644
index 00000000000..3d251346fbd
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/TestLogSFPatterns.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+Iteration0=Iteration {}
+Hello1=Hello, World
+Malformed=Hello, {.
+Hello2=Hello, {}World
+Hello3=Hello, {}
+Hello4={}, {}.
+Hello5={}{} {}.
+Hello6={}{} {}{}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogMFPatterns.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogMFPatterns.properties
new file mode 100644
index 00000000000..f5962dab3b8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogMFPatterns.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+Iteration0=Iteration {0}
+Hello1=Hello, World
+Malformed=Hello, {.
+Hello2=Hello, {0}World
+Hello3=Hello, {0}
+Hello4={1}, {0}.
+Hello5={1}{2} {0}.
+Hello6={1}{2} {0}{3}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogSFPatterns.properties b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogSFPatterns.properties
new file mode 100644
index 00000000000..3d251346fbd
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/TestLogSFPatterns.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+Iteration0=Iteration {}
+Hello1=Hello, World
+Malformed=Hello, {.
+Hello2=Hello, {}World
+Hello3=Hello, {}
+Hello4={}, {}.
+Hello5={}{} {}.
+Hello6={}{} {}{}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.log b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.log
new file mode 100644
index 00000000000..3ce933f832d
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.log
@@ -0,0 +1,3 @@
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1: p2: Message 0
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hello p2:World {p1=Hello, p2=World, x1=Mundo}
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hello p2:World Message 1
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.xml
new file mode 100644
index 00000000000..503a548156b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/map.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.log b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.log
new file mode 100644
index 00000000000..9aa2c499137
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.log
@@ -0,0 +1,2 @@
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hello p2:World Message 0
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hola p2:World Message 1
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.xml
new file mode 100644
index 00000000000..62f872027e9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/property.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.log b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.log
new file mode 100644
index 00000000000..da0b52f2dc4
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.log
@@ -0,0 +1,3 @@
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1: p2: Message 0
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1: p2:Hello I am bean.
+INFO org.apache.log4j.rewrite.RewriteAppenderTest - p1:Hola p2:Hello Welcome to The Hub
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.xml
new file mode 100644
index 00000000000..7a96f185053
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/resources/org/apache/log4j/rewrite/reflection.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-1.properties b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-1.properties
new file mode 100644
index 00000000000..c0d481e9353
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-1.properties
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+#
+log4j.appender.A1=org.apache.log4j.CustomNoopAppender
+log4j.appender.A1.booleanA=true
+log4j.appender.A1.intA=1
+log4j.appender.A1.stringA=A
+#
+log4j.appender.A2=org.apache.log4j.CustomFileAppender
+log4j.appender.A2.booleanA=true
+log4j.appender.A2.intA=1
+log4j.appender.A2.stringA=A
+log4j.appender.A2.File=target/temp.A2
+log4j.appender.A2.Append=false
+log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+log4j.appender.A2.layout.DateFormat=ISO8601
+#
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, A1, A2
diff --git a/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-2.properties b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-2.properties
new file mode 100644
index 00000000000..f514396ff2a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-appenders-custom-2.properties
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+#
+log4j.appender.A1=org.apache.log4j.CustomNoopAppender
+log4j.appender.A1.booleanA=false
+log4j.appender.A1.intA=2
+log4j.appender.A1.stringA=B
+#
+log4j.appender.A2=org.apache.log4j.CustomFileAppender
+log4j.appender.A2.booleanA=false
+log4j.appender.A2.intA=2
+log4j.appender.A2.stringA=B
+log4j.appender.A2.File=target/temp.A2
+log4j.appender.A2.Append=false
+log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+log4j.appender.A2.layout.DateFormat=ISO8601
+#
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, A1, A2
diff --git a/log4j-1.2-api/src/test/resources/log4j1-async.properties b/log4j-1.2-api/src/test/resources/log4j1-async.properties
new file mode 100644
index 00000000000..60333593aa9
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.properties
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+
+log4j.appender.list=org.apache.log4j.ListAppender
+log4j.appender.list.layout=org.apache.log4j.PatternLayout
+log4j.appender.list.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.async=org.apache.log4j.AsyncAppender
+log4j.appender.async.appender-ref=list
+log4j.rootLogger=trace, async
diff --git a/log4j-1.2-api/src/test/resources/log4j1-async.xml b/log4j-1.2-api/src/test/resources/log4j1-async.xml
new file mode 100644
index 00000000000..2eee705c0dd
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-async.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file-1.properties b/log4j-1.2-api/src/test/resources/log4j1-file-1.properties
new file mode 100644
index 00000000000..a1a213b267b
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file-1.properties
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+#
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=target/temp.A1
+log4j.appender.A1.Append=false
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-5p %c{2} - %m%n
+#
+log4j.appender.A2=org.apache.log4j.FileAppender
+log4j.appender.A2.File=target/temp.A2
+log4j.appender.A2.Append=false
+log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+log4j.appender.A2.layout.DateFormat=ISO8601
+#
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, A1, A2
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file-2.properties b/log4j-1.2-api/src/test/resources/log4j1-file-2.properties
new file mode 100644
index 00000000000..5405fcddbdd
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file-2.properties
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Target=System.out
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+#
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=target/temp.A1
+log4j.appender.A1.Append=true
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-5p %c{2} - %m%n
+#
+log4j.appender.A2=org.apache.log4j.FileAppender
+log4j.appender.A2.File=target/temp.A2
+log4j.appender.A2.Append=true
+log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+log4j.appender.A2.layout.DateFormat=ISO8601
+#
+log4j.logger.org.apache.log4j.xml=trace, A1
+log4j.rootLogger=trace, A1, A2
diff --git a/log4j-1.2-api/src/test/resources/log4j1-file.xml b/log4j-1.2-api/src/test/resources/log4j1-file.xml
new file mode 100644
index 00000000000..cba42154bef
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-file.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-list.properties b/log4j-1.2-api/src/test/resources/log4j1-list.properties
new file mode 100644
index 00000000000..8860b6e050a
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-list.properties
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+log4j.appender.list=org.apache.log4j.ListAppender
+log4j.appender.list.layout=org.apache.log4j.PatternLayout
+log4j.appender.list.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+log4j.appender.events=org.apache.log4j.ListAppender
+log4j.rootLogger=trace, list, events
diff --git a/log4j-1.2-api/src/test/resources/log4j1-list.xml b/log4j-1.2-api/src/test/resources/log4j1-list.xml
new file mode 100644
index 00000000000..478a92e0311
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-list.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-mapRewrite.xml b/log4j-1.2-api/src/test/resources/log4j1-mapRewrite.xml
new file mode 100644
index 00000000000..fb4a1c799d3
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-mapRewrite.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml b/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml
new file mode 100644
index 00000000000..bd0ea8c7bad
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.properties b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.properties
new file mode 100644
index 00000000000..5d6dbde2d5e
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.properties
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+log4j.debug=true
+
+# Properties for substitution
+somelogfile=somefile.log
+maxfilesize=256MB
+maxbackupindex=20
+
+log4j.rootLogger=TRACE, RFA
+
+# Appender configuration with variables
+log4j.appender.RFA=org.apache.log4j.RollingFileAppender
+log4j.appender.RFA.File=${test.directory}/${somelogfile}
+log4j.appender.RFA.MaxFileSize=${maxfilesize}
+log4j.appender.RFA.MaxBackupIndex=${maxbackupindex}
+log4j.appender.RFA.layout=org.apache.log4j.PatternLayout
+log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %p %c: %m%n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.xml b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.xml
new file mode 100644
index 00000000000..33910d18e13
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rolling-properties.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties b/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties
new file mode 100644
index 00000000000..d72784d23dc
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket-xml-layout.properties
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG,socket
+log4j.appender.socket=org.apache.log4j.net.SocketAppender
+log4j.appender.socket.remoteHost=localhost
+log4j.appender.socket.port=9999
+log4j.appender.socket.reconnectionDelay=100
+log4j.appender.socket.layout=org.apache.log4j.xml.XMLLayout
+log4j.appender.socket.Threshold=DEBUG
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.properties b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
new file mode 100644
index 00000000000..151ca7ed5d2
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG,socket
+log4j.appender.socket=org.apache.log4j.net.SocketAppender
+log4j.appender.socket.remoteHost=localhost
+log4j.appender.socket.port=9999
+log4j.appender.socket.reconnectionDelay=100
+log4j.appender.socket.layout=org.apache.log4j.PatternLayout
+log4j.appender.socket.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
+log4j.appender.socket.Threshold=DEBUG
diff --git a/log4j-1.2-api/src/test/resources/log4j1-socket.xml b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
new file mode 100644
index 00000000000..c9c7fad9550
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-socket.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-default.properties b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-default.properties
new file mode 100644
index 00000000000..967644f2233
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-default.properties
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG,syslog
+log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
+log4j.appender.syslog.Threshold=DEBUG
+log4j.appender.syslog.syslogHost=localhost:${syslog.port}
+log4j.appender.syslog.header=true
+log4j.appender.syslog.Facility=LOCAL3
+log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
+log4j.appender.syslog.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.properties b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.properties
new file mode 100644
index 00000000000..be6a4887460
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.properties
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG,syslog
+log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
+log4j.appender.syslog.Threshold=DEBUG
+log4j.appender.syslog.syslogHost=localhost:${syslog.port}
+log4j.appender.syslog.protocol=TCP
+log4j.appender.syslog.header=true
+log4j.appender.syslog.Facility=LOCAL3
+log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
+log4j.appender.syslog.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.xml b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.xml
new file mode 100644
index 00000000000..9d065e2e15c
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-tcp.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.properties b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.properties
new file mode 100644
index 00000000000..3786306ed86
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.properties
@@ -0,0 +1,26 @@
+#
+# 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.
+#
+
+log4j.rootLogger=DEBUG,syslog
+log4j.appender.syslog=org.apache.log4j.net.SyslogAppender
+log4j.appender.syslog.Threshold=DEBUG
+log4j.appender.syslog.syslogHost=localhost:${syslog.port}
+log4j.appender.syslog.protocol=UDP
+log4j.appender.syslog.header=true
+log4j.appender.syslog.Facility=LOCAL3
+log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
+log4j.appender.syslog.layout.conversionPattern=Main[%pid] :%t: %c %-4p - %m\n
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.xml b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.xml
new file mode 100644
index 00000000000..c1defbdd5f7
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog-protocol-udp.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j1-syslog.xml b/log4j-1.2-api/src/test/resources/log4j1-syslog.xml
new file mode 100644
index 00000000000..46d8a0d2d71
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/log4j-1.2-api/src/test/resources/log4j2-config.xml b/log4j-1.2-api/src/test/resources/log4j2-config.xml
index 2427af82cbe..8b47ed485e9 100644
--- a/log4j-1.2-api/src/test/resources/log4j2-config.xml
+++ b/log4j-1.2-api/src/test/resources/log4j2-config.xml
@@ -1,22 +1,21 @@
-
+
@@ -36,4 +35,4 @@
-
\ No newline at end of file
+
diff --git a/log4j-1.2-api/src/test/resources/logWithMDC.xml b/log4j-1.2-api/src/test/resources/logWithMDC.xml
index 1fe848295fe..93ad097c59d 100644
--- a/log4j-1.2-api/src/test/resources/logWithMDC.xml
+++ b/log4j-1.2-api/src/test/resources/logWithMDC.xml
@@ -1,22 +1,21 @@
-
+
@@ -37,4 +36,4 @@
-
\ No newline at end of file
+
diff --git a/log4j-api-java9/.gitignore b/log4j-api-java9/.gitignore
new file mode 100644
index 00000000000..ae3c1726048
--- /dev/null
+++ b/log4j-api-java9/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/log4j-api-java9/pom.xml b/log4j-api-java9/pom.xml
index 183ad14dd3c..5112014f8ac 100644
--- a/log4j-api-java9/pom.xml
+++ b/log4j-api-java9/pom.xml
@@ -1,153 +1,104 @@
4.0.0
org.apache.logging.log4j
log4j
- 2.10.1-SNAPSHOT
- ../
+ ${revision}
+ ../log4j-parent
log4j-api-java9
pom
Apache Log4j API Java 9 support
The Apache Log4j API (Java 9)
- ${basedir}/..
- API Documentation
- /api
+ true
+ 9
- junit
- junit
+ org.assertj
+ assertj-core
test
- org.apache.maven
- maven-core
+ org.junit.jupiter
+ junit-jupiter-engine
test
- org.apache.maven.plugins
- maven-toolchains-plugin
- 1.1
+ maven-assembly-plugin
+ zip
- toolchain
+ single
+ package
+
+ log4j-api-java9-${project.version}
+ false
+
+ src/assembly/java9.xml
+
+
-
-
-
- 9
-
-
-
+
org.apache.maven.plugins
maven-compiler-plugin
default-compile
- compile
compile
default-test-compile
- test-compile
testCompile
+ test-compile
-
- 9
- 9
- 9
- none
-
+
org.apache.maven.plugins
maven-surefire-plugin
-
- 2.13
+
- test
- test
+ run-tests
test
-
-
- true
-
- 2C
- true
-
- **/Test*.java
- **/*Test.java
-
-
- **/*FuncTest.java
-
-
-
-
- maven-assembly-plugin
-
-
- zip
- package
-
- single
-
-
- log4j-api-java9-${project.version}
- false
-
- src/assembly/java9.xml
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-deploy-plugin
- ${deploy.plugin.version}
-
- true
-
-
+
diff --git a/log4j-api-java9/src/assembly/java9.xml b/log4j-api-java9/src/assembly/java9.xml
index 3e595612db4..0aa94e150fb 100644
--- a/log4j-api-java9/src/assembly/java9.xml
+++ b/log4j-api-java9/src/assembly/java9.xml
@@ -1,23 +1,20 @@
-
+
-
+ ~ 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.
+ -->
src
@@ -29,22 +26,10 @@
${project.build.outputDirectory}
/classes/META-INF/versions/9
- **/*.class
-
-
- module-info.class
- **/Dummy.class
- **/spi/Provider.class
- **/util/PropertySource.class
- **/message/ThreadDumpMessage.class
- **/message/ThreadDumpMessage$ThreadInfoFactory.class
-
-
-
- ${project.build.outputDirectory}
- /classes
-
- module-info.class
+ org/apache/logging/log4j/util/Base64Util.class
+ org/apache/logging/log4j/util/ProcessIdUtil.class
+ org/apache/logging/log4j/util/StackLocator.class
+ org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.class
diff --git a/log4j-api-java9/src/main/java/module-info.java b/log4j-api-java9/src/main/java/module-info.java
deleted file mode 100644
index 3cb22e02da6..00000000000
--- a/log4j-api-java9/src/main/java/module-info.java
+++ /dev/null
@@ -1,28 +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.
- */
-module org.apache.logging.log4j {
- exports org.apache.logging.log4j;
- exports org.apache.logging.log4j.message;
- exports org.apache.logging.log4j.simple;
- exports org.apache.logging.log4j.spi;
- exports org.apache.logging.log4j.status;
- exports org.apache.logging.log4j.util;
-
- uses org.apache.logging.log4j.spi.Provider;
- uses org.apache.logging.log4j.util.PropertySource;
- uses org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory;
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/Dummy.java
index 24012e6ae3a..a2b60d2fe25 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/Dummy.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/Dummy.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j;
@@ -20,5 +20,4 @@
* This is a dummy class and is only here to allow module-info.java to compile. It will not
* be copied into the log4j-api module.
*/
-public class Dummy {
-}
+public class Dummy {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/log4j/util/PropertySource.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/log4j/util/PropertySource.java
deleted file mode 100644
index f94d10a9b28..00000000000
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/log4j/util/PropertySource.java
+++ /dev/null
@@ -1,24 +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.util;
-
-/**
- * This is a dummy class and is only here to allow module-info.java to compile. It will not
- * be copied into the log4j-api module.
- */
-public class PropertySource {
-}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/Dummy.java
index 082e36ea3e7..343013dcd45 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/Dummy.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/Dummy.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.message;
@@ -20,5 +20,4 @@
* This is a dummy class and is only here to allow module-info.java to compile. It will not
* be copied into the log4j-api module.
*/
-public class Dummy {
-}
+public class Dummy {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
index 8b1af5dd565..2cef3a24ece 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/message/ThreadDumpMessage.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.message;
@@ -21,7 +21,5 @@
* be copied into the log4j-api module.
*/
public class ThreadDumpMessage {
- public static interface ThreadInfoFactory {
-
- }
+ public static interface ThreadInfoFactory {}
}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/simple/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/simple/Dummy.java
index c3a24e282bf..25f4cf4d22e 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/simple/Dummy.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/simple/Dummy.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.simple;
@@ -20,5 +20,4 @@
* This is a dummy class and is only here to allow module-info.java to compile. It will not
* be copied into the log4j-api module.
*/
-public class Dummy {
-}
+public class Dummy {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/spi/Provider.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/spi/Provider.java
index 65b86380e9e..2f6fbdf3149 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/spi/Provider.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/spi/Provider.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.spi;
@@ -20,5 +20,4 @@
* This is a dummy class and is only here to allow module-info.java to compile. It will not
* be copied into the log4j-api module.
*/
-public class Provider {
-}
+public class Provider {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/status/Dummy.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/status/Dummy.java
index d53dc43e753..0004af0bfb1 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/status/Dummy.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/status/Dummy.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.status;
@@ -20,5 +20,4 @@
* This is a dummy class and is only here to allow module-info.java to compile. It will not
* be copied into the log4j-api module.
*/
-public class Dummy {
-}
+public class Dummy {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/Base64Util.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/Base64Util.java
new file mode 100644
index 00000000000..ad66303df38
--- /dev/null
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/Base64Util.java
@@ -0,0 +1,35 @@
+/*
+ * 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.util;
+
+import java.nio.charset.Charset;
+import java.util.Base64;
+
+/**
+ * Base64 encodes Strings. This utility is only necessary because the mechanism to do this changed in Java 8 and
+ * the original method for Base64 encoding was removed in Java 9.
+ */
+public final class Base64Util {
+
+ private static final Base64.Encoder encoder = Base64.getEncoder();
+
+ private Base64Util() {}
+
+ public static String encode(final String str) {
+ return str != null ? encoder.encodeToString(str.getBytes(Charset.defaultCharset())) : null;
+ }
+}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
new file mode 100644
index 00000000000..bf78f35df57
--- /dev/null
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java
@@ -0,0 +1,23 @@
+/*
+ * 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.util;
+
+/**
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
+ */
+public class EnvironmentPropertySource implements PropertySource {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
new file mode 100644
index 00000000000..6eb2c710425
--- /dev/null
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/LoaderUtil.java
@@ -0,0 +1,28 @@
+/*
+ * 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.util;
+
+/**
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
+ */
+public final class LoaderUtil {
+
+ public static ClassLoader getThreadContextClassLoader() {
+ return LoaderUtil.class.getClassLoader();
+ }
+}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/PrivateSecurityManagerStackTraceUtil.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/PrivateSecurityManagerStackTraceUtil.java
new file mode 100644
index 00000000000..9fb9a4ee56b
--- /dev/null
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/PrivateSecurityManagerStackTraceUtil.java
@@ -0,0 +1,34 @@
+/*
+ * 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.util;
+
+import java.util.Deque;
+
+/**
+ * This is a dummy class and is only here to allow this module to compile. It will not
+ * be copied into the log4j-api module.
+ */
+final class PrivateSecurityManagerStackTraceUtil {
+
+ static boolean isEnabled() {
+ return false;
+ }
+
+ static Deque> getCurrentStackTrace() {
+ return null;
+ }
+}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ProcessIdUtil.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ProcessIdUtil.java
index e467413db25..ad6666d5977 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ProcessIdUtil.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ProcessIdUtil.java
@@ -1,18 +1,18 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.util;
@@ -23,7 +23,7 @@ public class ProcessIdUtil {
public static String getProcessId() {
try {
return Long.toString(ProcessHandle.current().pid());
- } catch(Exception ex) {
+ } catch (Exception ex) {
return DEFAULT_PROCESSID;
}
}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/PropertySource.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/PropertySource.java
new file mode 100644
index 00000000000..56e6259b5d8
--- /dev/null
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/PropertySource.java
@@ -0,0 +1,23 @@
+/*
+ * 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.util;
+
+/**
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
+ */
+public interface PropertySource {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java
index b0661d0fe14..e928777ed6b 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/StackLocator.java
@@ -1,82 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.util;
-import java.util.List;
-import java.util.Stack;
-import java.util.stream.Collectors;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.function.Predicate;
/**
* Consider this class private. Determines the caller's class.
*/
-public class StackLocator {
+public final class StackLocator {
- private final static StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+ private static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
- private final static StackWalker stackWalker = StackWalker.getInstance();
-
- private final static StackLocator INSTANCE = new StackLocator();
+ private static final StackWalker STACK_WALKER = StackWalker.getInstance();
+ private static final StackLocator INSTANCE = new StackLocator();
public static StackLocator getInstance() {
return INSTANCE;
}
- private StackLocator() {
- }
+ private StackLocator() {}
- public Class> getCallerClass(final String fqcn) {
- return getCallerClass(fqcn, "");
+ public Class> getCallerClass(final Class> sentinelClass, final Predicate> callerPredicate) {
+ if (sentinelClass == null) {
+ throw new IllegalArgumentException("sentinelClass cannot be null");
+ }
+ if (callerPredicate == null) {
+ throw new IllegalArgumentException("callerPredicate cannot be null");
+ }
+ return WALKER.walk(s -> s.map(StackWalker.StackFrame::getDeclaringClass)
+ // Skip until the sentinel class is found
+ .dropWhile(clazz -> !sentinelClass.equals(clazz))
+ // Skip until the predicate evaluates to true, also ignoring recurrences of the sentinel
+ .dropWhile(clazz -> sentinelClass.equals(clazz) || !callerPredicate.test(clazz))
+ .findFirst()
+ .orElse(null));
}
public Class> getCallerClass(final String fqcn, final String pkg) {
- return walker.walk(s -> s.dropWhile(f -> !f.getClassName().equals(fqcn)).
- dropWhile(f -> f.getClassName().equals(fqcn)).dropWhile(f -> !f.getClassName().startsWith(pkg)).
- findFirst()).map(StackWalker.StackFrame::getDeclaringClass).orElse(null);
+ return WALKER.walk(s -> s.dropWhile(f -> !f.getClassName().equals(fqcn))
+ .dropWhile(f -> f.getClassName().equals(fqcn))
+ .dropWhile(f -> !f.getClassName().startsWith(pkg))
+ .findFirst())
+ .map(StackWalker.StackFrame::getDeclaringClass)
+ .orElse(null);
}
public Class> getCallerClass(final Class> anchor) {
- return walker.walk(s -> s.dropWhile(f -> !f.getDeclaringClass().equals(anchor)).
- dropWhile(f -> f.getDeclaringClass().equals(anchor)).findFirst()).
- map(StackWalker.StackFrame::getDeclaringClass).orElse(null);
+ return WALKER.walk(s -> s.dropWhile(f -> !f.getDeclaringClass().equals(anchor))
+ .dropWhile(f -> f.getDeclaringClass().equals(anchor))
+ .findFirst())
+ .map(StackWalker.StackFrame::getDeclaringClass)
+ .orElse(null);
}
public Class> getCallerClass(final int depth) {
- ;
- return walker.walk(s -> s.skip(depth).findFirst()).map(StackWalker.StackFrame::getDeclaringClass).orElse(null);
+ return WALKER.walk(s -> s.skip(depth).findFirst())
+ .map(StackWalker.StackFrame::getDeclaringClass)
+ .orElse(null);
}
- public Stack> getCurrentStackTrace() {
- Stack> stack = new Stack>();
- List> classes = walker.walk(s -> s.map(f -> f.getDeclaringClass()).collect(Collectors.toList()));
- stack.addAll(classes);
- return stack;
+ public Deque> getCurrentStackTrace() {
+ // benchmarks show that using the SecurityManager is much faster than looping through getCallerClass(int)
+ if (PrivateSecurityManagerStackTraceUtil.isEnabled()) {
+ return PrivateSecurityManagerStackTraceUtil.getCurrentStackTrace();
+ }
+ final Deque> stack = new ArrayDeque>();
+ return WALKER.walk(s -> {
+ s.forEach(f -> stack.add(f.getDeclaringClass()));
+ return stack;
+ });
}
public StackTraceElement calcLocation(final String fqcnOfLogger) {
- return stackWalker.walk(
- s -> s.dropWhile(f -> !f.getClassName().equals(fqcnOfLogger)) // drop the top frames until we reach the logger
+ return STACK_WALKER
+ .walk(s -> s.dropWhile(f ->
+ !f.getClassName().equals(fqcnOfLogger)) // drop the top frames until we reach the logger
.dropWhile(f -> f.getClassName().equals(fqcnOfLogger)) // drop the logger frames
.findFirst())
- .get()
- .toStackTraceElement();
+ .map(StackWalker.StackFrame::toStackTraceElement)
+ .orElse(null);
}
public StackTraceElement getStackTraceElement(final int depth) {
- return stackWalker.walk(s -> s.skip(depth).findFirst()).get().toStackTraceElement();
+ return STACK_WALKER
+ .walk(s -> s.skip(depth).findFirst())
+ .map(StackWalker.StackFrame::toStackTraceElement)
+ .orElse(null);
}
}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
new file mode 100644
index 00000000000..ec8a4792945
--- /dev/null
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/SystemPropertiesPropertySource.java
@@ -0,0 +1,23 @@
+/*
+ * 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.util;
+
+/**
+ * This is a dummy class and is only here to allow module-info.java to compile. It will not
+ * be copied into the log4j-api module.
+ */
+public class SystemPropertiesPropertySource implements PropertySource {}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java
index 6e7cee5278a..df62f71e5f7 100644
--- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/DefaultObjectInputFilter.java
@@ -1,51 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.util.internal;
+import static org.apache.logging.log4j.util.internal.SerializationUtil.REQUIRED_JAVA_CLASSES;
+import static org.apache.logging.log4j.util.internal.SerializationUtil.REQUIRED_JAVA_PACKAGES;
+
import java.io.ObjectInputFilter;
-import java.util.Arrays;
-import java.util.List;
public class DefaultObjectInputFilter implements ObjectInputFilter {
-
- private static final List REQUIRED_JAVA_CLASSES = Arrays.asList(
- "java.math.BigDecimal",
- "java.math.BigInteger",
- // for Message delegate
- "java.rmi.MarshalledObject",
- "[B"
- );
-
- private static final List REQUIRED_JAVA_PACKAGES = Arrays.asList(
- "java.lang.",
- "java.time",
- "java.util.",
- "org.apache.logging.log4j.",
- "[Lorg.apache.logging.log4j."
- );
-
private final ObjectInputFilter delegate;
public DefaultObjectInputFilter() {
delegate = null;
}
- public DefaultObjectInputFilter(ObjectInputFilter filter) {
+ public DefaultObjectInputFilter(final ObjectInputFilter filter) {
delegate = filter;
}
@@ -54,21 +38,20 @@ public DefaultObjectInputFilter(ObjectInputFilter filter) {
* @param filter The ObjectInputFilter.
* @return The DefaultObjectInputFilter.
*/
- public static DefaultObjectInputFilter newInstance(ObjectInputFilter filter) {
+ public static DefaultObjectInputFilter newInstance(final ObjectInputFilter filter) {
return new DefaultObjectInputFilter(filter);
}
-
@Override
- public Status checkInput(FilterInfo filterInfo) {
- Status status = null;
+ public Status checkInput(final FilterInfo filterInfo) {
+ Status status;
if (delegate != null) {
status = delegate.checkInput(filterInfo);
if (status != Status.UNDECIDED) {
return status;
}
}
- ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter();
+ final ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter();
if (serialFilter != null) {
status = serialFilter.checkInput(filterInfo);
if (status != Status.UNDECIDED) {
@@ -76,11 +59,15 @@ public Status checkInput(FilterInfo filterInfo) {
return status;
}
}
- if (filterInfo.serialClass() != null) {
- String name = filterInfo.serialClass().getName();
- if (isAllowedByDefault(name) || isRequiredPackage(name)) {
+ final Class> serialClass = filterInfo.serialClass();
+ if (serialClass != null) {
+ final String name = SerializationUtil.stripArray(serialClass);
+ if (isAllowedByDefault(name)) {
return Status.ALLOWED;
}
+ } else {
+ // Object already deserialized
+ return Status.ALLOWED;
}
return Status.REJECTED;
}
diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java
new file mode 100644
index 00000000000..ddade3dd8a8
--- /dev/null
+++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/internal/SerializationUtil.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util.internal;
+
+import java.util.List;
+
+/**
+ * Dummy class for compilation purposes only.
+ */
+public final class SerializationUtil {
+ public static final List REQUIRED_JAVA_CLASSES = List.of();
+ public static final List REQUIRED_JAVA_PACKAGES = List.of();
+
+ public static String stripArray(final Class> clazz) {
+ return null;
+ }
+}
diff --git a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/ProcessIdUtilTest.java b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/ProcessIdUtilTest.java
deleted file mode 100644
index 6b5368f5992..00000000000
--- a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/ProcessIdUtilTest.java
+++ /dev/null
@@ -1,29 +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.util;
-
-import org.junit.Test;
-import static org.junit.Assert.assertFalse;
-
-public class ProcessIdUtilTest {
-
- @Test
- public void processIdTest() throws Exception {
- String processId = ProcessIdUtil.getProcessId();
- assertFalse("ProcessId is default", processId.equals(ProcessIdUtil.DEFAULT_PROCESSID));
- }
-}
diff --git a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/StackLocatorTest.java b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/StackLocatorTest.java
deleted file mode 100644
index 77396c92fb3..00000000000
--- a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/StackLocatorTest.java
+++ /dev/null
@@ -1,145 +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.util;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.ParentRunner;
-
-import java.util.Stack;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-
-@RunWith(BlockJUnit4ClassRunner.class)
-public class StackLocatorTest {
-
- private static StackLocator stackLocator;
-
- @BeforeClass
- public static void setupClass() {
-
- stackLocator = StackLocator.getInstance();
- }
-
- @Test
- public void testGetCallerClass() throws Exception {
- final Class> expected = StackLocatorTest.class;
- final Class> actual = stackLocator.getCallerClass(1);
- assertSame(expected, actual);
- }
-
- @Test
- public void testGetCallerClassNameViaStackTrace() throws Exception {
- final Class> expected = StackLocatorTest.class;
- final Class> actual = Class.forName(new Throwable().getStackTrace()[0].getClassName());
- assertSame(expected, actual);
- }
-
- @Test
- public void testGetCurrentStackTrace() throws Exception {
- final Stack> classes = stackLocator.getCurrentStackTrace();
- final Stack> reversed = new Stack<>();
- reversed.ensureCapacity(classes.size());
- while (!classes.empty()) {
- reversed.push(classes.pop());
- }
- while (reversed.peek() != StackLocator.class) {
- reversed.pop();
- }
- reversed.pop(); // ReflectionUtil
- assertSame(StackLocatorTest.class, reversed.pop());
- }
-
- @Test
- public void testGetCallerClassViaName() throws Exception {
- final Class> expected = BlockJUnit4ClassRunner.class;
- final Class> actual = stackLocator.getCallerClass("org.junit.runners.ParentRunner");
- // if this test fails in the future, it's probably because of a JUnit upgrade; check the new stack trace and
- // update this test accordingly
- assertSame(expected, actual);
- }
-
- @Test
- public void testGetCallerClassViaAnchorClass() throws Exception {
- final Class> expected = BlockJUnit4ClassRunner.class;
- final Class> actual = stackLocator.getCallerClass(ParentRunner.class);
- // if this test fails in the future, it's probably because of a JUnit upgrade; check the new stack trace and
- // update this test accordingly
- assertSame(expected, actual);
- }
-
- @Test
- public void testLocateClass() {
- ClassLocator locator = new ClassLocator();
- Class> clazz = locator.locateClass();
- assertNotNull("Could not locate class", clazz);
- assertEquals("Incorrect class", this.getClass(), clazz);
- }
-
- private final class Foo {
-
- private StackTraceElement foo() {
- return new Bar().bar();
- }
-
- }
-
- private final class Bar {
-
- private StackTraceElement bar() {
- return baz();
- }
-
- private StackTraceElement baz() {
- return quux();
- }
-
- }
-
- private StackTraceElement quux() {
- return stackLocator.calcLocation("org.apache.logging.log4j.util.StackLocatorTest$Bar");
- }
-
- @Test
- public void testCalcLocation() {
- /*
- * We are setting up a stack trace that looks like:
- * - org.apache.logging.log4j.util.StackLocatorTest#quux(line:118)
- * - org.apache.logging.log4j.util.StackLocatorTest$Bar#baz(line:112)
- * - org.apache.logging.log4j.util.StackLocatorTest$Bar#bar(line:108)
- * - org.apache.logging.log4j.util.StackLocatorTest$Foo(line:100)
- *
- * We are pretending that org.apache.logging.log4j.util.StackLocatorTest$Bar is the logging class, and
- * org.apache.logging.log4j.util.StackLocatorTest$Foo is where the log line emanated.
- */
- final StackTraceElement element = new Foo().foo();
- assertEquals("org.apache.logging.log4j.util.StackLocatorTest$Foo", element.getClassName());
- assertEquals(100, element.getLineNumber());
- }
-
- class ClassLocator {
-
- public Class> locateClass() {
- return stackLocator.getCallerClass(ClassLocator.class);
- }
- }
-
-}
diff --git a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/java9/ProcessIdUtilTest.java b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/java9/ProcessIdUtilTest.java
new file mode 100644
index 00000000000..1a24860f57d
--- /dev/null
+++ b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/java9/ProcessIdUtilTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util.java9;
+
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import org.apache.logging.log4j.util.ProcessIdUtil;
+import org.junit.jupiter.api.Test;
+
+class ProcessIdUtilTest {
+
+ @Test
+ void processIdTest() {
+ final String processId = ProcessIdUtil.getProcessId();
+ assertNotEquals(processId, ProcessIdUtil.DEFAULT_PROCESSID, "ProcessId is default");
+ }
+}
diff --git a/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/java9/StackLocatorTest.java b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/java9/StackLocatorTest.java
new file mode 100644
index 00000000000..cc8f61218a7
--- /dev/null
+++ b/log4j-api-java9/src/test/java/org/apache/logging/log4j/util/java9/StackLocatorTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.util.java9;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import java.util.Deque;
+import java.util.Stack;
+import org.apache.logging.log4j.util.StackLocator;
+import org.junit.jupiter.api.Test;
+
+class StackLocatorTest {
+
+ @Test
+ void testGetCallerClass() {
+ final Class> expected = StackLocatorTest.class;
+ final StackLocator stackLocator = StackLocator.getInstance();
+ final Class> actual = stackLocator.getCallerClass(1);
+ assertSame(expected, actual);
+ }
+
+ @Test
+ void testGetCallerClassNameViaStackTrace() throws Exception {
+ final Class> expected = StackLocatorTest.class;
+ final Class> actual = Class.forName(new Throwable().getStackTrace()[0].getClassName());
+ assertSame(expected, actual);
+ }
+
+ @Test
+ void testGetCurrentStackTrace() {
+ final StackLocator stackLocator = StackLocator.getInstance();
+ final Deque> classes = stackLocator.getCurrentStackTrace();
+ final Stack> reversed = new Stack<>();
+ reversed.ensureCapacity(classes.size());
+ while (!classes.isEmpty()) {
+ reversed.push(classes.removeLast());
+ }
+ while (reversed.peek() != StackLocator.class) {
+ reversed.pop();
+ }
+ reversed.pop(); // ReflectionUtil
+ assertSame(StackLocatorTest.class, reversed.pop());
+ }
+
+ @Test
+ void testGetCallerClassViaName() {
+ Inner.assertCallerClassViaName();
+ }
+
+ @Test
+ void testGetCallerClassViaAnchorClass() {
+ Inner.assertCallerClassViaAnchorClass();
+ }
+
+ private static class Inner {
+ private static void assertCallerClassViaName() {
+ final Class> expected = StackLocatorTest.class;
+ final StackLocator stackLocator = StackLocator.getInstance();
+ final Class> actual = stackLocator.getCallerClass(Inner.class.getName(), "");
+ assertSame(expected, actual);
+ }
+
+ private static void assertCallerClassViaAnchorClass() {
+ final Class> expected = StackLocatorTest.class;
+ final StackLocator stackLocator = StackLocator.getInstance();
+ final Class> actual = stackLocator.getCallerClass(Inner.class);
+ assertSame(expected, actual);
+ }
+ }
+
+ @Test
+ void testLocateClass() {
+ final ClassLocator locator = new ClassLocator();
+ final Class> clazz = locator.locateClass();
+ assertNotNull(clazz, "Could not locate class");
+ assertEquals(this.getClass(), clazz, "Incorrect class");
+ }
+
+ private final class Foo {
+
+ private StackTraceElement foo() {
+ return new Bar().bar(); // <--- testCalcLocation() line
+ }
+ }
+
+ private final class Bar {
+
+ private StackTraceElement bar() {
+ return baz();
+ }
+
+ private StackTraceElement baz() {
+ return quux();
+ }
+ }
+
+ private StackTraceElement quux() {
+ final StackLocator stackLocator = StackLocator.getInstance();
+ return stackLocator.calcLocation("org.apache.logging.log4j.util.java9.StackLocatorTest$Bar");
+ }
+
+ @Test
+ void testCalcLocation() {
+ /*
+ * We are setting up a stack trace that looks like:
+ * - org.apache.logging.log4j.util.test.StackLocatorTest#quux(line:118)
+ * - org.apache.logging.log4j.util.test.StackLocatorTest$Bar#baz(line:112)
+ * - org.apache.logging.log4j.util.test.StackLocatorTest$Bar#bar(line:108)
+ * - org.apache.logging.log4j.util.test.StackLocatorTest$Foo(line:100)
+ *
+ * We are pretending that org.apache.logging.log4j.util.test.StackLocatorTest$Bar is the logging class, and
+ * org.apache.logging.log4j.util.test.StackLocatorTest$Foo is where the log line emanated.
+ */
+ final StackTraceElement element = new Foo().foo();
+ assertEquals("org.apache.logging.log4j.util.java9.StackLocatorTest$Foo", element.getClassName());
+ // The line number below may need adjustment if this file is changed.
+ assertEquals(99, element.getLineNumber());
+ }
+
+ @Test
+ void testTopElementInStackTrace() {
+ final StackLocator stackLocator = StackLocator.getInstance();
+ final Deque> classes = stackLocator.getCurrentStackTrace();
+ assertSame(StackLocator.class, classes.getFirst());
+ }
+
+ @Test
+ void testCalcLocationWhenNotInTheStack() {
+ final StackLocator stackLocator = StackLocator.getInstance();
+ final StackTraceElement stackTraceElement = stackLocator.calcLocation("java.util.Logger");
+ assertNull(stackTraceElement);
+ }
+
+ static class ClassLocator {
+
+ public Class> locateClass() {
+ final StackLocator stackLocator = StackLocator.getInstance();
+ return stackLocator.getCallerClass(ClassLocator.class);
+ }
+ }
+}
diff --git a/log4j-api-test/pom.xml b/log4j-api-test/pom.xml
new file mode 100644
index 00000000000..aef8a1b5d64
--- /dev/null
+++ b/log4j-api-test/pom.xml
@@ -0,0 +1,182 @@
+
+
+
+ 4.0.0
+
+ org.apache.logging.log4j
+ log4j
+ ${revision}
+ ../log4j-parent
+
+ log4j-api-test
+ jar
+ Apache Log4j API Tests
+ The Apache Log4j API Test
+
+
+ 9
+
+
+ org.apache.logging.log4j.test
+
+ org.apache.commons.lang3.*;resolution:=optional,
+ org.assertj.*;resolution:=optional,
+
+ org.junit.*;resolution:=optional,
+ org.hamcrest.*;resolution:=optional,
+ org.junitpioneer.*;resolution:=optional,
+ org.apache.maven.*;resolution:=optional,
+ org.codehaus.plexus.util.*;resolution:=optional,
+ org.mockito.*;resolution:=optional
+
+
+
+ junit;transitive=false,
+ org.hamcrest;transitive=false,
+ org.junit.jupiter.api;transitive=false,
+ org.junitpioneer;transitive=false,
+
+ maven.core;substitute="maven-core";transitive=false;static=true,
+ maven.model;substitute="maven-model";transitive=false;static=true,
+ maven.model.builder;substitute="maven-model-builder";transitive=false;static=true,
+ plexus.utils;substitute="plexus-utils";transitive=false;static=true
+
+
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.hamcrest
+ hamcrest
+
+
+ junit
+ junit
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+ org.junit-pioneer
+ junit-pioneer
+
+
+ org.junit.platform
+ junit-platform-commons
+
+
+ org.apache.maven
+ maven-core
+
+
+ org.apache.maven
+ maven-model
+
+
+ org.codehaus.plexus
+ plexus-utils
+
+
+ org.assertj
+ assertj-core
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ test
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.mockito
+ mockito-core
+
+
+ org.mockito
+ mockito-inline
+ test
+
+
+ org.jspecify
+ jspecify
+ test
+
+
+
+ org.osgi
+ org.osgi.core
+ test
+
+
+ uk.org.webcompere
+ system-stubs-core
+ test
+
+
+ uk.org.webcompere
+ system-stubs-jupiter
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ performance,smoke
+
+
+
+
+
+
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/AbstractSerializationTest.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/AbstractSerializationTest.java
new file mode 100644
index 00000000000..a2b67b64889
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/AbstractSerializationTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.test;
+
+import static org.apache.logging.log4j.test.SerializableMatchers.serializesRoundTrip;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.Serializable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Subclasses tests {@link Serializable} objects.
+ */
+@RunWith(Parameterized.class)
+public abstract class AbstractSerializationTest {
+
+ private final Serializable serializable;
+
+ public AbstractSerializationTest(final Serializable serializable) {
+ this.serializable = serializable;
+ }
+
+ @Test
+ public void testSerializationRoundtripEquals() {
+ assertThat(serializable, serializesRoundTrip(serializable));
+ }
+
+ @Test
+ public void testSerializationRoundtripNoException() {
+ assertThat(serializable, serializesRoundTrip());
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ListStatusListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ListStatusListener.java
new file mode 100644
index 00000000000..fb19e5b7aef
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ListStatusListener.java
@@ -0,0 +1,43 @@
+/*
+ * 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.test;
+
+import java.util.stream.Stream;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.status.StatusListener;
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * A {@link StatusListener} that collects messages for further inspection.
+ */
+@ProviderType
+public interface ListStatusListener extends StatusListener {
+
+ void clear();
+
+ Stream getStatusData();
+
+ default Stream findStatusData(Level level) {
+ return getStatusData().filter(data -> level.isLessSpecificThan(data.getLevel()));
+ }
+
+ default Stream findStatusData(Level level, String regex) {
+ return findStatusData(level)
+ .filter(data -> data.getMessage().getFormattedMessage().matches(regex));
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/SerializableMatchers.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java
similarity index 82%
rename from log4j-api/src/test/java/org/apache/logging/log4j/SerializableMatchers.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java
index 7545b964a96..6fc46f6f936 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/SerializableMatchers.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/SerializableMatchers.java
@@ -1,30 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
-import java.io.Serializable;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsInstanceOf.any;
+import java.io.Serializable;
import org.apache.commons.lang3.SerializationUtils;
import org.hamcrest.FeatureMatcher;
import org.hamcrest.Matcher;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.hamcrest.core.IsInstanceOf.any;
-
/**
* Hamcrest Matchers for Serializable classes.
*
@@ -53,6 +52,5 @@ public static Matcher super Serializable> serializesRoundTrip() {
return serializesRoundTrip(any(Serializable.class));
}
- private SerializableMatchers() {
- }
+ private SerializableMatchers() {}
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java
new file mode 100644
index 00000000000..e95f1e9bb67
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java
@@ -0,0 +1,269 @@
+/*
+ * 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.test;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.spi.AbstractLogger;
+
+/**
+ *
+ */
+public class TestLogger extends AbstractLogger {
+
+ private static final long serialVersionUID = 1L;
+
+ public TestLogger() {}
+
+ public TestLogger(final String name, final MessageFactory messageFactory) {
+ super(name, messageFactory);
+ }
+
+ public TestLogger(final String name) {
+ super(name);
+ }
+
+ private final List list = new ArrayList<>();
+
+ public List getEntries() {
+ return list;
+ }
+
+ @Override
+ public void logMessage(
+ final String fqcn, final Level level, final Marker marker, final Message msg, final Throwable throwable) {
+ log(level, marker, fqcn, (StackTraceElement) null, msg, throwable);
+ }
+
+ @Override
+ @SuppressFBWarnings("INFORMATION_EXPOSURE_THROUGH_AN_ERROR_MESSAGE")
+ protected void log(
+ final Level level,
+ final Marker marker,
+ final String fqcn,
+ final StackTraceElement location,
+ final Message message,
+ final Throwable throwable) {
+ final StringBuilder sb = new StringBuilder();
+ if (marker != null) {
+ sb.append(marker);
+ }
+ sb.append(' ');
+ sb.append(level.toString());
+ sb.append(' ');
+ if (location != null) {
+ sb.append(location);
+ sb.append(' ');
+ }
+ sb.append(message.getFormattedMessage());
+ final Map mdc = ThreadContext.getImmutableContext();
+ if (!mdc.isEmpty()) {
+ sb.append(' ');
+ sb.append(mdc);
+ sb.append(' ');
+ }
+ final Object[] params = message.getParameters();
+ final Throwable t;
+ if (throwable == null
+ && params != null
+ && params.length > 0
+ && params[params.length - 1] instanceof Throwable) {
+ t = (Throwable) params[params.length - 1];
+ } else {
+ t = throwable;
+ }
+ if (t != null) {
+ sb.append(' ');
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ t.printStackTrace(new PrintStream(baos));
+ sb.append(baos);
+ }
+ list.add(sb.toString());
+ // System.out.println(sb.toString());
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String msg) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String msg, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String msg, final Object... p1) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level, final Marker marker, final String message, final Object p0, final Object p1) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8,
+ final Object p9) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final CharSequence msg, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Object msg, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Message msg, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public Level getLevel() {
+ return Level.ALL;
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContext.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContext.java
similarity index 83%
rename from log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContext.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContext.java
index 7ca2fd69e79..d2b6ea7b587 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/TestLoggerContext.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContext.java
@@ -1,27 +1,26 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.util.HashMap;
import java.util.Map;
-
import org.apache.logging.log4j.message.MessageFactory;
-import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.spi.ExtendedLogger;
+import org.apache.logging.log4j.spi.LoggerContext;
/**
*
@@ -33,7 +32,7 @@ public class TestLoggerContext implements LoggerContext {
public ExtendedLogger getLogger(final String name) {
final ExtendedLogger extendedLogger = map.get(name);
if (extendedLogger != null) {
- return extendedLogger;
+ return extendedLogger;
}
final ExtendedLogger logger = new TestLogger(name);
map.put(name, logger);
@@ -64,5 +63,4 @@ public boolean hasLogger(final String name, final MessageFactory messageFactory)
public boolean hasLogger(final String name, final Class extends MessageFactory> messageFactoryClass) {
return false;
}
-
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContextFactory.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContextFactory.java
new file mode 100644
index 00000000000..132b04ee707
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLoggerContextFactory.java
@@ -0,0 +1,54 @@
+/*
+ * 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.test;
+
+import java.net.URI;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+
+/**
+ *
+ */
+public class TestLoggerContextFactory implements LoggerContextFactory {
+
+ private static final LoggerContext context = new TestLoggerContext();
+
+ @Override
+ public LoggerContext getContext(
+ final String fqcn, final ClassLoader loader, final Object externalContext, final boolean currentContext) {
+ return context;
+ }
+
+ @Override
+ public LoggerContext getContext(
+ final String fqcn,
+ final ClassLoader loader,
+ final Object externalContext,
+ final boolean currentContext,
+ final URI configLocation,
+ final String name) {
+ return context;
+ }
+
+ @Override
+ public void removeContext(final LoggerContext context) {}
+
+ @Override
+ public boolean isClassLoaderDependent() {
+ return false;
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java
new file mode 100644
index 00000000000..9942990520c
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestProperties.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test;
+
+/**
+ * A container for per-test properties.
+ */
+public interface TestProperties {
+
+ /**
+ * Path to a directory specific to the test class,
+ */
+ public static final String LOGGING_PATH = "logging.path";
+
+ String getProperty(final String key);
+
+ boolean containsProperty(final String key);
+
+ void setProperty(final String key, final String value);
+
+ default void setProperty(final String key, final boolean value) {
+ setProperty(key, value ? "true" : "false");
+ }
+
+ default void setProperty(final String key, final int value) {
+ setProperty(key, Integer.toString(value));
+ }
+
+ void clearProperty(final String key);
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextHolder.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextHolder.java
similarity index 85%
rename from log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextHolder.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextHolder.java
index 4c85bde3671..2407e237307 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextHolder.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextHolder.java
@@ -1,37 +1,36 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
import java.util.Map;
-
+import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.ThreadContext.ContextStack;
/**
* Holds an immutable copy of the ThreadContext stack and map.
- *
+ *
* TODO Use LOG4J2-1517 Add ThreadContext.setContext(Map)
- *
+ *
* or
- *
+ *
* TODO Might be replaced by something from LOG4J2-1447.
- *
+ *
* or do nothing.
- *
+ *
* @since 2.7
*/
public class ThreadContextHolder {
@@ -43,7 +42,7 @@ public class ThreadContextHolder {
/**
* Constructs a new holder initialized with an immutable copy of the ThreadContext stack and map.
- *
+ *
* @param restoreContext
* @param restoreStack
*/
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
similarity index 77%
rename from log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
index c6e1826fad4..eed1952c324 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/ThreadContextUtilityClass.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/ThreadContextUtilityClass.java
@@ -1,32 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-package org.apache.logging.log4j;
+package org.apache.logging.log4j.test;
-import java.util.Map;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.apache.logging.log4j.Timer;
+import java.util.Map;
import org.apache.logging.log4j.ThreadContext;
-import static org.junit.Assert.*;
-
-
+import org.apache.logging.log4j.util.Timer;
public class ThreadContextUtilityClass {
- public static void perfTest() throws Exception {
+ public static void perfTest() {
ThreadContext.clearMap();
final Timer complete = new Timer("ThreadContextTest");
complete.start();
@@ -53,13 +56,11 @@ public static void perfTest() throws Exception {
System.out.println(complete.toString());
}
-
public static void testGetContextReturnsEmptyMapIfEmpty() {
ThreadContext.clearMap();
assertTrue(ThreadContext.getContext().isEmpty());
}
-
public static void testGetContextReturnsMutableCopy() {
ThreadContext.clearMap();
final Map map1 = ThreadContext.getContext();
@@ -87,18 +88,17 @@ public static void testGetImmutableContextReturnsEmptyMapIfEmpty() {
assertTrue(ThreadContext.getImmutableContext().isEmpty());
}
-
public static void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
ThreadContext.clearMap();
ThreadContext.put("key", "val");
final Map immutable = ThreadContext.getImmutableContext();
- immutable.put("otherkey", "otherval");
+ assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
}
public static void testGetImmutableContextReturnsImmutableMapIfEmpty() {
ThreadContext.clearMap();
final Map immutable = ThreadContext.getImmutableContext();
- immutable.put("otherkey", "otherval");
+ assertThrows(UnsupportedOperationException.class, () -> immutable.put("otherkey", "otherval"));
}
public static void testGetImmutableStackReturnsEmptyStackIfEmpty() {
@@ -106,11 +106,14 @@ public static void testGetImmutableStackReturnsEmptyStackIfEmpty() {
assertTrue(ThreadContext.getImmutableStack().asList().isEmpty());
}
-
public static void testPut() {
ThreadContext.clearMap();
assertNull(ThreadContext.get("testKey"));
ThreadContext.put("testKey", "testValue");
assertEquals("testValue", ThreadContext.get("testKey"));
}
+
+ public static void reset() {
+ ThreadContext.init();
+ }
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractFileCleaner.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractFileCleaner.java
new file mode 100644
index 00000000000..268c9318d61
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/AbstractFileCleaner.java
@@ -0,0 +1,88 @@
+/*
+ * 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.test.junit;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+abstract class AbstractFileCleaner implements BeforeEachCallback, AfterEachCallback {
+
+ private static final int MAX_TRIES = Integer.getInteger("log4j2.junit.fileCleanerMaxTries", 10);
+
+ private static final int SLEEP_PERIOD_MILLIS = Integer.getInteger("log4j2.junit.fileCleanerSleepPeriodMillis", 200);
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ clean(context);
+ }
+
+ @Override
+ public void afterEach(final ExtensionContext context) throws Exception {
+ clean(context);
+ }
+
+ private void clean(final ExtensionContext context) {
+ final Collection paths = getPathsForTest(context);
+ if (paths.isEmpty()) {
+ return;
+ }
+ final Map failures = new ConcurrentHashMap<>();
+ for (final Path path : paths) {
+ if (Files.exists(path)) {
+ for (int i = 0; i < MAX_TRIES; i++) {
+ try {
+ if (delete(path)) {
+ failures.remove(path);
+ break;
+ }
+ } catch (final IOException e) {
+ failures.put(path, e);
+ }
+ try {
+ TimeUnit.MILLISECONDS.sleep(SLEEP_PERIOD_MILLIS);
+ } catch (final InterruptedException ignored) {
+ failures.put(path, new InterruptedIOException());
+ Thread.currentThread().interrupt();
+ break;
+ }
+ }
+ }
+ }
+ if (!failures.isEmpty()) {
+ final String message = failures.entrySet().stream()
+ .map(e -> e.getKey() + " failed with " + e.getValue())
+ .collect(Collectors.joining(", "));
+ fail(message);
+ }
+ }
+
+ abstract Collection getPathsForTest(final ExtensionContext context);
+
+ abstract boolean delete(final Path path) throws IOException;
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/BundleTestInfo.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/BundleTestInfo.java
new file mode 100644
index 00000000000..6dce1d5ad1c
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/BundleTestInfo.java
@@ -0,0 +1,68 @@
+/*
+ * 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.test.junit;
+
+import java.io.FileReader;
+import java.io.IOException;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Provides tests with bundle information. Reads the {@code pom.xml} in the current directory to get project settings.
+ */
+public class BundleTestInfo {
+
+ private final MavenProject project;
+
+ /**
+ * Constructs a new helper objects and initializes itself.
+ */
+ public BundleTestInfo() {
+ try (final FileReader reader = new FileReader("pom.xml")) {
+ // get a raw POM view, not a fully realized POM object.
+ final Model model = new MavenXpp3Reader().read(reader);
+ this.project = new MavenProject(model);
+ } catch (final IOException | XmlPullParserException e) {
+ throw new IllegalStateException("Could not read pom.xml", e);
+ }
+ }
+
+ /**
+ * Gets the Maven artifact ID.
+ *
+ * @return the Maven artifact ID.
+ */
+ public String getArtifactId() {
+ return project.getArtifactId();
+ }
+
+ /**
+ * Gets the Maven version String.
+ *
+ * @return the Maven version String.
+ */
+ public String getVersion() {
+ return project.getVersion();
+ }
+
+ @Override
+ public String toString() {
+ return "BundleTestInfo [project=" + project + "]";
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpDirectories.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpDirectories.java
new file mode 100644
index 00000000000..841f6d1fcca
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpDirectories.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * JUnit extension to automatically clean up a list of directories and their contents before and after test execution.
+ * This will automatically retry deletion up to 10 times per file while pausing for 200ms each time.
+ * These can be overridden with system properties {@code log4j2.junit.fileCleanerMaxTries} and
+ * {@code log4j2.junit.fileCleanerSleepPeriodMillis}.
+ *
+ * @see DirectoryCleaner
+ * @see CleanUpFiles
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Documented
+@Inherited
+@ExtendWith(DirectoryCleaner.class)
+public @interface CleanUpDirectories {
+ String[] value();
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpFiles.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpFiles.java
new file mode 100644
index 00000000000..7ab44f6d7b6
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/CleanUpFiles.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * JUnit extension to automatically clean up a list of files before and after test execution.
+ * This will automatically retry deletion up to 10 times per file while pausing for 200ms each time.
+ * These can be overridden with system properties {@code log4j2.junit.fileCleanerMaxTries} and
+ * {@code log4j2.junit.fileCleanerSleepPeriodMillis}.
+ *
+ * @see FileCleaner
+ * @see CleanUpDirectories
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Documented
+@Inherited
+@ExtendWith(FileCleaner.class)
+public @interface CleanUpFiles {
+ String[] value();
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/DirectoryCleaner.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/DirectoryCleaner.java
new file mode 100644
index 00000000000..84c12b42109
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/DirectoryCleaner.java
@@ -0,0 +1,76 @@
+/*
+ * 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.test.junit;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collection;
+import java.util.HashSet;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class DirectoryCleaner extends AbstractFileCleaner {
+ @Override
+ @SuppressFBWarnings("PATH_TRAVERSAL_IN")
+ Collection getPathsForTest(final ExtensionContext context) {
+ final Collection paths = new HashSet<>();
+ final CleanUpDirectories testClassAnnotation =
+ context.getRequiredTestClass().getAnnotation(CleanUpDirectories.class);
+ if (testClassAnnotation != null) {
+ for (final String path : testClassAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ final CleanUpDirectories testMethodAnnotation =
+ context.getRequiredTestMethod().getAnnotation(CleanUpDirectories.class);
+ if (testMethodAnnotation != null) {
+ for (final String path : testMethodAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ return paths;
+ }
+
+ @Override
+ boolean delete(final Path path) throws IOException {
+ return deleteDirectory(path);
+ }
+
+ static boolean deleteDirectory(final Path path) throws IOException {
+ if (Files.exists(path) && Files.isDirectory(path)) {
+ Files.walkFileTree(path, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ Files.deleteIfExists(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+ Files.deleteIfExists(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ return true;
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
new file mode 100644
index 00000000000..0e82c01b8e1
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ExtensionContextAnchor.java
@@ -0,0 +1,88 @@
+/*
+ * 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.test.junit;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+
+public class ExtensionContextAnchor
+ implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback, AfterEachCallback {
+
+ public static Namespace LOG4J2_NAMESPACE = Namespace.create("org.apache.logging.log4j.junit");
+ private static final ThreadLocal EXTENSION_CONTEXT = new InheritableThreadLocal<>();
+
+ private static void bind(final ExtensionContext context) {
+ EXTENSION_CONTEXT.set(context);
+ }
+
+ private static void unbind(final ExtensionContext context) {
+ EXTENSION_CONTEXT.set(context.getParent().orElse(null));
+ }
+
+ public static ExtensionContext getContext() {
+ return EXTENSION_CONTEXT.get();
+ }
+
+ public static ExtensionContext getContext(final ExtensionContext context) {
+ return context != null ? context : EXTENSION_CONTEXT.get();
+ }
+
+ public static T getAttribute(final Object key, final Class clazz, final ExtensionContext context) {
+ final ExtensionContext actualContext = getContext(context);
+ assertNotNull(actualContext, "missing ExtensionContext");
+ return actualContext.getStore(LOG4J2_NAMESPACE).get(key, clazz);
+ }
+
+ public static void setAttribute(final Object key, final Object value, final ExtensionContext context) {
+ final ExtensionContext actualContext = getContext(context);
+ assertNotNull(actualContext, "missing ExtensionContext");
+ actualContext.getStore(LOG4J2_NAMESPACE).put(key, value);
+ }
+
+ public static void removeAttribute(final Object key, final ExtensionContext context) {
+ final ExtensionContext actualContext = getContext(context);
+ if (actualContext != null) {
+ actualContext.getStore(LOG4J2_NAMESPACE).remove(key);
+ }
+ }
+
+ @Override
+ public void afterEach(final ExtensionContext context) throws Exception {
+ unbind(context);
+ }
+
+ @Override
+ public void afterAll(final ExtensionContext context) throws Exception {
+ unbind(context);
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ bind(context);
+ }
+
+ @Override
+ public void beforeAll(final ExtensionContext context) throws Exception {
+ bind(context);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/FileCleaner.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/FileCleaner.java
new file mode 100644
index 00000000000..9a9500d53a6
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/FileCleaner.java
@@ -0,0 +1,53 @@
+/*
+ * 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.test.junit;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashSet;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class FileCleaner extends AbstractFileCleaner {
+ @Override
+ @SuppressFBWarnings("PATH_TRAVERSAL_IN")
+ Collection getPathsForTest(final ExtensionContext context) {
+ final Collection paths = new HashSet<>();
+ final CleanUpFiles testClassAnnotation = context.getRequiredTestClass().getAnnotation(CleanUpFiles.class);
+ if (testClassAnnotation != null) {
+ for (final String path : testClassAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ final CleanUpFiles testMethodAnnotation =
+ context.getRequiredTestMethod().getAnnotation(CleanUpFiles.class);
+ if (testMethodAnnotation != null) {
+ for (final String path : testMethodAnnotation.value()) {
+ paths.add(Paths.get(path));
+ }
+ }
+ return paths;
+ }
+
+ @Override
+ boolean delete(final Path path) throws IOException {
+ return Files.deleteIfExists(path);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
new file mode 100644
index 00000000000..abbb616dc0e
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/InitializesThreadContext.java
@@ -0,0 +1,39 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/**
+ * Marks a test class that initializes the {@link ThreadContext} class;
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Documented
+@Inherited
+@ExtendWith(ThreadContextInitializer.class)
+@ResourceLock(value = Log4jStaticResources.THREAD_CONTEXT, mode = ResourceAccessMode.READ_WRITE)
+public @interface InitializesThreadContext {}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4jStaticResources.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4jStaticResources.java
new file mode 100644
index 00000000000..b387ed095ac
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Log4jStaticResources.java
@@ -0,0 +1,55 @@
+/*
+ * 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.test.junit;
+
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/**
+ * Constants to use the {@link ResourceLock} annotation.
+ */
+public final class Log4jStaticResources {
+
+ /**
+ * Marks tests that require access to {@link org.apache.logging.log4j.LogManager} methods or change its
+ * underlying {@link org.apache.logging.log4j.spi.LoggerContextFactory} implementation.
+ */
+ public static final String LOG_MANAGER = "log4j.LogManager";
+
+ /**
+ * Marks tests that require access to {@link org.apache.logging.log4j.ThreadContext} methods or change its
+ * underlying {@link org.apache.logging.log4j.spi.ThreadContextMap} implementation.
+ */
+ public static final String THREAD_CONTEXT = "log4j.ThreadContext";
+
+ /**
+ * Marks tests that require access to {@link org.apache.logging.log4j.MarkerManager} methods.
+ */
+ public static final String MARKER_MANAGER = "log4j.MarkerManager";
+
+ /**
+ * Marks tests that requires access to {@link org.apache.logging.log4j.Level} static methods to create new levels.
+ */
+ public static final String LEVEL = "log4j.Level";
+
+ /**
+ * Marks tests that require access to {@link org.apache.logging.log4j.status.StatusLogger} static methods or
+ * change its underlying implementation.
+ */
+ public static final String STATUS_LOGGER = "log4j.StatusLogger";
+
+ private Log4jStaticResources() {}
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/LogManagerLoggerContextFactoryRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LogManagerLoggerContextFactoryRule.java
similarity index 77%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/LogManagerLoggerContextFactoryRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LogManagerLoggerContextFactoryRule.java
index 98735ab8552..a925fedb6d2 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/LogManagerLoggerContextFactoryRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LogManagerLoggerContextFactoryRule.java
@@ -1,20 +1,20 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.spi.LoggerContextFactory;
@@ -23,7 +23,10 @@
/**
* Sets the {@link LogManager}'s {@link LoggerContextFactory} to the given instance before the test and restores it to
* the original value after the test.
+ *
+ * @deprecated Use {@link LoggerContextFactoryExtension} with JUnit 5
*/
+@Deprecated
public class LogManagerLoggerContextFactoryRule extends ExternalResource {
private final LoggerContextFactory loggerContextFactory;
@@ -31,7 +34,6 @@ public class LogManagerLoggerContextFactoryRule extends ExternalResource {
private LoggerContextFactory restoreLoggerContextFactory;
public LogManagerLoggerContextFactoryRule(final LoggerContextFactory loggerContextFactory) {
- super();
this.loggerContextFactory = loggerContextFactory;
}
@@ -45,5 +47,4 @@ protected void before() throws Throwable {
this.restoreLoggerContextFactory = LogManager.getFactory();
LogManager.setFactory(this.loggerContextFactory);
}
-
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LoggerContextFactoryExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LoggerContextFactoryExtension.java
new file mode 100644
index 00000000000..4e0fbf75df5
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/LoggerContextFactoryExtension.java
@@ -0,0 +1,53 @@
+/*
+ * 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.test.junit;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.spi.LoggerContextFactory;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+/**
+ * JUnit 5 extension that sets a particular {@link LoggerContextFactory} for the entire run of tests in a class.
+ *
+ * @since 2.14.0
+ */
+public class LoggerContextFactoryExtension implements BeforeAllCallback, AfterAllCallback {
+
+ private static final String KEY = "previousFactory";
+ private final LoggerContextFactory loggerContextFactory;
+
+ public LoggerContextFactoryExtension(final LoggerContextFactory loggerContextFactory) {
+ this.loggerContextFactory = loggerContextFactory;
+ }
+
+ @Override
+ public void beforeAll(final ExtensionContext context) throws Exception {
+ getStore(context).put(KEY, LogManager.getFactory());
+ LogManager.setFactory(loggerContextFactory);
+ }
+
+ @Override
+ public void afterAll(final ExtensionContext context) throws Exception {
+ LogManager.setFactory(getStore(context).get(KEY, LoggerContextFactory.class));
+ }
+
+ private ExtensionContext.Store getStore(final ExtensionContext context) {
+ return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestClass()));
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Mutable.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Mutable.java
new file mode 100644
index 00000000000..f9f1a12185f
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/Mutable.java
@@ -0,0 +1,34 @@
+/*
+ * 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.test.junit;
+
+/**
+ * Helper class for JUnit tests.
+ */
+public class Mutable {
+ private String value;
+
+ public Mutable set(final String value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return this.value;
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SecurityManagerTestRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SecurityManagerTestRule.java
new file mode 100644
index 00000000000..8107ecb3dab
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SecurityManagerTestRule.java
@@ -0,0 +1,90 @@
+/*
+ * 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.test.junit;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Sets a security manager for a test run. The current security manager is first saved then restored after the test is
+ * run.
+ *
+ * Using a security manager can mess up other tests so this is best used from integration tests (classes that end in
+ * "IT" instead of "Test" and "TestCase".)
+ *
+ *
+ *
+ * When this test rule is evaluated, it will:
+ *
+ *
+ * Save the current SecurityManager.
+ * Set the SecurityManager to the instance supplied to this rule.
+ * Evaluate the test statement.
+ * Reset the current SecurityManager to the one from step (1).
+ *
+ *
+ * @since 2.11.0
+ */
+public class SecurityManagerTestRule implements TestRule {
+
+ /**
+ * Constructs a new instance with the given {@link SecurityManager}.
+ *
+ * When this test rule is evaluated, it will:
+ *
+ *
+ * Save the current SecurityManager.
+ * Set the SecurityManager to the instance supplied to this rule.
+ * Evaluate the test statement.
+ * Reset the current SecurityManager to the one from step (1).
+ *
+ *
+ * @param securityManager
+ * the {@link SecurityManager} to use while running a test.
+ */
+ public SecurityManagerTestRule(final SecurityManager securityManager) {
+ this.securityManager = securityManager;
+ }
+
+ private SecurityManager securityManagerBefore;
+ private final SecurityManager securityManager;
+
+ @Override
+ public Statement apply(final Statement base, final Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ before();
+ try {
+ base.evaluate();
+ } finally {
+ after();
+ }
+ }
+
+ private void after() {
+ System.setSecurityManager(securityManagerBefore);
+ }
+
+ private void before() {
+ securityManagerBefore = System.getSecurityManager();
+ System.setSecurityManager(securityManager);
+ }
+ };
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java
new file mode 100644
index 00000000000..e568e415130
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SerialUtil.java
@@ -0,0 +1,104 @@
+/*
+ * 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.test.junit;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import org.apache.logging.log4j.util.Constants;
+import org.apache.logging.log4j.util.FilteredObjectInputStream;
+
+/**
+ * Utility class to facilitate serializing and deserializing objects.
+ */
+public class SerialUtil {
+
+ private SerialUtil() {}
+
+ /**
+ * Serializes the specified object and returns the result as a byte array.
+ * @param obj the object to serialize
+ * @return the serialized object
+ */
+ public static byte[] serialize(final Serializable obj) {
+ return serialize(new Serializable[] {obj});
+ }
+
+ /**
+ * Serializes the specified object and returns the result as a byte array.
+ * @param objs an array of objects to serialize
+ * @return the serialized object
+ */
+ public static byte[] serialize(final Serializable... objs) {
+ try {
+ final ByteArrayOutputStream bas = new ByteArrayOutputStream(8192);
+ final ObjectOutput oos = new ObjectOutputStream(bas);
+ for (final Object obj : objs) {
+ oos.writeObject(obj);
+ }
+ oos.flush();
+ return bas.toByteArray();
+ } catch (final Exception ex) {
+ throw new IllegalStateException("Could not serialize", ex);
+ }
+ }
+
+ /**
+ * Deserialize an object from the specified byte array and returns the result.
+ * @param data byte array representing the serialized object
+ * @return the deserialized object
+ */
+ @SuppressWarnings("unchecked")
+ @SuppressFBWarnings("OBJECT_DESERIALIZATION")
+ public static T deserialize(final byte[] data) {
+ try {
+ final ObjectInputStream ois = getObjectInputStream(data);
+ return (T) ois.readObject();
+ } catch (final Exception ex) {
+ throw new IllegalStateException("Could not deserialize", ex);
+ }
+ }
+
+ /**
+ * Creates an {@link ObjectInputStream} adapted to the current Java version.
+ * @param data data to deserialize,
+ * @return an object input stream.
+ */
+ @SuppressFBWarnings("OBJECT_DESERIALIZATION")
+ public static ObjectInputStream getObjectInputStream(final byte[] data) throws IOException {
+ final ByteArrayInputStream bas = new ByteArrayInputStream(data);
+ return getObjectInputStream(bas);
+ }
+
+ /**
+ * Creates an {@link ObjectInputStream} adapted to the current Java version.
+ * @param stream stream of data to deserialize,
+ * @return an object input stream.
+ */
+ @SuppressFBWarnings("OBJECT_DESERIALIZATION")
+ public static ObjectInputStream getObjectInputStream(final InputStream stream) throws IOException {
+ return Constants.JAVA_MAJOR_VERSION == 8
+ ? new FilteredObjectInputStream(stream)
+ : new ObjectInputStream(stream);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java
new file mode 100644
index 00000000000..9989235165c
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/SetTestProperty.java
@@ -0,0 +1,60 @@
+/*
+ * 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.test.junit;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junitpioneer.jupiter.ReadsEnvironmentVariable;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+/**
+ * Registers a Log4j2 system property with the {@link TestPropertySource}. The
+ * property will also be available in configuration files using the
+ * {@code ${test:...} lookup.
+ *
+ */
+@Retention(RUNTIME)
+@Target({TYPE, METHOD})
+@Inherited
+@Documented
+@ExtendWith({ExtensionContextAnchor.class, TestPropertyResolver.class})
+@Repeatable(SetTestProperty.SetTestProperties.class)
+@ReadsSystemProperty
+@ReadsEnvironmentVariable
+public @interface SetTestProperty {
+
+ String key();
+
+ String value();
+
+ @Retention(RUNTIME)
+ @Target({TYPE, METHOD})
+ @Documented
+ @Inherited
+ @interface SetTestProperties {
+
+ SetTestProperty[] value();
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusListenerExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusListenerExtension.java
new file mode 100644
index 00000000000..7ca5776c583
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusListenerExtension.java
@@ -0,0 +1,189 @@
+/*
+ * 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.test.junit;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.status.StatusListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
+import org.junit.jupiter.api.extension.ExtensionContextException;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
+import org.junit.platform.commons.support.HierarchyTraversalMode;
+import org.junit.platform.commons.support.ModifierSupport;
+import org.junit.platform.commons.support.ReflectionSupport;
+
+class StatusListenerExtension extends TypeBasedParameterResolver
+ implements BeforeAllCallback, BeforeEachCallback, TestExecutionExceptionHandler {
+
+ private static final Object KEY = ListStatusListener.class;
+
+ public StatusListenerExtension() {
+ super(ListStatusListener.class);
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ // Stores the per-class status listener to catch the messages caused by other
+ // `beforeAll` methods and extensions.
+ final ListStatusListenerHolder holder = new ListStatusListenerHolder(context, null);
+ ExtensionContextAnchor.setAttribute(KEY, holder, context);
+ ReflectionSupport.findFields(
+ context.getRequiredTestClass(),
+ f -> ModifierSupport.isStatic(f) && f.getType().equals(ListStatusListener.class),
+ HierarchyTraversalMode.TOP_DOWN)
+ .forEach(f -> {
+ try {
+ f.setAccessible(true);
+ f.set(null, holder.getStatusListener());
+ } catch (final ReflectiveOperationException e) {
+ throw new ExtensionContextException("Failed to inject field.", e);
+ }
+ });
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ // Retrieves the per-class status listener
+ final ListStatusListenerHolder parentHolder =
+ ExtensionContextAnchor.getAttribute(KEY, ListStatusListenerHolder.class, context);
+ final ListStatusListener parent = parentHolder != null ? parentHolder.getStatusListener() : null;
+ final ListStatusListenerHolder holder = new ListStatusListenerHolder(context, parent);
+ ExtensionContextAnchor.setAttribute(KEY, holder, context);
+ ReflectionSupport.findFields(
+ context.getRequiredTestClass(),
+ f -> ModifierSupport.isNotStatic(f) && f.getType().equals(ListStatusListener.class),
+ HierarchyTraversalMode.TOP_DOWN)
+ .forEach(f -> {
+ try {
+ f.setAccessible(true);
+ f.set(context.getRequiredTestInstance(), holder.getStatusListener());
+ } catch (final ReflectiveOperationException e) {
+ throw new ExtensionContextException("Failed to inject field.", e);
+ }
+ });
+ }
+
+ @Override
+ public void handleTestExecutionException(final ExtensionContext context, final Throwable throwable)
+ throws Throwable {
+ final ListStatusListenerHolder holder =
+ ExtensionContextAnchor.getAttribute(KEY, ListStatusListenerHolder.class, context);
+ if (holder != null) {
+ holder.handleException(context, throwable);
+ }
+ throw throwable;
+ }
+
+ @Override
+ public ListStatusListener resolveParameter(
+ final ParameterContext parameterContext, final ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ final ListStatusListenerHolder holder =
+ ExtensionContextAnchor.getAttribute(KEY, ListStatusListenerHolder.class, extensionContext);
+ return holder.getStatusListener();
+ }
+
+ private static class ListStatusListenerHolder implements CloseableResource {
+
+ private final StatusLogger statusLogger;
+ private final ListStatusListener statusListener;
+
+ public ListStatusListenerHolder(final ExtensionContext context, final ListStatusListener parent) {
+ this.statusLogger = StatusLogger.getLogger();
+ this.statusListener = new JUnitListStatusListener(context, parent);
+ statusLogger.registerListener(statusListener);
+ }
+
+ public ListStatusListener getStatusListener() {
+ return statusListener;
+ }
+
+ @Override
+ public void close() {
+ statusLogger.removeListener(statusListener);
+ }
+
+ public void handleException(final ExtensionContext context, final Throwable throwable) {
+ final StatusListener listener = new StatusConsoleListener(Level.ALL, System.err);
+ listener.log(new StatusData(
+ null,
+ Level.ERROR,
+ new ParameterizedMessage("Test `{}` has failed, dumping status data...", context.getDisplayName()),
+ throwable,
+ null));
+ statusListener.getStatusData().forEach(listener::log);
+ }
+ }
+
+ private static class JUnitListStatusListener implements ListStatusListener {
+
+ private final ExtensionContext context;
+ private final ListStatusListener parent;
+ private final ArrayList statusData = new ArrayList<>();
+
+ public JUnitListStatusListener(final ExtensionContext context, final ListStatusListener parent) {
+ this.context = context;
+ this.parent = parent;
+ }
+
+ @Override
+ public void log(final StatusData data) {
+ if (context.equals(ExtensionContextAnchor.getContext())) {
+ synchronized (statusData) {
+ statusData.add(data);
+ }
+ }
+ }
+
+ @Override
+ public Level getStatusLevel() {
+ return Level.DEBUG;
+ }
+
+ @Override
+ public void close() {
+ // NOP
+ }
+
+ @Override
+ public Stream getStatusData() {
+ synchronized (statusData) {
+ final List clone = new ArrayList<>(statusData);
+ return parent != null ? Stream.concat(parent.getStatusData(), clone.stream()) : clone.stream();
+ }
+ }
+
+ @Override
+ public void clear() {
+ synchronized (statusData) {
+ statusData.clear();
+ }
+ }
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevel.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevel.java
new file mode 100644
index 00000000000..ffeeee07a99
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevel.java
@@ -0,0 +1,42 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/**
+ * JUnit 5 test extension that sets a specific StatusLogger logging level for each test.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Inherited
+@ExtendWith(StatusLoggerLevelExtension.class)
+@ResourceLock("log4j2.StatusLogger")
+public @interface StatusLoggerLevel {
+ /** Name of {@link org.apache.logging.log4j.Level} to use for status logger. */
+ String value();
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevelExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevelExtension.java
new file mode 100644
index 00000000000..54534ed9dca
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerLevelExtension.java
@@ -0,0 +1,48 @@
+/*
+ * 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.test.junit;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class StatusLoggerLevelExtension implements BeforeEachCallback, AfterEachCallback {
+
+ private static final String KEY = "previousLevel";
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ final StatusLoggerLevel annotation = context.getRequiredTestClass().getAnnotation(StatusLoggerLevel.class);
+ if (annotation == null) {
+ return;
+ }
+ final StatusLogger logger = StatusLogger.getLogger();
+ getStore(context).put(KEY, logger.getLevel());
+ logger.setLevel(Level.valueOf(annotation.value()));
+ }
+
+ @Override
+ public void afterEach(final ExtensionContext context) throws Exception {
+ StatusLogger.getLogger().setLevel(getStore(context).get(KEY, Level.class));
+ }
+
+ private ExtensionContext.Store getStore(final ExtensionContext context) {
+ return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestInstance()));
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerMockExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerMockExtension.java
new file mode 100644
index 00000000000..2725dc7314a
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerMockExtension.java
@@ -0,0 +1,75 @@
+/*
+ * 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.test.junit;
+
+import static org.apache.logging.log4j.test.junit.ExtensionContextAnchor.getAttribute;
+import static org.apache.logging.log4j.test.junit.ExtensionContextAnchor.setAttribute;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import org.apache.logging.log4j.status.StatusConsoleListener;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+/**
+ * Replaces {@link StatusLogger} static instance with a mocked one.
+ *
+ * Warning!
+ * Many classes store the result of {@link StatusLogger#getLogger()} in {@code static} field.
+ * Hence, the mock replacement must be performed before anybody tries to access it.
+ * Similarly, we cannot replace the mock in between tests, since it is already stored in {@code static} fields.
+ * That is why we only reset the mocked instance before each test.
+ *
+ *
+ * @see UsingStatusLoggerMock
+ */
+class StatusLoggerMockExtension implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback {
+
+ private static final String KEY_PREFIX = StatusLoggerMockExtension.class.getSimpleName() + '.';
+
+ private static final String INITIAL_STATUS_LOGGER_KEY = KEY_PREFIX + "initialStatusLogger";
+
+ @Override
+ public void beforeAll(final ExtensionContext context) throws Exception {
+ setAttribute(INITIAL_STATUS_LOGGER_KEY, StatusLogger.getLogger(), context);
+ final StatusLogger statusLogger = mock(StatusLogger.class);
+ stubFallbackListener(statusLogger);
+ StatusLogger.setLogger(statusLogger);
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ final StatusLogger statusLogger = StatusLogger.getLogger();
+ reset(statusLogger); // Stubs get reset too!
+ stubFallbackListener(statusLogger);
+ }
+
+ private static void stubFallbackListener(final StatusLogger statusLogger) {
+ final StatusConsoleListener fallbackListener = mock(StatusConsoleListener.class);
+ when(statusLogger.getFallbackListener()).thenReturn(fallbackListener);
+ }
+
+ @Override
+ public void afterAll(final ExtensionContext context) {
+ final StatusLogger statusLogger = getAttribute(INITIAL_STATUS_LOGGER_KEY, StatusLogger.class, context);
+ StatusLogger.setLogger(statusLogger);
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/StatusLoggerRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerRule.java
similarity index 76%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/StatusLoggerRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerRule.java
index 11f945a4333..2e808adca13 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/StatusLoggerRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/StatusLoggerRule.java
@@ -1,20 +1,20 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.status.StatusLogger;
@@ -25,7 +25,9 @@
* Log4j configuration file.
*
* @since 2.8
+ * @deprecated Use {@link StatusLoggerLevel} with JUnit 5
*/
+@Deprecated
public class StatusLoggerRule extends ExternalResource {
private final StatusLogger statusLogger = StatusLogger.getLogger();
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TempLoggingDir.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TempLoggingDir.java
new file mode 100644
index 00000000000..dab49ddd4d2
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TempLoggingDir.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.junit;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.CleanupMode;
+
+/**
+ * Injects the given static field with a per test logging directory.
+ *
+ * The same directory is set as "logging.path" Log4j2 property.
+ *
+ */
+@Retention(RUNTIME)
+@Target({FIELD, PARAMETER})
+@Inherited
+@Documented
+@ExtendWith({ExtensionContextAnchor.class, TempLoggingDirectory.class})
+public @interface TempLoggingDir {
+
+ CleanupMode cleanup() default CleanupMode.DEFAULT;
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TempLoggingDirectory.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TempLoggingDirectory.java
new file mode 100644
index 00000000000..1ba5a16442e
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TempLoggingDirectory.java
@@ -0,0 +1,214 @@
+/*
+ * 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.test.junit;
+
+import static org.junit.jupiter.api.io.CleanupMode.NEVER;
+import static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.TestProperties;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
+import org.junit.jupiter.api.extension.ExtensionContextException;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.junit.jupiter.api.io.CleanupMode;
+import org.junit.platform.commons.PreconditionViolationException;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.junit.platform.commons.support.ModifierSupport;
+
+public class TempLoggingDirectory implements BeforeAllCallback, BeforeEachCallback, ParameterResolver {
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ final List fields = AnnotationSupport.findAnnotatedFields(
+ context.getRequiredTestClass(), TempLoggingDir.class, ModifierSupport::isStatic);
+ Path loggingPath = null;
+ for (final Field field : fields) {
+ if (loggingPath != null) {
+ throw new PreconditionViolationException(
+ "Multiple static fields with @TempLoggingDir annotation are not supported.");
+ } else {
+ final CleanupMode cleanup = determineCleanupMode(field);
+ loggingPath = createLoggingPath(context, cleanup).getPath();
+ }
+ field.setAccessible(true);
+ field.set(null, loggingPath);
+ }
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ // JUnit 5 does not set an error on the parent context if one of the children fails.
+ // We record the list of children.
+ context.getParent().ifPresent(c -> {
+ final PathHolder holder = ExtensionContextAnchor.getAttribute(PathHolder.class, PathHolder.class, c);
+ if (holder != null) {
+ holder.addContext(context);
+ }
+ });
+ // Inject fields
+ final List fields = AnnotationSupport.findAnnotatedFields(
+ context.getRequiredTestClass(), TempLoggingDir.class, ModifierSupport::isNotStatic);
+ Path loggingPath = null;
+ final Object instance = context.getRequiredTestInstance();
+ for (final Field field : fields) {
+ if (loggingPath != null) {
+ throw new PreconditionViolationException(
+ "Multiple instance fields with @TempLoggingDir annotation are not supported.");
+ } else {
+ final CleanupMode cleanup = determineCleanupMode(field);
+ loggingPath = createLoggingPath(context, cleanup).getPath();
+ }
+ field.setAccessible(true);
+ field.set(instance, loggingPath);
+ }
+ }
+
+ @Override
+ public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ if (parameterContext.getParameter().getType().isAssignableFrom(Path.class)) {
+ return parameterContext.findAnnotation(TempLoggingDir.class).isPresent();
+ }
+ return false;
+ }
+
+ @Override
+ public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ final TempLoggingDir annotation = parameterContext
+ .findAnnotation(TempLoggingDir.class)
+ .orElseThrow(() -> new PreconditionViolationException(String.format(
+ "Missing `%s` annotation on parameter `%s`",
+ TempLoggingDir.class.getSimpleName(), parameterContext)));
+ // Get or create a temporary directory
+ PathHolder holder = ExtensionContextAnchor.getAttribute(PathHolder.class, PathHolder.class, extensionContext);
+ if (holder == null || !extensionContext.equals(holder.getMainContext())) {
+ final CleanupMode mode = determineCleanupMode(annotation);
+ holder = createLoggingPath(extensionContext, mode);
+ }
+ return holder.getPath();
+ }
+
+ private PathHolder createLoggingPath(ExtensionContext context, CleanupMode cleanup) {
+ final TestProperties props = TestPropertySource.createProperties(context);
+ final Path perClassPath = determinePerClassPath(context);
+ // Per test subfolder
+ final Path loggingPath = context.getTestMethod()
+ .map(m -> perClassPath.resolve(m.getName()))
+ .orElse(perClassPath);
+ try {
+ Files.createDirectories(loggingPath);
+ } catch (final IOException e) {
+ throw new ExtensionContextException("Failed to create temporary directory.", e);
+ }
+ props.setProperty(TestProperties.LOGGING_PATH, loggingPath.toString());
+ // Register deletion
+ final PathHolder holder = new PathHolder(loggingPath, cleanup, context);
+ ExtensionContextAnchor.setAttribute(PathHolder.class, holder, context);
+ return holder;
+ }
+
+ private Path determinePerClassPath(ExtensionContext context) {
+ // Check if the parent context already created a folder
+ PathHolder holder = ExtensionContextAnchor.getAttribute(PathHolder.class, PathHolder.class, context);
+ if (holder == null) {
+ try {
+ // Create temporary per-class directory
+ final String baseDir = System.getProperty("basedir");
+ final Path basePath = (baseDir != null ? Paths.get(baseDir, "target") : Paths.get(".")).resolve("logs");
+ final Class> clazz = context.getRequiredTestClass();
+ final Package pkg = clazz.getPackage();
+ final String dir = pkg.getName()
+ .replaceAll("org\\.apache\\.(logging\\.)?log4j\\.", "")
+ .replaceAll("[.$]", File.separatorChar == '\\' ? "\\\\" : File.separator);
+ // Create a temporary directory that uses the simple class name as prefix
+ Path packagePath = basePath.resolve(dir);
+ Files.createDirectories(packagePath);
+ // Use a UNIX timestamp to (roughly) sort directories by execution time.
+ return Files.createTempDirectory(
+ packagePath,
+ String.format("%s_%08x_", clazz.getSimpleName(), System.currentTimeMillis() / 1000));
+ } catch (final IOException e) {
+ throw new ExtensionContextException("Failed to create temporary directory.", e);
+ }
+ }
+ return holder.getPath();
+ }
+
+ private CleanupMode determineCleanupMode(final TempLoggingDir annotation) {
+ final CleanupMode mode = annotation.cleanup();
+ // TODO: use JupiterConfiguration
+ return mode != CleanupMode.DEFAULT ? mode : CleanupMode.ON_SUCCESS;
+ }
+
+ private CleanupMode determineCleanupMode(final Field field) {
+ return determineCleanupMode(field.getAnnotation(TempLoggingDir.class));
+ }
+
+ private static class PathHolder implements CloseableResource {
+
+ private final Path path;
+ private final CleanupMode cleanupMode;
+ private final ExtensionContext mainContext;
+ private final Map contexts = new ConcurrentHashMap<>();
+
+ public PathHolder(final Path path, final CleanupMode cleanup, final ExtensionContext context) {
+ this.path = path;
+ this.cleanupMode = cleanup;
+ this.contexts.put(context, Boolean.TRUE);
+ this.mainContext = context;
+ }
+
+ public void addContext(final ExtensionContext context) {
+ this.contexts.put(context, Boolean.TRUE);
+ }
+
+ public Path getPath() {
+ return path;
+ }
+
+ public ExtensionContext getMainContext() {
+ return mainContext;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (cleanupMode == NEVER
+ || (cleanupMode == ON_SUCCESS
+ && contexts.keySet().stream().anyMatch(context -> context.getExecutionException()
+ .isPresent()))) {
+ StatusLogger.getLogger().debug("Skipping cleanup of directory {}.", path);
+ return;
+ }
+ DirectoryCleaner.deleteDirectory(path);
+ }
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java
new file mode 100644
index 00000000000..93af5c29122
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertyResolver.java
@@ -0,0 +1,86 @@
+/*
+ * 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.test.junit;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.junit.platform.commons.support.HierarchyTraversalMode;
+import org.junit.platform.commons.support.ModifierSupport;
+import org.junit.platform.commons.support.ReflectionSupport;
+
+public class TestPropertyResolver extends TypeBasedParameterResolver
+ implements BeforeAllCallback, BeforeEachCallback {
+
+ public TestPropertyResolver() {
+ super(TestProperties.class);
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ final TestProperties props = TestPropertySource.createProperties(context);
+ AnnotationSupport.findRepeatableAnnotations(context.getRequiredTestMethod(), SetTestProperty.class)
+ .forEach(setProperty -> props.setProperty(setProperty.key(), setProperty.value()));
+ final Class> testClass = context.getRequiredTestClass();
+ final Object testInstance = context.getRequiredTestInstance();
+ ReflectionSupport.findFields(
+ testClass,
+ field -> ModifierSupport.isNotStatic(field)
+ && field.getType().equals(TestProperties.class),
+ HierarchyTraversalMode.BOTTOM_UP)
+ .forEach(field -> {
+ try {
+ field.setAccessible(true);
+ field.set(testInstance, props);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ });
+ }
+
+ @Override
+ public void beforeAll(final ExtensionContext context) throws Exception {
+ final TestProperties props = TestPropertySource.createProperties(context);
+ AnnotationSupport.findRepeatableAnnotations(context.getRequiredTestClass(), SetTestProperty.class)
+ .forEach(setProperty -> props.setProperty(setProperty.key(), setProperty.value()));
+ final Class> testClass = context.getRequiredTestClass();
+ ReflectionSupport.findFields(
+ testClass,
+ field -> ModifierSupport.isStatic(field)
+ && field.getType().equals(TestProperties.class),
+ HierarchyTraversalMode.BOTTOM_UP)
+ .forEach(field -> {
+ try {
+ field.setAccessible(true);
+ field.set(null, props);
+ } catch (IllegalAccessException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ });
+ }
+
+ @Override
+ public TestProperties resolveParameter(
+ final ParameterContext parameterContext, final ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ return TestPropertySource.createProperties(extensionContext);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java
new file mode 100644
index 00000000000..c6057223ba6
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TestPropertySource.java
@@ -0,0 +1,138 @@
+/*
+ * 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.test.junit;
+
+import org.apache.logging.log4j.test.TestProperties;
+import org.apache.logging.log4j.util.PropertySource;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+
+public class TestPropertySource implements PropertySource {
+
+ private static final String PREFIX = "log4j2.";
+ private static final Namespace NAMESPACE = ExtensionContextAnchor.LOG4J2_NAMESPACE.append("properties");
+ private static final TestProperties EMPTY_PROPERTIES = new EmptyTestProperties();
+
+ @Override
+ public int getPriority() {
+ // Highest priority
+ return Integer.MIN_VALUE;
+ }
+
+ public static TestProperties createProperties(final ExtensionContext context) {
+ TestProperties props = getProperties(context);
+ // Make sure that the properties do not come from the parent ExtensionContext
+ if (props instanceof JUnitTestProperties && context.equals(((JUnitTestProperties) props).getContext())) {
+ return props;
+ }
+ props = new JUnitTestProperties(context);
+ ExtensionContextAnchor.setAttribute(TestProperties.class, props, context);
+ return props;
+ }
+
+ public static TestProperties getProperties() {
+ return getProperties(null);
+ }
+
+ private static TestProperties getProperties(final ExtensionContext context) {
+ final ExtensionContext actualContext = context != null ? context : ExtensionContextAnchor.getContext();
+ if (actualContext != null) {
+ final TestProperties props =
+ ExtensionContextAnchor.getAttribute(TestProperties.class, TestProperties.class, actualContext);
+ if (props != null) {
+ return props;
+ }
+ }
+ return EMPTY_PROPERTIES;
+ }
+
+ @Override
+ public CharSequence getNormalForm(final Iterable extends CharSequence> tokens) {
+ final CharSequence camelCase = Util.joinAsCamelCase(tokens);
+ // Do not use Strings to prevent recursive initialization
+ return camelCase.length() > 0 ? PREFIX + camelCase.toString() : null;
+ }
+
+ @Override
+ public String getProperty(final String key) {
+ return getProperties().getProperty(key);
+ }
+
+ @Override
+ public boolean containsProperty(final String key) {
+ return getProperties().containsProperty(key);
+ }
+
+ private static class JUnitTestProperties implements TestProperties {
+
+ private final ExtensionContext context;
+ private final Store store;
+
+ public JUnitTestProperties(final ExtensionContext context) {
+ this.context = context;
+ this.store = context.getStore(NAMESPACE);
+ }
+
+ public ExtensionContext getContext() {
+ return context;
+ }
+
+ @Override
+ public String getProperty(final String key) {
+ return store.get(key, String.class);
+ }
+
+ @Override
+ public boolean containsProperty(final String key) {
+ return getProperty(key) != null;
+ }
+
+ @Override
+ public void setProperty(final String key, final String value) {
+ store.put(key, value);
+ }
+
+ @Override
+ public void clearProperty(final String key) {
+ store.remove(key, String.class);
+ }
+ }
+
+ private static class EmptyTestProperties implements TestProperties {
+
+ @Override
+ public String getProperty(final String key) {
+ return null;
+ }
+
+ @Override
+ public boolean containsProperty(final String key) {
+ return false;
+ }
+
+ @Override
+ public void setProperty(final String key, final String value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clearProperty(final String key) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextExtension.java
new file mode 100644
index 00000000000..5dc22ed2857
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextExtension.java
@@ -0,0 +1,56 @@
+/*
+ * 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.test.junit;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.test.ThreadContextHolder;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class ThreadContextExtension implements BeforeEachCallback, AfterEachCallback {
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ final Class> testClass = context.getRequiredTestClass();
+ final ThreadContextHolder holder;
+ if (testClass.isAnnotationPresent(UsingAnyThreadContext.class)) {
+ holder = new ThreadContextHolder(true, true);
+ ThreadContext.clearAll();
+ } else if (testClass.isAnnotationPresent(UsingThreadContextMap.class)) {
+ holder = new ThreadContextHolder(true, false);
+ ThreadContext.clearMap();
+ } else if (testClass.isAnnotationPresent(UsingThreadContextStack.class)) {
+ holder = new ThreadContextHolder(false, true);
+ ThreadContext.clearStack();
+ } else {
+ return;
+ }
+ getStore(context).put(ThreadContextHolder.class, holder);
+ }
+
+ @Override
+ public void afterEach(final ExtensionContext context) throws Exception {
+ final ThreadContextHolder holder = getStore(context).get(ThreadContextHolder.class, ThreadContextHolder.class);
+ if (holder != null) {
+ holder.restore();
+ }
+ }
+
+ private ExtensionContext.Store getStore(final ExtensionContext context) {
+ return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestInstance()));
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
new file mode 100644
index 00000000000..8afceef7378
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextInitializer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.test.junit;
+
+import org.apache.logging.log4j.ThreadContext;
+import org.apache.logging.log4j.test.ThreadContextUtilityClass;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
+import org.junit.platform.commons.support.AnnotationSupport;
+
+class ThreadContextInitializer implements BeforeAllCallback, BeforeEachCallback {
+
+ @Override
+ public void beforeAll(final ExtensionContext context) throws Exception {
+ if (AnnotationSupport.isAnnotated(context.getRequiredTestClass(), InitializesThreadContext.class)) {
+ resetThreadContext(context);
+ }
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ if (AnnotationSupport.isAnnotated(context.getRequiredTestMethod(), InitializesThreadContext.class)) {
+ resetThreadContext(context);
+ }
+ }
+
+ private void resetThreadContext(final ExtensionContext context) {
+ ThreadContextUtilityClass.reset();
+ // We use `CloseableResource` instead of `afterAll` to reset the
+ // ThreadContextFactory
+ // *after* the `@SetSystemProperty` extension has restored the properties
+ ExtensionContextAnchor.setAttribute(
+ ThreadContext.class,
+ new CloseableResource() {
+ @Override
+ public void close() throws Throwable {
+ ThreadContextUtilityClass.reset();
+ }
+ },
+ context);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java
new file mode 100644
index 00000000000..3afab2f9d48
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapExtension.java
@@ -0,0 +1,44 @@
+/*
+ * 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.test.junit;
+
+import java.util.Map;
+import org.apache.logging.log4j.ThreadContext;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+class ThreadContextMapExtension implements BeforeEachCallback {
+ private static final class ThreadContextMapStore implements ExtensionContext.Store.CloseableResource {
+ private final Map previousMap = ThreadContext.getImmutableContext();
+
+ private ThreadContextMapStore() {
+ ThreadContext.clearMap();
+ }
+
+ @Override
+ public void close() throws Throwable {
+ // TODO LOG4J2-1517 Add ThreadContext.setContext(Map)
+ ThreadContext.clearMap();
+ ThreadContext.putAll(previousMap);
+ }
+ }
+
+ @Override
+ public void beforeEach(final ExtensionContext context) throws Exception {
+ context.getStore(ExtensionContextAnchor.LOG4J2_NAMESPACE).getOrComputeIfAbsent(ThreadContextMapStore.class);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapRule.java
new file mode 100644
index 00000000000..94986a26e62
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextMapRule.java
@@ -0,0 +1,40 @@
+/*
+ * 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.test.junit;
+
+/**
+ * Restores the ThreadContext to it's initial map values after a JUnit test.
+ *
+ * Usage:
+ *
+ *
+ * @Rule
+ * public final ThreadContextMapRule threadContextRule = new ThreadContextMapRule();
+ *
+ *
+ * @deprecated use {@link UsingThreadContextMap} with JUnit 5
+ */
+@Deprecated
+public class ThreadContextMapRule extends ThreadContextRule {
+
+ /**
+ * Constructs an initialized instance.
+ */
+ public ThreadContextMapRule() {
+ super(true, false);
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextRule.java
similarity index 80%
rename from log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextRule.java
rename to log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextRule.java
index dad7b5b32bf..d8c870781b4 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/junit/ThreadContextRule.java
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextRule.java
@@ -1,35 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-package org.apache.logging.log4j.junit;
+package org.apache.logging.log4j.test.junit;
import org.apache.logging.log4j.ThreadContext;
-import org.apache.logging.log4j.ThreadContextHolder;
+import org.apache.logging.log4j.test.ThreadContextHolder;
import org.junit.rules.ExternalResource;
/**
* Restores the ThreadContext to it's initial map and stack values after a JUnit test.
- *
+ *
* Usage:
- *
+ *
*
* @Rule
* public final ThreadContextRule threadContextRule = new ThreadContextRule();
*
+ *
+ * @deprecated use {@link UsingAnyThreadContext} with JUnit 5
*/
+@Deprecated
public class ThreadContextRule extends ExternalResource {
private final boolean restoreMap;
@@ -45,14 +48,13 @@ public ThreadContextRule() {
/**
* Constructs an instance initialized to restore the given structures.
- *
+ *
* @param restoreMap
* Whether to restore the thread context map.
* @param restoreStack
* Whether to restore the thread context stack.
*/
public ThreadContextRule(final boolean restoreMap, final boolean restoreStack) {
- super();
this.restoreMap = restoreMap;
this.restoreStack = restoreStack;
}
@@ -74,5 +76,4 @@ protected void before() throws Throwable {
ThreadContext.clearStack();
}
}
-
}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackRule.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackRule.java
new file mode 100644
index 00000000000..452c322a52d
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/ThreadContextStackRule.java
@@ -0,0 +1,39 @@
+/*
+ * 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.test.junit;
+
+/**
+ * Restores the ThreadContext to it's initial stack values after a JUnit test.
+ *
+ * Usage:
+ *
+ *
+ * @Rule
+ * public final ThreadContextStackRule threadContextRule = new ThreadContextStackRule();
+ *
+ * @deprecated use {@link UsingThreadContextStack} with JUnit 5
+ */
+@Deprecated
+public class ThreadContextStackRule extends ThreadContextRule {
+
+ /**
+ * Constructs an initialized instance.
+ */
+ public ThreadContextStackRule() {
+ super(false, true);
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TypeBasedParameterResolver.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TypeBasedParameterResolver.java
new file mode 100644
index 00000000000..970c1aff73c
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/TypeBasedParameterResolver.java
@@ -0,0 +1,43 @@
+/*
+ * 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.test.junit;
+
+import java.lang.reflect.Type;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+
+public abstract class TypeBasedParameterResolver implements ParameterResolver {
+
+ private final Type supportedParameterType;
+
+ public TypeBasedParameterResolver(final Type supportedParameterType) {
+ this.supportedParameterType = supportedParameterType;
+ }
+
+ @Override
+ public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext)
+ throws ParameterResolutionException {
+ return this.supportedParameterType.equals(
+ parameterContext.getParameter().getParameterizedType());
+ }
+
+ @Override
+ public abstract T resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+ throws ParameterResolutionException;
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingAnyThreadContext.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingAnyThreadContext.java
new file mode 100644
index 00000000000..c67a9a1994a
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingAnyThreadContext.java
@@ -0,0 +1,42 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+/**
+ * Marks a test class as using {@link org.apache.logging.log4j.ThreadContext} APIs. This will automatically clear and restore
+ * both the thread context map and stack for each test invocation.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Inherited
+@ExtendWith(ThreadContextExtension.class)
+@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+public @interface UsingAnyThreadContext {}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusListener.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusListener.java
new file mode 100644
index 00000000000..7fa198c509b
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusListener.java
@@ -0,0 +1,39 @@
+/*
+ * 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.test.junit;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.apache.logging.log4j.status.StatusListener;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * Configures and injects a {@link StatusListener} of type
+ * {@link ListStatusListener}, that will collect status messages for the test
+ * context.
+ */
+@Retention(RUNTIME)
+@Target({TYPE, METHOD})
+@Documented
+@ExtendWith({ExtensionContextAnchor.class, TestPropertyResolver.class, StatusListenerExtension.class})
+public @interface UsingStatusListener {}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusLoggerMock.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusLoggerMock.java
new file mode 100644
index 00000000000..bcada66f7c9
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingStatusLoggerMock.java
@@ -0,0 +1,37 @@
+/*
+ * 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.test.junit;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+/**
+ * Shortcut to {@link StatusLoggerMockExtension}.
+ */
+@Retention(RUNTIME)
+@Target({TYPE, METHOD})
+@Documented
+@ExtendWith({ExtensionContextAnchor.class, StatusLoggerMockExtension.class})
+@ResourceLock("log4j2.StatusLogger")
+public @interface UsingStatusLoggerMock {}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java
new file mode 100644
index 00000000000..6d66dd4e032
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingTestProperties.java
@@ -0,0 +1,43 @@
+/*
+ * 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.test.junit;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import org.apache.logging.log4j.test.TestProperties;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junitpioneer.jupiter.ReadsEnvironmentVariable;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+/**
+ * A field or method parameter of type {@link TestProperties} will be injected with a per-test source of Log4j2's
+ * system properties.
+ */
+@Retention(RUNTIME)
+@Target({TYPE, METHOD})
+@Inherited
+@Documented
+@ExtendWith({ExtensionContextAnchor.class, TestPropertyResolver.class})
+@ReadsSystemProperty
+@ReadsEnvironmentVariable
+public @interface UsingTestProperties {}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
new file mode 100644
index 00000000000..5876af72aa6
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextMap.java
@@ -0,0 +1,43 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+/**
+ * Marks a test class as using {@link org.apache.logging.log4j.spi.ThreadContextMap} APIs. This will automatically clear and
+ * restore the thread context map (MDC) for each test invocation.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Documented
+@Inherited
+@ExtendWith(ThreadContextMapExtension.class)
+@ReadsSystemProperty
+@ResourceLock(value = Log4jStaticResources.THREAD_CONTEXT, mode = ResourceAccessMode.READ)
+public @interface UsingThreadContextMap {}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextStack.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextStack.java
new file mode 100644
index 00000000000..309dc1ce642
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/UsingThreadContextStack.java
@@ -0,0 +1,42 @@
+/*
+ * 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.test.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+/**
+ * Marks a test class as using {@link org.apache.logging.log4j.spi.ThreadContextStack} APIs. This will automatically clear and
+ * restore the thread context stack (NDC) for each test invocation.
+ *
+ * @since 2.14.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+@Inherited
+@ExtendWith(ThreadContextExtension.class)
+@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+public @interface UsingThreadContextStack {}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java
new file mode 100644
index 00000000000..2a37b2933ff
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/junit/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.24.1")
+package org.apache.logging.log4j.test.junit;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/package-info.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/package-info.java
new file mode 100644
index 00000000000..5a0ebc45101
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.21.1")
+package org.apache.logging.log4j.test;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/spi/ThreadContextMapSuite.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/spi/ThreadContextMapSuite.java
new file mode 100644
index 00000000000..23bb42d483a
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/spi/ThreadContextMapSuite.java
@@ -0,0 +1,139 @@
+/*
+ * 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.test.spi;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.apache.logging.log4j.spi.ThreadContextMap;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+
+/**
+ * Provides test cases to apply to all implementations of {@link ThreadContextMap}.
+ * @since 2.24.0
+ */
+@Execution(ExecutionMode.CONCURRENT)
+public abstract class ThreadContextMapSuite {
+
+ private static final String KEY = "key";
+
+ /**
+ * Checks if the context map does not propagate to other threads by default.
+ */
+ protected static void threadLocalNotInheritableByDefault(final ThreadContextMap contextMap) {
+ contextMap.put(KEY, "threadLocalNotInheritableByDefault");
+ verifyThreadContextValueFromANewThread(contextMap, null);
+ }
+
+ /**
+ * Checks if the context map can be configured to propagate to other threads.
+ */
+ protected static void threadLocalInheritableIfConfigured(final ThreadContextMap contextMap) {
+ contextMap.put(KEY, "threadLocalInheritableIfConfigured");
+ verifyThreadContextValueFromANewThread(contextMap, "threadLocalInheritableIfConfigured");
+ }
+
+ /**
+ * Checks basic put/remove pattern.
+ */
+ protected static void singleValue(final ThreadContextMap contextMap) {
+ assertThat(contextMap.isEmpty()).as("Map is empty").isTrue();
+ contextMap.put(KEY, "testPut");
+ assertThat(contextMap.isEmpty()).as("Map is not empty").isFalse();
+ assertThat(contextMap.containsKey(KEY)).as("Map key exists").isTrue();
+ assertThat(contextMap.get(KEY)).as("Map contains expected value").isEqualTo("testPut");
+ contextMap.remove(KEY);
+ assertThat(contextMap.isEmpty()).as("Map is empty").isTrue();
+ }
+
+ /**
+ * Checks mutable copy
+ */
+ protected static void getCopyReturnsMutableCopy(final ThreadContextMap contextMap) {
+ contextMap.put(KEY, "testGetCopyReturnsMutableCopy");
+
+ final Map copy = contextMap.getCopy();
+ assertThat(copy).as("Copy contains same value").containsExactly(entry(KEY, "testGetCopyReturnsMutableCopy"));
+
+ copy.put(KEY, "testGetCopyReturnsMutableCopy2");
+ assertThat(contextMap.get(KEY))
+ .as("Original map is not affected by changes in the copy")
+ .isEqualTo("testGetCopyReturnsMutableCopy");
+
+ contextMap.clear();
+ assertThat(contextMap.isEmpty()).as("Original map is empty").isTrue();
+ assertThat(copy)
+ .as("Copy is not affected by changes in the map.")
+ .containsExactly(entry(KEY, "testGetCopyReturnsMutableCopy2"));
+ }
+
+ /**
+ * The immutable copy must be {@code null} if the map is empty.
+ */
+ protected static void getImmutableMapReturnsNullIfEmpty(final ThreadContextMap contextMap) {
+ assertThat(contextMap.isEmpty()).as("Original map is empty").isTrue();
+ assertThat(contextMap.getImmutableMapOrNull())
+ .as("Immutable copy is null")
+ .isNull();
+ }
+
+ /**
+ * The result of {@link ThreadContextMap#getImmutableMapOrNull()} must be immutable.
+ */
+ protected static void getImmutableMapReturnsImmutableMapIfNonEmpty(final ThreadContextMap contextMap) {
+ contextMap.put(KEY, "getImmutableMapReturnsImmutableMapIfNonEmpty");
+
+ final Map immutable = contextMap.getImmutableMapOrNull();
+ assertThat(immutable)
+ .as("Immutable copy contains same value")
+ .containsExactly(entry(KEY, "getImmutableMapReturnsImmutableMapIfNonEmpty"));
+
+ assertThrows(
+ UnsupportedOperationException.class, () -> immutable.put(KEY, "getImmutableMapReturnsNullIfEmpty2"));
+ }
+
+ /**
+ * The immutable copy is not affected by changes to the original map.
+ */
+ protected static void getImmutableMapCopyNotAffectedByContextMapChanges(final ThreadContextMap contextMap) {
+ contextMap.put(KEY, "getImmutableMapCopyNotAffectedByContextMapChanges");
+
+ final Map immutable = contextMap.getImmutableMapOrNull();
+ contextMap.put(KEY, "getImmutableMapCopyNotAffectedByContextMapChanges2");
+ assertThat(immutable)
+ .as("Immutable copy contains the original value")
+ .containsExactly(entry(KEY, "getImmutableMapCopyNotAffectedByContextMapChanges"));
+ }
+
+ private static void verifyThreadContextValueFromANewThread(
+ final ThreadContextMap contextMap, final String expected) {
+ final ExecutorService executorService = Executors.newSingleThreadExecutor();
+ try {
+ assertThat(executorService.submit(() -> contextMap.get(KEY)))
+ .succeedsWithin(Duration.ofSeconds(1))
+ .isEqualTo(expected);
+ } finally {
+ executorService.shutdown();
+ }
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/util/OsgiServiceLocatorTest.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/util/OsgiServiceLocatorTest.java
new file mode 100644
index 00000000000..7c18f9003b7
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/util/OsgiServiceLocatorTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.test.util;
+
+import java.lang.invoke.MethodHandles;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.spi.Provider;
+import org.apache.logging.log4j.util.OsgiServiceLocator;
+import org.apache.logging.log4j.util.PropertySource;
+
+public class OsgiServiceLocatorTest {
+
+ /**
+ * Used by OSGI {@link AbstractLoadBundleTest} to preserve caller
+ * sensitivity.
+ *
+ * @return
+ */
+ public static Stream loadProviders() {
+ return OsgiServiceLocator.loadServices(Provider.class, MethodHandles.lookup());
+ }
+
+ public static Stream loadPropertySources() {
+ return OsgiServiceLocator.loadServices(PropertySource.class, MethodHandles.lookup());
+ }
+}
diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/util/package-info.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/util/package-info.java
new file mode 100644
index 00000000000..949af1b0527
--- /dev/null
+++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/util/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@Export
+@Version("2.20.1")
+package org.apache.logging.log4j.test.util;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource b/log4j-api-test/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
similarity index 87%
rename from log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
rename to log4j-api-test/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
index 39c959ca65a..2c7cb498d92 100644
--- a/log4j-api/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
+++ b/log4j-api-test/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource
@@ -12,5 +12,4 @@
# 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.
-org.apache.logging.log4j.util.EnvironmentPropertySource
-org.apache.logging.log4j.util.SystemPropertiesPropertySource
\ No newline at end of file
+org.apache.logging.log4j.test.junit.TestPropertySource
diff --git a/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
new file mode 100644
index 00000000000..ca7ce84edd3
--- /dev/null
+++ b/log4j-api-test/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension
@@ -0,0 +1,15 @@
+# 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.
+org.apache.logging.log4j.test.junit.ExtensionContextAnchor
\ No newline at end of file
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java
new file mode 100644
index 00000000000..79a8fd75dd8
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/AbstractLoggerTest.java
@@ -0,0 +1,1459 @@
+/*
+ * 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;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.List;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.AbstractLogger;
+import org.apache.logging.log4j.spi.MessageFactory2Adapter;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.apache.logging.log4j.test.junit.Log4jStaticResources;
+import org.apache.logging.log4j.test.junit.StatusLoggerLevel;
+import org.apache.logging.log4j.util.Constants;
+import org.apache.logging.log4j.util.MessageSupplier;
+import org.apache.logging.log4j.util.Supplier;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+@StatusLoggerLevel("WARN")
+@ResourceLock(value = Log4jStaticResources.MARKER_MANAGER, mode = ResourceAccessMode.READ)
+@SetSystemProperty(key = "log4j2.status.entries", value = "200")
+@SetSystemProperty(key = "log4j2.StatusLogger.level", value = "WARN")
+class AbstractLoggerTest {
+
+ private static final StringBuilder CHAR_SEQ = new StringBuilder("CharSeq");
+
+ // TODO add proper tests for ReusableMessage
+
+ @SuppressWarnings("ThrowableInstanceNeverThrown")
+ private static final Throwable t = new UnsupportedOperationException("Test");
+
+ private static final Class obj = AbstractLogger.class;
+ private static final String pattern = "{}, {}";
+ private static final String p1 = "Long Beach";
+
+ private static final String p2 = "California";
+ private static final Message charSeq = new SimpleMessage(CHAR_SEQ);
+ private static final Message simple = new SimpleMessage("Hello");
+ private static final Message object = new ObjectMessage(obj);
+
+ private static final Message param = new ParameterizedMessage(pattern, p1, p2);
+
+ private final Marker MARKER = MarkerManager.getMarker("TEST");
+ private static final String MARKER_NAME = "TEST";
+
+ private static final LogEvent[] EVENTS = new LogEvent[] {
+ new LogEvent(null, simple, null),
+ new LogEvent(MARKER_NAME, simple, null),
+ new LogEvent(null, simple, t),
+ new LogEvent(MARKER_NAME, simple, t),
+ new LogEvent(null, object, null),
+ new LogEvent(MARKER_NAME, object, null),
+ new LogEvent(null, object, t),
+ new LogEvent(MARKER_NAME, object, t),
+ new LogEvent(null, param, null),
+ new LogEvent(MARKER_NAME, param, null),
+ new LogEvent(null, simple, null),
+ new LogEvent(null, simple, t),
+ new LogEvent(MARKER_NAME, simple, null),
+ new LogEvent(MARKER_NAME, simple, t),
+ new LogEvent(MARKER_NAME, simple, null),
+ new LogEvent(null, charSeq, null),
+ new LogEvent(null, charSeq, t),
+ new LogEvent(MARKER_NAME, charSeq, null),
+ new LogEvent(MARKER_NAME, charSeq, t),
+ };
+
+ @Test
+ void testDebug() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.DEBUG);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.debug("Hello");
+ logger.debug((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.debug(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.debug("Hello", t);
+ logger.debug((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.debug(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.debug(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.debug(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.debug(obj, t);
+ logger.debug((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.debug(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.debug(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.debug(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.debug(simple);
+ logger.debug((Marker) null, simple);
+ logger.debug((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.debug(simple, t);
+ logger.debug((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.debug(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.debug(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.debug(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.debug(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.debug(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.debug(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.debug(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testError() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.ERROR);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.error("Hello");
+ logger.error((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.error(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.error("Hello", t);
+ logger.error((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.error(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.error(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.error(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.error(obj, t);
+ logger.error((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.error(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.error(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.error(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.error(simple);
+ logger.error((Marker) null, simple);
+ logger.error((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.error(simple, t);
+ logger.error((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.error(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.error(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.error(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.error(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.error(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.error(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.error(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testFatal() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.FATAL);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.fatal("Hello");
+ logger.fatal((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.fatal(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.fatal("Hello", t);
+ logger.fatal((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.fatal(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.fatal(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.fatal(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.fatal(obj, t);
+ logger.fatal((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.fatal(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.fatal(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.fatal(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.fatal(simple);
+ logger.fatal((Marker) null, simple);
+ logger.fatal((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.fatal(simple, t);
+ logger.fatal((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.fatal(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.fatal(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.fatal(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.fatal(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.fatal(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.fatal(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.fatal(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testInfo() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.INFO);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.info("Hello");
+ logger.info((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.info(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.info("Hello", t);
+ logger.info((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.info(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.info(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.info(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.info(obj, t);
+ logger.info((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.info(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.info(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.info(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.info(simple);
+ logger.info((Marker) null, simple);
+ logger.info((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.info(simple, t);
+ logger.info((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.info(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.info(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.info(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.info(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.info(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.info(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.info(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testLogDebug() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.DEBUG);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.DEBUG, "Hello");
+ logger.log(Level.DEBUG, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.DEBUG, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.DEBUG, "Hello", t);
+ logger.log(Level.DEBUG, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.DEBUG, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.DEBUG, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.DEBUG, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.DEBUG, obj, t);
+ logger.log(Level.DEBUG, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.DEBUG, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.DEBUG, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.DEBUG, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.DEBUG, simple);
+ logger.log(Level.DEBUG, (Marker) null, simple);
+ logger.log(Level.DEBUG, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.DEBUG, simple, t);
+ logger.log(Level.DEBUG, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.DEBUG, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.DEBUG, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.DEBUG, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.DEBUG, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.DEBUG, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.DEBUG, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.DEBUG, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testLogError() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.ERROR);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.ERROR, "Hello");
+ logger.log(Level.ERROR, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.ERROR, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.ERROR, "Hello", t);
+ logger.log(Level.ERROR, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.ERROR, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.ERROR, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.ERROR, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.ERROR, obj, t);
+ logger.log(Level.ERROR, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.ERROR, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.ERROR, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.ERROR, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.ERROR, simple);
+ logger.log(Level.ERROR, (Marker) null, simple);
+ logger.log(Level.ERROR, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.ERROR, simple, t);
+ logger.log(Level.ERROR, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.ERROR, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.ERROR, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.ERROR, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.ERROR, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.ERROR, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.ERROR, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.ERROR, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testLogFatal() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.FATAL);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.FATAL, "Hello");
+ logger.log(Level.FATAL, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.FATAL, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.FATAL, "Hello", t);
+ logger.log(Level.FATAL, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.FATAL, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.FATAL, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.FATAL, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.FATAL, obj, t);
+ logger.log(Level.FATAL, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.FATAL, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.FATAL, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.FATAL, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.FATAL, simple);
+ logger.log(Level.FATAL, (Marker) null, simple);
+ logger.log(Level.FATAL, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.FATAL, simple, t);
+ logger.log(Level.FATAL, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.FATAL, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.FATAL, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.FATAL, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.FATAL, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.FATAL, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.FATAL, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.FATAL, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testLogInfo() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.INFO);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.INFO, "Hello");
+ logger.log(Level.INFO, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.INFO, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.INFO, "Hello", t);
+ logger.log(Level.INFO, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.INFO, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.INFO, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.INFO, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.INFO, obj, t);
+ logger.log(Level.INFO, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.INFO, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.INFO, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.INFO, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.INFO, simple);
+ logger.log(Level.INFO, (Marker) null, simple);
+ logger.log(Level.INFO, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.INFO, simple, t);
+ logger.log(Level.INFO, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.INFO, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.INFO, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.INFO, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.INFO, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.INFO, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.INFO, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.INFO, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testLogTrace() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.TRACE);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.TRACE, "Hello");
+ logger.log(Level.TRACE, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.TRACE, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.TRACE, "Hello", t);
+ logger.log(Level.TRACE, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.TRACE, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.TRACE, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.TRACE, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.TRACE, obj, t);
+ logger.log(Level.TRACE, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.TRACE, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.TRACE, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.TRACE, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.TRACE, simple);
+ logger.log(Level.TRACE, (Marker) null, simple);
+ logger.log(Level.TRACE, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.TRACE, simple, t);
+ logger.log(Level.TRACE, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.TRACE, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.TRACE, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.TRACE, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.TRACE, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.TRACE, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.TRACE, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.TRACE, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testLogWarn() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.WARN);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.log(Level.WARN, "Hello");
+ logger.log(Level.WARN, (Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.log(Level.WARN, MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.log(Level.WARN, "Hello", t);
+ logger.log(Level.WARN, (Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.log(Level.WARN, MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.log(Level.WARN, obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.log(Level.WARN, MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.log(Level.WARN, obj, t);
+ logger.log(Level.WARN, (Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.log(Level.WARN, MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.log(Level.WARN, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.log(Level.WARN, MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.log(Level.WARN, simple);
+ logger.log(Level.WARN, (Marker) null, simple);
+ logger.log(Level.WARN, (Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.log(Level.WARN, simple, t);
+ logger.log(Level.WARN, (Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.log(Level.WARN, MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.log(Level.WARN, MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.log(Level.WARN, MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.log(Level.WARN, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.log(Level.WARN, CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.log(Level.WARN, MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.log(Level.WARN, MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testTrace() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.TRACE);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.trace("Hello");
+ logger.trace((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.trace(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.trace("Hello", t);
+ logger.trace((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.trace(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.trace(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.trace(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.trace(obj, t);
+ logger.trace((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.trace(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.trace(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.trace(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.trace(simple);
+ logger.trace((Marker) null, simple);
+ logger.trace((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.trace(simple, t);
+ logger.trace((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.trace(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.trace(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.trace(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.trace(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.trace(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.trace(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.trace(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testWarn() {
+ final CountingLogger logger = new CountingLogger();
+ logger.setCurrentLevel(Level.WARN);
+
+ logger.setCurrentEvent(EVENTS[0]);
+ logger.warn("Hello");
+ logger.warn((Marker) null, "Hello");
+ logger.setCurrentEvent(EVENTS[1]);
+ logger.warn(MARKER, "Hello");
+ logger.setCurrentEvent(EVENTS[2]);
+ logger.warn("Hello", t);
+ logger.warn((Marker) null, "Hello", t);
+ logger.setCurrentEvent(EVENTS[3]);
+ logger.warn(MARKER, "Hello", t);
+ logger.setCurrentEvent(EVENTS[4]);
+ logger.warn(obj);
+ logger.setCurrentEvent(EVENTS[5]);
+ logger.warn(MARKER, obj);
+ logger.setCurrentEvent(EVENTS[6]);
+ logger.warn(obj, t);
+ logger.warn((Marker) null, obj, t);
+ logger.setCurrentEvent(EVENTS[7]);
+ logger.warn(MARKER, obj, t);
+ logger.setCurrentEvent(EVENTS[8]);
+ logger.warn(pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[9]);
+ logger.warn(MARKER, pattern, p1, p2);
+ logger.setCurrentEvent(EVENTS[10]);
+ logger.warn(simple);
+ logger.warn((Marker) null, simple);
+ logger.warn((Marker) null, simple, null);
+ logger.setCurrentEvent(EVENTS[11]);
+ logger.warn(simple, t);
+ logger.warn((Marker) null, simple, t);
+ logger.setCurrentEvent(EVENTS[12]);
+ logger.warn(MARKER, simple, null);
+ logger.setCurrentEvent(EVENTS[13]);
+ logger.warn(MARKER, simple, t);
+ logger.setCurrentEvent(EVENTS[14]);
+ logger.warn(MARKER, simple);
+
+ logger.setCurrentEvent(EVENTS[15]);
+ logger.warn(CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[16]);
+ logger.warn(CHAR_SEQ, t);
+ logger.setCurrentEvent(EVENTS[17]);
+ logger.warn(MARKER, CHAR_SEQ);
+ logger.setCurrentEvent(EVENTS[18]);
+ logger.warn(MARKER, CHAR_SEQ, t);
+
+ assertEquals(4, logger.getCharSeqCount(), "log(CharSeq) invocations");
+ assertEquals(5, logger.getObjectCount(), "log(Object) invocations");
+ }
+
+ @Test
+ void testMessageWithThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(true);
+ final ThrowableMessage message = new ThrowableMessage(t);
+
+ logger.debug(message);
+ logger.error(message);
+ logger.fatal(message);
+ logger.info(message);
+ logger.trace(message);
+ logger.warn(message);
+ logger.log(Level.INFO, message);
+
+ logger.debug(MARKER, message);
+ logger.error(MARKER, message);
+ logger.fatal(MARKER, message);
+ logger.info(MARKER, message);
+ logger.trace(MARKER, message);
+ logger.warn(MARKER, message);
+ logger.log(Level.INFO, MARKER, message);
+ }
+
+ @Test
+ void testMessageWithoutThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ final ThrowableMessage message = new ThrowableMessage(null);
+
+ logger.debug(message);
+ logger.error(message);
+ logger.fatal(message);
+ logger.info(message);
+ logger.trace(message);
+ logger.warn(message);
+ logger.log(Level.INFO, message);
+
+ logger.debug(MARKER, message);
+ logger.error(MARKER, message);
+ logger.fatal(MARKER, message);
+ logger.info(MARKER, message);
+ logger.trace(MARKER, message);
+ logger.warn(MARKER, message);
+ logger.log(Level.INFO, MARKER, message);
+ }
+
+ @Test
+ void testMessageSupplierWithThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(true);
+ final ThrowableMessage message = new ThrowableMessage(t);
+ final MessageSupplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ void testMessageSupplierWithoutThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ final ThrowableMessage message = new ThrowableMessage(null);
+ final MessageSupplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ void testSupplierWithThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(true);
+ final ThrowableMessage message = new ThrowableMessage(t);
+ final Supplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ void testSupplierWithoutThrowable() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ final ThrowableMessage message = new ThrowableMessage(null);
+ final Supplier supplier = () -> message;
+
+ logger.debug(supplier);
+ logger.error(supplier);
+ logger.fatal(supplier);
+ logger.info(supplier);
+ logger.trace(supplier);
+ logger.warn(supplier);
+ logger.log(Level.INFO, supplier);
+
+ logger.debug(MARKER, supplier);
+ logger.error(MARKER, supplier);
+ logger.fatal(MARKER, supplier);
+ logger.info(MARKER, supplier);
+ logger.trace(MARKER, supplier);
+ logger.warn(MARKER, supplier);
+ logger.log(Level.INFO, MARKER, supplier);
+ }
+
+ @Test
+ @ResourceLock("log4j2.StatusLogger")
+ void testMessageThrows() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ logger.error(new TestMessage(
+ () -> {
+ throw new IllegalStateException("Oops!");
+ },
+ "Message Format"));
+ final List statusDatalist = StatusLogger.getLogger().getStatusData();
+ final StatusData mostRecent = statusDatalist.get(statusDatalist.size() - 1);
+ assertEquals(Level.WARN, mostRecent.getLevel());
+ assertThat(
+ mostRecent.getFormattedStatus(),
+ containsString("org.apache.logging.log4j.spi.AbstractLogger caught "
+ + "java.lang.IllegalStateException logging TestMessage: Message Format"));
+ }
+
+ @Test
+ @ResourceLock("log4j2.StatusLogger")
+ void testMessageThrowsAndNullFormat() {
+ final ThrowableExpectingLogger logger = new ThrowableExpectingLogger(false);
+ logger.error(new TestMessage(
+ () -> {
+ throw new IllegalStateException("Oops!");
+ },
+ null /* format */));
+ final List statusDatalist = StatusLogger.getLogger().getStatusData();
+ final StatusData mostRecent = statusDatalist.get(statusDatalist.size() - 1);
+ assertEquals(Level.WARN, mostRecent.getLevel());
+ assertThat(
+ mostRecent.getFormattedStatus(),
+ containsString("org.apache.logging.log4j.spi.AbstractLogger caught "
+ + "java.lang.IllegalStateException logging TestMessage: "));
+ }
+
+ private static final class TestMessage implements Message {
+ private final FormattedMessageSupplier formattedMessageSupplier;
+ private final String format;
+
+ TestMessage(final FormattedMessageSupplier formattedMessageSupplier, final String format) {
+ this.formattedMessageSupplier = formattedMessageSupplier;
+ this.format = format;
+ }
+
+ @Override
+ public String getFormattedMessage() {
+ return formattedMessageSupplier.getFormattedMessage();
+ }
+
+ @Override
+ public String getFormat() {
+ return format;
+ }
+
+ @Override
+ public Object[] getParameters() {
+ return Constants.EMPTY_OBJECT_ARRAY;
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return null;
+ }
+
+ interface FormattedMessageSupplier {
+ String getFormattedMessage();
+ }
+ }
+
+ private static class CountingLogger extends AbstractLogger {
+ private static final long serialVersionUID = -3171452617952475480L;
+
+ private Level currentLevel;
+ private LogEvent currentEvent;
+ private int charSeqCount;
+ private int objectCount;
+
+ CountingLogger() {
+ super("CountingLogger", new MessageFactory2Adapter(ParameterizedMessageFactory.INSTANCE));
+ }
+
+ void setCurrentLevel(final Level currentLevel) {
+ this.currentLevel = currentLevel;
+ }
+
+ void setCurrentEvent(final LogEvent currentEvent) {
+ this.currentEvent = currentEvent;
+ }
+
+ int getCharSeqCount() {
+ return charSeqCount;
+ }
+
+ int getObjectCount() {
+ return objectCount;
+ }
+
+ @Override
+ public Level getLevel() {
+ return currentLevel;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Message data, final Throwable t) {
+ assertEquals(level, currentLevel, "Incorrect Level. Expected " + currentLevel + ", actual " + level);
+ if (marker == null) {
+ if (currentEvent.markerName != null) {
+ fail("Incorrect marker. Expected " + currentEvent.markerName + ", actual is null");
+ }
+ } else if (currentEvent.markerName == null) {
+ fail("Incorrect marker. Expected null. Actual is " + marker.getName());
+ } else {
+ assertEquals(
+ currentEvent.markerName,
+ marker.getName(),
+ "Incorrect marker. Expected " + currentEvent.markerName + ", actual " + marker.getName());
+ }
+ if (data == null) {
+ if (currentEvent.data != null) {
+ fail("Incorrect message. Expected " + currentEvent.data + ", actual is null");
+ }
+ } else if (currentEvent.data == null) {
+ fail("Incorrect message. Expected null. Actual is " + data.getFormattedMessage());
+ } else {
+ assertTrue(
+ data.getClass().isAssignableFrom(currentEvent.data.getClass()),
+ "Incorrect message type. Expected " + currentEvent.data + ", actual " + data);
+ assertEquals(
+ currentEvent.data.getFormattedMessage(),
+ data.getFormattedMessage(),
+ "Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ", actual "
+ + data.getFormattedMessage());
+ }
+ if (t == null) {
+ if (currentEvent.t != null) {
+ fail("Incorrect Throwable. Expected " + currentEvent.t + ", actual is null");
+ }
+ } else if (currentEvent.t == null) {
+ fail("Incorrect Throwable. Expected null. Actual is " + t);
+ } else {
+ assertEquals(currentEvent.t, t, "Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final CharSequence data, final Throwable t) {
+ charSeqCount++;
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), t);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Object data, final Throwable t) {
+ objectCount++;
+ return isEnabled(level, marker, new ObjectMessage(data), t);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data) {
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data, final Object... p1) {
+ return isEnabled(level, marker, new ParameterizedMessage(data, p1), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level, final Marker marker, final String message, final Object p0, final Object p1) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8) {
+ return isEnabled(
+ level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8,
+ final Object p9) {
+ return isEnabled(
+ level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data, final Throwable t) {
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), t);
+ }
+
+ @Override
+ public void logMessage(
+ final String fqcn, final Level level, final Marker marker, final Message data, final Throwable t) {
+ assertEquals(level, currentLevel, "Incorrect Level. Expected " + currentLevel + ", actual " + level);
+ if (marker == null) {
+ if (currentEvent.markerName != null) {
+ fail("Incorrect marker. Expected " + currentEvent.markerName + ", actual is null");
+ }
+ } else if (currentEvent.markerName == null) {
+ fail("Incorrect marker. Expected null. Actual is " + marker.getName());
+ } else {
+ assertEquals(
+ currentEvent.markerName,
+ marker.getName(),
+ "Incorrect marker. Expected " + currentEvent.markerName + ", actual " + marker.getName());
+ }
+ if (data == null) {
+ if (currentEvent.data != null) {
+ fail("Incorrect message. Expected " + currentEvent.data + ", actual is null");
+ }
+ } else if (currentEvent.data == null) {
+ fail("Incorrect message. Expected null. Actual is " + data.getFormattedMessage());
+ } else {
+ assertTrue(
+ data.getClass().isAssignableFrom(currentEvent.data.getClass()),
+ "Incorrect message type. Expected " + currentEvent.data + ", actual " + data);
+ assertEquals(
+ currentEvent.data.getFormattedMessage(),
+ data.getFormattedMessage(),
+ "Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ", actual "
+ + data.getFormattedMessage());
+ }
+ if (t == null) {
+ if (currentEvent.t != null) {
+ fail("Incorrect Throwable. Expected " + currentEvent.t + ", actual is null");
+ }
+ } else if (currentEvent.t == null) {
+ fail("Incorrect Throwable. Expected null. Actual is " + t);
+ } else {
+ assertEquals(currentEvent.t, t, "Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t);
+ }
+ }
+ }
+
+ private static class LogEvent {
+ String markerName;
+ Message data;
+ Throwable t;
+
+ public LogEvent(final String markerName, final Message data, final Throwable t) {
+ this.markerName = markerName;
+ this.data = data;
+ this.t = t;
+ }
+ }
+
+ private static class ThrowableExpectingLogger extends AbstractLogger {
+ private static final long serialVersionUID = -7218195998038685039L;
+ private final boolean expectingThrowables;
+
+ ThrowableExpectingLogger(final boolean expectingThrowables) {
+ super("ThrowableExpectingLogger", new MessageFactory2Adapter(ParameterizedMessageFactory.INSTANCE));
+ this.expectingThrowables = expectingThrowables;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Message message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level, final Marker marker, final CharSequence message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Object message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Throwable t) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object... params) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level, final Marker marker, final String message, final Object p0, final Object p1) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8) {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8,
+ final Object p9) {
+ return true;
+ }
+
+ @Override
+ public void logMessage(
+ final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
+ if (expectingThrowables) {
+ assertNotNull(t, "Expected a Throwable but received null!");
+ } else {
+ assertNull(t, "Expected null but received a Throwable! " + t);
+ }
+ if (message != null) {
+ message.getFormattedMessage();
+ }
+ }
+
+ @Override
+ public Level getLevel() {
+ return Level.INFO;
+ }
+ }
+
+ private static class ThrowableMessage implements Message {
+ private static final long serialVersionUID = 1L;
+ private final Throwable throwable;
+
+ public ThrowableMessage(final Throwable throwable) {
+ this.throwable = throwable;
+ }
+
+ @Override
+ public String getFormattedMessage() {
+ return null;
+ }
+
+ @Override
+ public Object[] getParameters() {
+ return Constants.EMPTY_OBJECT_ARRAY;
+ }
+
+ @Override
+ public Throwable getThrowable() {
+ return throwable;
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java
new file mode 100644
index 00000000000..c6bab5da531
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/CloseableThreadContextTest.java
@@ -0,0 +1,315 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+/**
+ * Tests {@link CloseableThreadContext}.
+ *
+ * @since 2.6
+ */
+@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)
+class CloseableThreadContextTest {
+
+ private final String key = "key";
+ private final String value = "value";
+
+ @BeforeEach
+ @AfterEach
+ void clearThreadContext() {
+ ThreadContext.clearAll();
+ }
+
+ @Test
+ void shouldAddAnEntryToTheMap() {
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ }
+
+ @Test
+ void shouldAddTwoEntriesToTheMap() {
+ final String key2 = "key2";
+ final String value2 = "value2";
+ try (final CloseableThreadContext.Instance ignored =
+ CloseableThreadContext.put(key, value).put(key2, value2)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ assertEquals(value2, ThreadContext.get(key2));
+ }
+ }
+
+ @Test
+ void shouldNestEntries() {
+ final String oldValue = "oldValue";
+ final String innerValue = "innerValue";
+ ThreadContext.put(key, oldValue);
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ try (final CloseableThreadContext.Instance ignored2 = CloseableThreadContext.put(key, innerValue)) {
+ assertNotNull(ignored2);
+ assertEquals(innerValue, ThreadContext.get(key));
+ }
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+ }
+
+ @Test
+ void shouldPreserveOldEntriesFromTheMapWhenAutoClosed() {
+ final String oldValue = "oldValue";
+ ThreadContext.put(key, oldValue);
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+ }
+
+ @Test
+ void ifTheSameKeyIsAddedTwiceTheOriginalShouldBeUsed() {
+ final String oldValue = "oldValue";
+ final String secondValue = "innerValue";
+ ThreadContext.put(key, oldValue);
+ try (final CloseableThreadContext.Instance ignored =
+ CloseableThreadContext.put(key, value).put(key, secondValue)) {
+ assertNotNull(ignored);
+ assertEquals(secondValue, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+ }
+
+ @Test
+ void shouldPushAndPopAnEntryToTheStack() {
+ final String message = "message";
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.push(message)) {
+ assertNotNull(ignored);
+ assertEquals(message, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ void shouldPushAndPopTwoEntriesToTheStack() {
+ final String message1 = "message1";
+ final String message2 = "message2";
+ try (final CloseableThreadContext.Instance ignored =
+ CloseableThreadContext.push(message1).push(message2)) {
+ assertNotNull(ignored);
+ assertEquals(message2, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ void shouldPushAndPopAParameterizedEntryToTheStack() {
+ final String parameterizedMessage = "message {}";
+ final String parameterizedMessageParameter = "param";
+ final String formattedMessage = parameterizedMessage.replace("{}", parameterizedMessageParameter);
+ try (final CloseableThreadContext.Instance ignored =
+ CloseableThreadContext.push(parameterizedMessage, parameterizedMessageParameter)) {
+ assertNotNull(ignored);
+ assertEquals(formattedMessage, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ void shouldRemoveAnEntryFromTheMapWhenAutoClosed() {
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key, value)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertFalse(ThreadContext.containsKey(key));
+ }
+
+ @Test
+ void shouldAddEntriesToBothStackAndMap() {
+ final String stackValue = "something";
+ try (final CloseableThreadContext.Instance ignored =
+ CloseableThreadContext.put(key, value).push(stackValue)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ assertEquals(stackValue, ThreadContext.peek());
+ }
+ assertFalse(ThreadContext.containsKey(key));
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ void canReuseCloseableThreadContext() {
+ final String stackValue = "something";
+ // Create a ctc and close it
+ final CloseableThreadContext.Instance ctc =
+ CloseableThreadContext.push(stackValue).put(key, value);
+ assertNotNull(ctc);
+ assertEquals(value, ThreadContext.get(key));
+ assertEquals(stackValue, ThreadContext.peek());
+ ctc.close();
+
+ assertFalse(ThreadContext.containsKey(key));
+ assertEquals("", ThreadContext.peek());
+
+ final String anotherKey = "key2";
+ final String anotherValue = "value2";
+ final String anotherStackValue = "something else";
+ // Use it again
+ ctc.push(anotherStackValue).put(anotherKey, anotherValue);
+ assertEquals(anotherValue, ThreadContext.get(anotherKey));
+ assertEquals(anotherStackValue, ThreadContext.peek());
+ ctc.close();
+
+ assertFalse(ThreadContext.containsKey(anotherKey));
+ assertEquals("", ThreadContext.peek());
+ }
+
+ @Test
+ void closeIsIdempotent() {
+
+ final String originalMapValue = "map to keep";
+ final String originalStackValue = "stack to keep";
+ ThreadContext.put(key, originalMapValue);
+ ThreadContext.push(originalStackValue);
+
+ final String newMapValue = "temp map value";
+ final String newStackValue = "temp stack to keep";
+ final CloseableThreadContext.Instance ctc =
+ CloseableThreadContext.push(newStackValue).put(key, newMapValue);
+ assertNotNull(ctc);
+
+ ctc.close();
+ assertEquals(originalMapValue, ThreadContext.get(key));
+ assertEquals(originalStackValue, ThreadContext.peek());
+
+ ctc.close();
+ assertEquals(originalMapValue, ThreadContext.get(key));
+ assertEquals(originalStackValue, ThreadContext.peek());
+ }
+
+ @Test
+ void putAllWillPutAllValues() {
+
+ final String oldValue = "oldValue";
+ ThreadContext.put(key, oldValue);
+
+ final Map valuesToPut = new HashMap<>();
+ valuesToPut.put(key, value);
+
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.putAll(valuesToPut)) {
+ assertNotNull(ignored);
+ assertEquals(value, ThreadContext.get(key));
+ }
+ assertEquals(oldValue, ThreadContext.get(key));
+ }
+
+ @Test
+ void pushAllWillPushAllValues() {
+
+ ThreadContext.push(key);
+ final List messages = ThreadContext.getImmutableStack().asList();
+ ThreadContext.pop();
+
+ try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.pushAll(messages)) {
+ assertNotNull(ignored);
+ assertEquals(key, ThreadContext.peek());
+ }
+ assertEquals("", ThreadContext.peek());
+ }
+
+ /**
+ * User provided test stressing nesting using {@link CloseableThreadContext#put(String, String)}.
+ *
+ * @see #2946
+ */
+ @Test
+ void testAutoCloseableThreadContextPut() {
+ try (final CloseableThreadContext.Instance ctc1 = CloseableThreadContext.put("outer", "one")) {
+ try (final CloseableThreadContext.Instance ctc2 = CloseableThreadContext.put("outer", "two")) {
+ assertEquals("two", ThreadContext.get("outer"));
+
+ try (final CloseableThreadContext.Instance ctc3 = CloseableThreadContext.put("inner", "one")) {
+ assertEquals("one", ThreadContext.get("inner"));
+
+ ThreadContext.put(
+ "not-in-closeable", "true"); // Remove this line, and closing context behaves as expected
+ assertEquals("two", ThreadContext.get("outer"));
+ }
+
+ assertEquals("two", ThreadContext.get("outer"));
+ assertNull(ThreadContext.get("inner")); // Test fails here
+ }
+
+ assertEquals("one", ThreadContext.get("outer"));
+ assertNull(ThreadContext.get("inner"));
+ }
+ assertEquals("true", ThreadContext.get("not-in-closeable"));
+
+ assertNull(ThreadContext.get("inner"));
+ assertNull(ThreadContext.get("outer"));
+ }
+
+ /**
+ * User provided test stressing nesting using {@link CloseableThreadContext#putAll(Map)}.
+ *
+ * @see #2946
+ */
+ @Test
+ void testAutoCloseableThreadContextPutAll() {
+ try (final CloseableThreadContext.Instance ctc1 = CloseableThreadContext.put("outer", "one")) {
+ try (final CloseableThreadContext.Instance ctc2 = CloseableThreadContext.put("outer", "two")) {
+ assertEquals("two", ThreadContext.get("outer"));
+
+ try (final CloseableThreadContext.Instance ctc3 = CloseableThreadContext.put("inner", "one")) {
+ assertEquals("one", ThreadContext.get("inner"));
+
+ ThreadContext.put(
+ "not-in-closeable", "true"); // Remove this line, and closing context behaves as expected
+ ThreadContext.putAll(Collections.singletonMap("inner", "two")); // But this is not a problem
+ assertEquals("two", ThreadContext.get("inner"));
+ assertEquals("two", ThreadContext.get("outer"));
+ }
+
+ assertEquals("two", ThreadContext.get("outer"));
+ assertNull(ThreadContext.get("inner")); // This is where the test fails
+ }
+
+ assertEquals("one", ThreadContext.get("outer"));
+ assertNull(ThreadContext.get("inner"));
+ }
+ assertEquals("true", ThreadContext.get("not-in-closeable"));
+
+ assertNull(ThreadContext.get("inner"));
+ assertNull(ThreadContext.get("outer"));
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
new file mode 100644
index 00000000000..a6611bbf27f
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/EventLoggerTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+import java.util.Locale;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.test.TestLogger;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+@ResourceLock("log4j2.TestLogger")
+class EventLoggerTest {
+
+ TestLogger logger = (TestLogger) LogManager.getLogger("EventLogger");
+ List results = logger.getEntries();
+
+ @BeforeEach
+ void setup() {
+ results.clear();
+ }
+
+ @Test
+ void structuredData() {
+ ThreadContext.put("loginId", "JohnDoe");
+ ThreadContext.put("ipAddress", "192.168.0.120");
+ ThreadContext.put("locale", Locale.US.getDisplayName(Locale.US));
+ final StructuredDataMessage msg = new StructuredDataMessage("Transfer@18060", "Transfer Complete", "Audit");
+ msg.put("ToAccount", "123456");
+ msg.put("FromAccount", "123457");
+ msg.put("Amount", "200.00");
+ EventLogger.logEvent(msg);
+ ThreadContext.clearMap();
+ assertThat(results).hasSize(1);
+ final String expected =
+ "EVENT OFF Audit [Transfer@18060 Amount=\"200.00\" FromAccount=\"123457\" ToAccount=\"123456\"] Transfer Complete";
+ assertThat(results.get(0)).startsWith(expected);
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
similarity index 84%
rename from log4j-api/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
index f6d1623d682..c84ba45e91b 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LambdaLoggerTest.java
@@ -1,52 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-
package org.apache.logging.log4j;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
import java.util.ArrayList;
import java.util.List;
-
import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ReusableMessage;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.spi.AbstractLogger;
import org.apache.logging.log4j.util.Supplier;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
/**
* Tests the AbstractLogger implementation of the Logger2 interface.
*/
-public class LambdaLoggerTest {
+class LambdaLoggerTest {
private static class LogEvent {
@SuppressWarnings("unused")
final String fqcn;
+
final Level level;
final Marker marker;
final Message message;
final Throwable throwable;
- public LogEvent(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
+ public LogEvent(
+ final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
this.fqcn = fqcn;
this.level = level;
this.marker = marker;
- this.message = message;
+ this.message = (message instanceof ReusableMessage) ? ((ReusableMessage) message).memento() : message;
this.throwable = t;
}
}
@@ -63,7 +67,8 @@ public boolean isEnabled(final Level level, final Marker marker, final Message m
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final CharSequence message, final Throwable t) {
+ public boolean isEnabled(
+ final Level level, final Marker marker, final CharSequence message, final Throwable t) {
return enabled;
}
@@ -93,70 +98,130 @@ public boolean isEnabled(final Level level, final Marker marker, final String me
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1) {
+ public boolean isEnabled(
+ final Level level, final Marker marker, final String message, final Object p0, final Object p1) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2) {
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2, final Object p3) {
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2, final Object p3,
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
final Object p4) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2, final Object p3,
- final Object p4, final Object p5) {
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2, final Object p3,
- final Object p4, final Object p5, final Object p6) {
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2, final Object p3,
- final Object p4, final Object p5, final Object p6,
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
final Object p7) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2, final Object p3,
- final Object p4, final Object p5, final Object p6,
- final Object p7, final Object p8) {
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8) {
return enabled;
}
@Override
- public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0,
- final Object p1, final Object p2, final Object p3,
- final Object p4, final Object p5, final Object p6,
- final Object p7, final Object p8, final Object p9) {
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8,
+ final Object p9) {
return enabled;
}
@Override
- public void logMessage(final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
+ public void logMessage(
+ final String fqcn, final Level level, final Marker marker, final Message message, final Throwable t) {
list.add(new LogEvent(fqcn, level, marker, message, t));
}
@@ -209,15 +274,15 @@ public String get() {
final Supplier[] supplierArray1 = new Supplier[] {supplier};
final Supplier[] supplierArray2 = new Supplier[] {supplier, supplier2};
- @Before
- public void beforeEachTest() {
+ @BeforeEach
+ void beforeEachTest() {
logger2.list.clear();
supplier.invoked = false;
messageSupplier.invoked = false;
}
@Test
- public void testDebugMarkerMessageSupplier() {
+ void testDebugMarkerMessageSupplier() {
logger2.disable().debug(marker, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -233,7 +298,7 @@ public void testDebugMarkerMessageSupplier() {
}
@Test
- public void testDebugMessageSupplier() {
+ void testDebugMessageSupplier() {
logger2.disable().debug(messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -248,7 +313,7 @@ public void testDebugMessageSupplier() {
}
@Test
- public void testDebugMarkerMessageSupplierThrowable() {
+ void testDebugMarkerMessageSupplierThrowable() {
logger2.disable().debug(marker, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -265,7 +330,7 @@ public void testDebugMarkerMessageSupplierThrowable() {
}
@Test
- public void testDebugMessageSupplierThrowable() {
+ void testDebugMessageSupplierThrowable() {
logger2.disable().debug(messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -281,7 +346,7 @@ public void testDebugMessageSupplierThrowable() {
}
@Test
- public void testDebugMarkerSupplier() {
+ void testDebugMarkerSupplier() {
logger2.disable().debug(marker, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -297,7 +362,7 @@ public void testDebugMarkerSupplier() {
}
@Test
- public void testDebugSupplier() {
+ void testDebugSupplier() {
logger2.disable().debug(supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -312,7 +377,7 @@ public void testDebugSupplier() {
}
@Test
- public void testDebugMarkerSupplierThrowable() {
+ void testDebugMarkerSupplierThrowable() {
logger2.disable().debug(marker, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -329,7 +394,7 @@ public void testDebugMarkerSupplierThrowable() {
}
@Test
- public void testDebugSupplierThrowable() {
+ void testDebugSupplierThrowable() {
logger2.disable().debug(supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -345,7 +410,7 @@ public void testDebugSupplierThrowable() {
}
@Test
- public void testDebugStringParamSupplier() {
+ void testDebugStringParamSupplier() {
logger2.disable().debug("abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -360,7 +425,7 @@ public void testDebugStringParamSupplier() {
}
@Test
- public void testDebugMarkerStringParamSupplier() {
+ void testDebugMarkerStringParamSupplier() {
logger2.disable().debug(marker, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -376,7 +441,7 @@ public void testDebugMarkerStringParamSupplier() {
}
@Test
- public void testErrorMarkerMessageSupplier() {
+ void testErrorMarkerMessageSupplier() {
logger2.disable().error(marker, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -392,7 +457,7 @@ public void testErrorMarkerMessageSupplier() {
}
@Test
- public void testErrorMessageSupplier() {
+ void testErrorMessageSupplier() {
logger2.disable().error(messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -407,7 +472,7 @@ public void testErrorMessageSupplier() {
}
@Test
- public void testErrorMarkerMessageSupplierThrowable() {
+ void testErrorMarkerMessageSupplierThrowable() {
logger2.disable().error(marker, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -424,7 +489,7 @@ public void testErrorMarkerMessageSupplierThrowable() {
}
@Test
- public void testErrorMessageSupplierThrowable() {
+ void testErrorMessageSupplierThrowable() {
logger2.disable().error(messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -440,7 +505,7 @@ public void testErrorMessageSupplierThrowable() {
}
@Test
- public void testErrorMarkerSupplier() {
+ void testErrorMarkerSupplier() {
logger2.disable().error(marker, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -456,7 +521,7 @@ public void testErrorMarkerSupplier() {
}
@Test
- public void testErrorSupplier() {
+ void testErrorSupplier() {
logger2.disable().error(supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -471,7 +536,7 @@ public void testErrorSupplier() {
}
@Test
- public void testErrorMarkerSupplierThrowable() {
+ void testErrorMarkerSupplierThrowable() {
logger2.disable().error(marker, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -488,7 +553,7 @@ public void testErrorMarkerSupplierThrowable() {
}
@Test
- public void testErrorSupplierThrowable() {
+ void testErrorSupplierThrowable() {
logger2.disable().error(supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -504,7 +569,7 @@ public void testErrorSupplierThrowable() {
}
@Test
- public void testErrorStringParamSupplier() {
+ void testErrorStringParamSupplier() {
logger2.disable().error("abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -519,7 +584,7 @@ public void testErrorStringParamSupplier() {
}
@Test
- public void testErrorMarkerStringParamSupplier() {
+ void testErrorMarkerStringParamSupplier() {
logger2.disable().error(marker, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -535,7 +600,7 @@ public void testErrorMarkerStringParamSupplier() {
}
@Test
- public void testFatalMarkerMessageSupplier() {
+ void testFatalMarkerMessageSupplier() {
logger2.disable().fatal(marker, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -551,7 +616,7 @@ public void testFatalMarkerMessageSupplier() {
}
@Test
- public void testFatalMessageSupplier() {
+ void testFatalMessageSupplier() {
logger2.disable().fatal(messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -566,7 +631,7 @@ public void testFatalMessageSupplier() {
}
@Test
- public void testFatalMarkerMessageSupplierThrowable() {
+ void testFatalMarkerMessageSupplierThrowable() {
logger2.disable().fatal(marker, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -583,7 +648,7 @@ public void testFatalMarkerMessageSupplierThrowable() {
}
@Test
- public void testFatalMessageSupplierThrowable() {
+ void testFatalMessageSupplierThrowable() {
logger2.disable().fatal(messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -599,7 +664,7 @@ public void testFatalMessageSupplierThrowable() {
}
@Test
- public void testFatalMarkerSupplier() {
+ void testFatalMarkerSupplier() {
logger2.disable().fatal(marker, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -615,7 +680,7 @@ public void testFatalMarkerSupplier() {
}
@Test
- public void testFatalSupplier() {
+ void testFatalSupplier() {
logger2.disable().fatal(supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -630,7 +695,7 @@ public void testFatalSupplier() {
}
@Test
- public void testFatalMarkerSupplierThrowable() {
+ void testFatalMarkerSupplierThrowable() {
logger2.disable().fatal(marker, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -647,7 +712,7 @@ public void testFatalMarkerSupplierThrowable() {
}
@Test
- public void testFatalSupplierThrowable() {
+ void testFatalSupplierThrowable() {
logger2.disable().fatal(supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -663,7 +728,7 @@ public void testFatalSupplierThrowable() {
}
@Test
- public void testFatalStringParamSupplier() {
+ void testFatalStringParamSupplier() {
logger2.disable().fatal("abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -678,7 +743,7 @@ public void testFatalStringParamSupplier() {
}
@Test
- public void testFatalStringParam2Suppliers() {
+ void testFatalStringParam2Suppliers() {
logger2.disable().fatal("abc {}{}", supplierArray2);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -695,7 +760,7 @@ public void testFatalStringParam2Suppliers() {
}
@Test
- public void testFatalMarkerStringParamSupplier() {
+ void testFatalMarkerStringParamSupplier() {
logger2.disable().fatal(marker, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -711,7 +776,7 @@ public void testFatalMarkerStringParamSupplier() {
}
@Test
- public void testInfoMarkerMessageSupplier() {
+ void testInfoMarkerMessageSupplier() {
logger2.disable().info(marker, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -727,7 +792,7 @@ public void testInfoMarkerMessageSupplier() {
}
@Test
- public void testInfoMessageSupplier() {
+ void testInfoMessageSupplier() {
logger2.disable().info(messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -742,7 +807,7 @@ public void testInfoMessageSupplier() {
}
@Test
- public void testInfoMarkerMessageSupplierThrowable() {
+ void testInfoMarkerMessageSupplierThrowable() {
logger2.disable().info(marker, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -759,7 +824,7 @@ public void testInfoMarkerMessageSupplierThrowable() {
}
@Test
- public void testInfoMessageSupplierThrowable() {
+ void testInfoMessageSupplierThrowable() {
logger2.disable().info(messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -775,7 +840,7 @@ public void testInfoMessageSupplierThrowable() {
}
@Test
- public void testInfoMarkerSupplier() {
+ void testInfoMarkerSupplier() {
logger2.disable().info(marker, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -791,7 +856,7 @@ public void testInfoMarkerSupplier() {
}
@Test
- public void testInfoSupplier() {
+ void testInfoSupplier() {
logger2.disable().info(supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -806,7 +871,7 @@ public void testInfoSupplier() {
}
@Test
- public void testInfoMarkerSupplierThrowable() {
+ void testInfoMarkerSupplierThrowable() {
logger2.disable().info(marker, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -823,7 +888,7 @@ public void testInfoMarkerSupplierThrowable() {
}
@Test
- public void testInfoSupplierThrowable() {
+ void testInfoSupplierThrowable() {
logger2.disable().info(supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -839,7 +904,7 @@ public void testInfoSupplierThrowable() {
}
@Test
- public void testInfoStringParamSupplier() {
+ void testInfoStringParamSupplier() {
logger2.disable().info("abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -854,7 +919,7 @@ public void testInfoStringParamSupplier() {
}
@Test
- public void testInfoMarkerStringParamSupplier() {
+ void testInfoMarkerStringParamSupplier() {
logger2.disable().info(marker, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -870,7 +935,7 @@ public void testInfoMarkerStringParamSupplier() {
}
@Test
- public void testTraceMarkerMessageSupplier() {
+ void testTraceMarkerMessageSupplier() {
logger2.disable().trace(marker, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -886,7 +951,7 @@ public void testTraceMarkerMessageSupplier() {
}
@Test
- public void testTraceMessageSupplier() {
+ void testTraceMessageSupplier() {
logger2.disable().trace(messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -901,7 +966,7 @@ public void testTraceMessageSupplier() {
}
@Test
- public void testTraceMarkerMessageSupplierThrowable() {
+ void testTraceMarkerMessageSupplierThrowable() {
logger2.disable().trace(marker, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -918,7 +983,7 @@ public void testTraceMarkerMessageSupplierThrowable() {
}
@Test
- public void testTraceMessageSupplierThrowable() {
+ void testTraceMessageSupplierThrowable() {
logger2.disable().trace(messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -934,7 +999,7 @@ public void testTraceMessageSupplierThrowable() {
}
@Test
- public void testTraceMarkerSupplier() {
+ void testTraceMarkerSupplier() {
logger2.disable().trace(marker, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -950,7 +1015,7 @@ public void testTraceMarkerSupplier() {
}
@Test
- public void testTraceSupplier() {
+ void testTraceSupplier() {
logger2.disable().trace(supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -965,7 +1030,7 @@ public void testTraceSupplier() {
}
@Test
- public void testTraceMarkerSupplierThrowable() {
+ void testTraceMarkerSupplierThrowable() {
logger2.disable().trace(marker, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -982,7 +1047,7 @@ public void testTraceMarkerSupplierThrowable() {
}
@Test
- public void testTraceSupplierThrowable() {
+ void testTraceSupplierThrowable() {
logger2.disable().trace(supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -998,7 +1063,7 @@ public void testTraceSupplierThrowable() {
}
@Test
- public void testTraceStringParamSupplier() {
+ void testTraceStringParamSupplier() {
logger2.disable().trace("abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1013,7 +1078,7 @@ public void testTraceStringParamSupplier() {
}
@Test
- public void testTraceMarkerStringParamSupplier() {
+ void testTraceMarkerStringParamSupplier() {
logger2.disable().trace(marker, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1029,7 +1094,7 @@ public void testTraceMarkerStringParamSupplier() {
}
@Test
- public void testWarnMarkerMessageSupplier() {
+ void testWarnMarkerMessageSupplier() {
logger2.disable().warn(marker, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1045,7 +1110,7 @@ public void testWarnMarkerMessageSupplier() {
}
@Test
- public void testWarnMessageSupplier() {
+ void testWarnMessageSupplier() {
logger2.disable().warn(messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1060,7 +1125,7 @@ public void testWarnMessageSupplier() {
}
@Test
- public void testWarnMarkerMessageSupplierThrowable() {
+ void testWarnMarkerMessageSupplierThrowable() {
logger2.disable().warn(marker, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1077,7 +1142,7 @@ public void testWarnMarkerMessageSupplierThrowable() {
}
@Test
- public void testWarnMessageSupplierThrowable() {
+ void testWarnMessageSupplierThrowable() {
logger2.disable().warn(messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1093,7 +1158,7 @@ public void testWarnMessageSupplierThrowable() {
}
@Test
- public void testWarnMarkerSupplier() {
+ void testWarnMarkerSupplier() {
logger2.disable().warn(marker, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1109,7 +1174,7 @@ public void testWarnMarkerSupplier() {
}
@Test
- public void testWarnSupplier() {
+ void testWarnSupplier() {
logger2.disable().warn(supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1124,7 +1189,7 @@ public void testWarnSupplier() {
}
@Test
- public void testWarnMarkerSupplierThrowable() {
+ void testWarnMarkerSupplierThrowable() {
logger2.disable().warn(marker, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1141,7 +1206,7 @@ public void testWarnMarkerSupplierThrowable() {
}
@Test
- public void testWarnSupplierThrowable() {
+ void testWarnSupplierThrowable() {
logger2.disable().warn(supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1157,7 +1222,7 @@ public void testWarnSupplierThrowable() {
}
@Test
- public void testWarnStringParamSupplier() {
+ void testWarnStringParamSupplier() {
logger2.disable().warn("abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1172,7 +1237,7 @@ public void testWarnStringParamSupplier() {
}
@Test
- public void testWarnMarkerStringParamSupplier() {
+ void testWarnMarkerStringParamSupplier() {
logger2.disable().warn(marker, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1188,7 +1253,7 @@ public void testWarnMarkerStringParamSupplier() {
}
@Test
- public void testLogMarkerMessageSupplier() {
+ void testLogMarkerMessageSupplier() {
logger2.disable().log(Level.WARN, marker, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1204,7 +1269,7 @@ public void testLogMarkerMessageSupplier() {
}
@Test
- public void testLogMessageSupplier() {
+ void testLogMessageSupplier() {
logger2.disable().log(Level.WARN, messageSupplier);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1219,7 +1284,7 @@ public void testLogMessageSupplier() {
}
@Test
- public void testLogMarkerMessageSupplierThrowable() {
+ void testLogMarkerMessageSupplierThrowable() {
logger2.disable().log(Level.WARN, marker, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1236,7 +1301,7 @@ public void testLogMarkerMessageSupplierThrowable() {
}
@Test
- public void testLogMessageSupplierThrowable() {
+ void testLogMessageSupplierThrowable() {
logger2.disable().log(Level.WARN, messageSupplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(messageSupplier.invoked);
@@ -1252,7 +1317,7 @@ public void testLogMessageSupplierThrowable() {
}
@Test
- public void testLogMarkerSupplier() {
+ void testLogMarkerSupplier() {
logger2.disable().log(Level.WARN, marker, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1268,7 +1333,7 @@ public void testLogMarkerSupplier() {
}
@Test
- public void testLogSupplier() {
+ void testLogSupplier() {
logger2.disable().log(Level.WARN, supplier);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1283,7 +1348,7 @@ public void testLogSupplier() {
}
@Test
- public void testLogMarkerSupplierThrowable() {
+ void testLogMarkerSupplierThrowable() {
logger2.disable().log(Level.WARN, marker, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1300,7 +1365,7 @@ public void testLogMarkerSupplierThrowable() {
}
@Test
- public void testLogSupplierThrowable() {
+ void testLogSupplierThrowable() {
logger2.disable().log(Level.WARN, supplier, throwable);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1316,7 +1381,7 @@ public void testLogSupplierThrowable() {
}
@Test
- public void testLogStringParamSupplier() {
+ void testLogStringParamSupplier() {
logger2.disable().log(Level.WARN, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1331,7 +1396,7 @@ public void testLogStringParamSupplier() {
}
@Test
- public void testLogMarkerStringParamSupplier() {
+ void testLogMarkerStringParamSupplier() {
logger2.disable().log(Level.WARN, marker, "abc {}", supplierArray1);
assertTrue(logger2.list.isEmpty());
assertFalse(supplier.invoked);
@@ -1345,5 +1410,4 @@ public void testLogMarkerStringParamSupplier() {
assertSame(marker, event.marker);
assertEquals("abc Hi", event.message.getFormattedMessage());
}
-
}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java
new file mode 100644
index 00000000000..30f9e4073d5
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LevelTest.java
@@ -0,0 +1,273 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class LevelTest {
+
+ @Test
+ void testDefault() {
+ final Level level = Level.toLevel("Information", Level.ERROR);
+ assertNotNull(level);
+ assertEquals(Level.ERROR, level);
+ }
+
+ @Test
+ void testForNameEquals() {
+ final String name = "Foo";
+ final int intValue = 1;
+ final Level level = Level.forName(name, intValue);
+ assertNotNull(level);
+ assertEquals(level, Level.forName(name, intValue));
+ assertEquals(level, Level.getLevel(name));
+ assertEquals(level, Level.toLevel(name));
+ assertEquals(intValue, Level.getLevel(name).intLevel());
+ }
+
+ @Test
+ void testThrowsOnNull() {
+ assertThrowsExactly(IllegalArgumentException.class, () -> Level.forName(null, 100));
+ assertThrowsExactly(IllegalArgumentException.class, () -> Level.getLevel(null));
+ // the intLevel should be checked only if we create a new level
+ assertNull(Level.getLevel("Bar"));
+ assertThrowsExactly(IllegalArgumentException.class, () -> Level.forName("Bar", -1));
+ }
+
+ @Test
+ void testGoodLevels() {
+ final Level level = Level.toLevel("INFO");
+ assertNotNull(level);
+ assertEquals(Level.INFO, level);
+ }
+
+ @Test
+ void testIsInRangeErrorToDebug() {
+ assertFalse(Level.OFF.isInRange(Level.ERROR, Level.DEBUG));
+ assertFalse(Level.FATAL.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.ERROR.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.WARN.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.INFO.isInRange(Level.ERROR, Level.DEBUG));
+ assertTrue(Level.DEBUG.isInRange(Level.ERROR, Level.DEBUG));
+ assertFalse(Level.TRACE.isInRange(Level.ERROR, Level.DEBUG));
+ assertFalse(Level.ALL.isInRange(Level.ERROR, Level.DEBUG));
+ }
+
+ @Test
+ void testIsInRangeFatalToTrace() {
+ assertFalse(Level.OFF.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.FATAL.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.ERROR.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.WARN.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.INFO.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.DEBUG.isInRange(Level.FATAL, Level.TRACE));
+ assertTrue(Level.TRACE.isInRange(Level.FATAL, Level.TRACE));
+ assertFalse(Level.ALL.isInRange(Level.FATAL, Level.TRACE));
+ }
+
+ @Test
+ void testIsInRangeOffToAll() {
+ assertTrue(Level.OFF.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.FATAL.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.ERROR.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.WARN.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.INFO.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.DEBUG.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.TRACE.isInRange(Level.OFF, Level.ALL));
+ assertTrue(Level.ALL.isInRange(Level.OFF, Level.ALL));
+ }
+
+ @Test
+ void testIsInRangeSameLevels() {
+ // Level.OFF
+ assertTrue(Level.OFF.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.OFF.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.OFF.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.OFF.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.OFF.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.OFF.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.OFF.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.OFF.isInRange(Level.ALL, Level.ALL));
+ // Level.FATAL
+ assertFalse(Level.FATAL.isInRange(Level.OFF, Level.OFF));
+ assertTrue(Level.FATAL.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.FATAL.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.FATAL.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.FATAL.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.FATAL.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.FATAL.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.FATAL.isInRange(Level.ALL, Level.ALL));
+ // Level.ERROR
+ assertFalse(Level.ERROR.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.ERROR.isInRange(Level.FATAL, Level.FATAL));
+ assertTrue(Level.ERROR.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.ERROR.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.ERROR.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.ERROR.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.ERROR.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.ERROR.isInRange(Level.ALL, Level.ALL));
+ // Level.WARN
+ assertFalse(Level.WARN.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.WARN.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.WARN.isInRange(Level.ERROR, Level.ERROR));
+ assertTrue(Level.WARN.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.WARN.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.WARN.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.WARN.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.WARN.isInRange(Level.ALL, Level.ALL));
+ // Level.INFO
+ assertFalse(Level.INFO.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.INFO.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.INFO.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.INFO.isInRange(Level.WARN, Level.WARN));
+ assertTrue(Level.INFO.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.INFO.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.INFO.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.INFO.isInRange(Level.ALL, Level.ALL));
+ // Level.DEBUG
+ assertFalse(Level.DEBUG.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.DEBUG.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.DEBUG.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.DEBUG.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.DEBUG.isInRange(Level.INFO, Level.INFO));
+ assertTrue(Level.DEBUG.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.DEBUG.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.DEBUG.isInRange(Level.ALL, Level.ALL));
+ // Level.TRACE
+ assertFalse(Level.TRACE.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.TRACE.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.TRACE.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.TRACE.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.TRACE.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.TRACE.isInRange(Level.DEBUG, Level.DEBUG));
+ assertTrue(Level.TRACE.isInRange(Level.TRACE, Level.TRACE));
+ assertFalse(Level.TRACE.isInRange(Level.ALL, Level.ALL));
+ // Level.ALL
+ assertFalse(Level.ALL.isInRange(Level.OFF, Level.OFF));
+ assertFalse(Level.ALL.isInRange(Level.FATAL, Level.FATAL));
+ assertFalse(Level.ALL.isInRange(Level.ERROR, Level.ERROR));
+ assertFalse(Level.ALL.isInRange(Level.WARN, Level.WARN));
+ assertFalse(Level.ALL.isInRange(Level.INFO, Level.INFO));
+ assertFalse(Level.ALL.isInRange(Level.DEBUG, Level.DEBUG));
+ assertFalse(Level.ALL.isInRange(Level.TRACE, Level.TRACE));
+ assertTrue(Level.ALL.isInRange(Level.ALL, Level.ALL));
+ }
+
+ @Test
+ void testIsInRangeWarnToInfo() {
+ assertFalse(Level.OFF.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.FATAL.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.ERROR.isInRange(Level.WARN, Level.INFO));
+ assertTrue(Level.WARN.isInRange(Level.WARN, Level.INFO));
+ assertTrue(Level.INFO.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.DEBUG.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.TRACE.isInRange(Level.WARN, Level.INFO));
+ assertFalse(Level.ALL.isInRange(Level.WARN, Level.INFO));
+ }
+
+ @Test
+ void testIsLessSpecificThan() {
+ // Level.OFF
+ assertTrue(Level.OFF.isLessSpecificThan(Level.OFF));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.FATAL));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.ERROR));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.OFF.isLessSpecificThan(Level.ALL));
+ // Level.FATAL
+ assertTrue(Level.FATAL.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.FATAL.isLessSpecificThan(Level.FATAL));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.ERROR));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.FATAL.isLessSpecificThan(Level.ALL));
+ // Level.ERROR
+ assertTrue(Level.ERROR.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.ERROR.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.ERROR.isLessSpecificThan(Level.ERROR));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.ERROR.isLessSpecificThan(Level.ALL));
+ // Level.ERROR
+ assertTrue(Level.WARN.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.ALL));
+ // Level.WARN
+ assertTrue(Level.WARN.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.WARN.isLessSpecificThan(Level.WARN));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.WARN.isLessSpecificThan(Level.ALL));
+ // Level.INFO
+ assertTrue(Level.INFO.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.INFO.isLessSpecificThan(Level.INFO));
+ assertFalse(Level.INFO.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.INFO.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.INFO.isLessSpecificThan(Level.ALL));
+ // Level.DEBUG
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.INFO));
+ assertTrue(Level.DEBUG.isLessSpecificThan(Level.DEBUG));
+ assertFalse(Level.DEBUG.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.DEBUG.isLessSpecificThan(Level.ALL));
+ // Level.TRACE
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.INFO));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.DEBUG));
+ assertTrue(Level.TRACE.isLessSpecificThan(Level.TRACE));
+ assertFalse(Level.TRACE.isLessSpecificThan(Level.ALL));
+ // Level.ALL
+ assertTrue(Level.ALL.isLessSpecificThan(Level.OFF));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.FATAL));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.ERROR));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.WARN));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.INFO));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.DEBUG));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.TRACE));
+ assertTrue(Level.ALL.isLessSpecificThan(Level.ALL));
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java
new file mode 100644
index 00000000000..6d54a96c52a
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LogManagerTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.io.Closeable;
+import java.io.IOException;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
+import org.apache.logging.log4j.spi.LoggerContext;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+@ResourceLock(value = "log4j2.LoggerContextFactory", mode = ResourceAccessMode.READ)
+class LogManagerTest {
+
+ @SuppressWarnings("InnerClassMayBeStatic")
+ class Inner {
+ final Logger LOGGER = LogManager.getLogger();
+ }
+
+ @SuppressWarnings("InnerClassMayBeStatic")
+ class InnerByClass {
+ final Logger LOGGER = LogManager.getLogger(InnerByClass.class);
+ }
+
+ static class StaticInner {
+ static final Logger LOGGER = LogManager.getLogger();
+ }
+
+ static class StaticInnerByClass {
+ static final Logger LOGGER = LogManager.getLogger(StaticInnerByClass.class);
+ }
+
+ @Test
+ void testGetLogger() {
+ Logger logger = LogManager.getLogger();
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger(ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Class>) null);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Class>) null, ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((String) null);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((String) null, ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Object) null);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ logger = LogManager.getLogger((Object) null, ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(logger, "No Logger returned");
+ assertEquals(LogManagerTest.class.getName(), logger.getName(), "Incorrect Logger name: " + logger.getName());
+ }
+
+ @Test
+ void testGetLoggerForAnonymousInnerClass1() throws IOException {
+ final Closeable closeable = new Closeable() {
+
+ final Logger LOGGER = LogManager.getLogger();
+
+ @Override
+ public void close() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest$1", LOGGER.getName());
+ }
+ };
+ closeable.close();
+ }
+
+ @Test
+ void testGetLoggerForAnonymousInnerClass2() throws IOException {
+ final Closeable closeable = new Closeable() {
+
+ final Logger LOGGER = LogManager.getLogger(getClass());
+
+ @Override
+ public void close() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest$2", LOGGER.getName());
+ }
+ };
+ closeable.close();
+ }
+
+ @Test
+ void testGetLoggerForInner() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.Inner", new Inner().LOGGER.getName());
+ }
+
+ @Test
+ void testGetLoggerForInnerByClass() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.InnerByClass", new InnerByClass().LOGGER.getName());
+ }
+
+ @Test
+ void testGetLoggerForStaticInner() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.StaticInner", StaticInner.LOGGER.getName());
+ }
+
+ @Test
+ void testGetLoggerForStaticInnerByClass() {
+ assertEquals("org.apache.logging.log4j.LogManagerTest.StaticInnerByClass", StaticInnerByClass.LOGGER.getName());
+ }
+
+ @Test
+ void testShutdown() {
+ final LoggerContext loggerContext = LogManager.getContext(false);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java
new file mode 100644
index 00000000000..761425eabb2
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerSupplierTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+import org.apache.logging.log4j.message.FormattedMessage;
+import org.apache.logging.log4j.message.JsonMessage;
+import org.apache.logging.log4j.message.LocalizedMessage;
+import org.apache.logging.log4j.message.MessageFormatMessage;
+import org.apache.logging.log4j.message.ObjectArrayMessage;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.message.StringFormattedMessage;
+import org.apache.logging.log4j.message.ThreadDumpMessage;
+import org.apache.logging.log4j.test.TestLogger;
+import org.apache.logging.log4j.util.Supplier;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+/**
+ * Tests Logger APIs with {@link Supplier}.
+ */
+@ResourceLock(Resources.LOCALE)
+@ResourceLock("log4j2.TestLogger")
+class LoggerSupplierTest {
+
+ private final TestLogger logger = (TestLogger) LogManager.getLogger("LoggerTest");
+
+ private final List results = logger.getEntries();
+
+ Locale defaultLocale;
+
+ @Test
+ void flowTracing_SupplierOfFormattedMessage() {
+ logger.traceEntry(() -> new FormattedMessage("int foo={}", 1234567890));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1234567890)")
+ .doesNotContain("FormattedMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfJsonMessage() {
+ final Properties props = new Properties();
+ props.setProperty("foo", "bar");
+ logger.traceEntry(() -> new JsonMessage(props));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("\"foo\":\"bar\"")
+ .doesNotContain("JsonMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfLocalizedMessage() {
+ logger.traceEntry(() -> new LocalizedMessage("int foo={}", 1234567890));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1234567890)")
+ .doesNotContain("LocalizedMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfLong() {
+ logger.traceEntry(() -> 1234567890L);
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("SimpleMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfMessageFormatMessage() {
+ logger.traceEntry(() -> new MessageFormatMessage("int foo={0}", 1234567890));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1,234,567,890)")
+ .doesNotContain("MessageFormatMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfObjectArrayMessage() {
+ logger.traceEntry(() -> new ObjectArrayMessage(1234567890));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("([1234567890])")
+ .doesNotContain("ObjectArrayMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfObjectMessage() {
+ logger.traceEntry(() -> new ObjectMessage(1234567890));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("ObjectMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfParameterizedMessage() {
+ logger.traceEntry(() -> new ParameterizedMessage("int foo={}", 1234567890));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1234567890)")
+ .doesNotContain("ParameterizedMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfSimpleMessage() {
+ logger.traceEntry(() -> new SimpleMessage("1234567890"));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("SimpleMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfString() {
+ logger.traceEntry(() -> "1234567890");
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(1234567890)")
+ .doesNotContain("SimpleMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfStringFormattedMessage() {
+ logger.traceEntry(() -> new StringFormattedMessage("int foo=%,d", 1234567890));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("(int foo=1,234,567,890)")
+ .doesNotContain("StringFormattedMessage");
+ }
+
+ @Test
+ void flowTracing_SupplierOfThreadDumpMessage() {
+ logger.traceEntry(() -> new ThreadDumpMessage("Title of ..."));
+ assertThat(results).hasSize(1);
+ final String entry = results.get(0);
+ assertThat(entry)
+ .startsWith("ENTER[ FLOW ] TRACE Enter")
+ .contains("RUNNABLE", "Title of ...", getClass().getName());
+ }
+
+ @BeforeEach
+ void setup() {
+ results.clear();
+ defaultLocale = Locale.getDefault(Locale.Category.FORMAT);
+ Locale.setDefault(Locale.Category.FORMAT, java.util.Locale.US);
+ }
+
+ @AfterEach
+ void tearDown() {
+ Locale.setDefault(Locale.Category.FORMAT, defaultLocale);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
new file mode 100644
index 00000000000..ac00b97be9a
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/LoggerTest.java
@@ -0,0 +1,658 @@
+/*
+ * 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;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+import org.apache.logging.log4j.message.EntryMessage;
+import org.apache.logging.log4j.message.JsonMessage;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.MessageFactory;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessageFactory;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.message.SimpleMessageFactory;
+import org.apache.logging.log4j.message.StringFormatterMessageFactory;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.spi.MessageFactory2Adapter;
+import org.apache.logging.log4j.test.TestLogger;
+import org.apache.logging.log4j.test.junit.Log4jStaticResources;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.apache.logging.log4j.util.Strings;
+import org.apache.logging.log4j.util.Supplier;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junitpioneer.jupiter.ReadsSystemProperty;
+
+@ResourceLock(value = Log4jStaticResources.MARKER_MANAGER, mode = ResourceAccessMode.READ)
+@ReadsSystemProperty
+class LoggerTest {
+
+ private static class TestParameterizedMessageFactory {
+ // empty
+ }
+
+ private static class TestStringFormatterMessageFactory {
+ // empty
+ }
+
+ private final TestLogger logger = (TestLogger) LogManager.getLogger(LoggerTest.class);
+ private final Marker marker = MarkerManager.getMarker("test");
+ private final List results = logger.getEntries();
+
+ @Test
+ void builder() {
+ logger.atDebug().withLocation().log("Hello");
+ logger.atError().withMarker(marker).log("Hello {}", "John");
+ logger.atWarn().withThrowable(new Throwable("This is a test")).log((Message) new SimpleMessage("Log4j rocks!"));
+ assertEquals(3, results.size());
+ assertThat(
+ "Incorrect message 1",
+ results.get(0),
+ equalTo(" DEBUG org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:72) Hello"));
+ assertThat("Incorrect message 2", results.get(1), equalTo("test ERROR Hello John"));
+ assertThat(
+ "Incorrect message 3",
+ results.get(2),
+ startsWith(" WARN Log4j rocks! java.lang.Throwable: This is a test"));
+ assertThat(
+ "Throwable incorrect in message 3",
+ results.get(2),
+ containsString("org.apache.logging.log4j.LoggerTest.builder(LoggerTest.java:74)"));
+ }
+
+ @Test
+ void basicFlow() {
+ logger.entry();
+ logger.exit();
+ assertEquals(2, results.size());
+ assertThat(results.get(0)).isEqualTo("ENTER[ FLOW ] TRACE Enter");
+ assertThat(results.get(1)).isEqualTo("EXIT[ FLOW ] TRACE Exit");
+ }
+
+ @Test
+ void flowTracingMessage() {
+ final Properties props = new Properties();
+ props.setProperty("foo", "bar");
+ logger.traceEntry(new JsonMessage(props));
+ final Response response = new Response(-1, "Generic error");
+ logger.traceExit(new JsonMessage(response), response);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("\"foo\":\"bar\"");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("\"message\":\"Generic error\"");
+ }
+
+ @Test
+ void flowTracingString_ObjectArray1() {
+ logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit("doFoo(a=1, b=2): {}", 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ void flowTracingExitValueOnly() {
+ logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit(3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("3");
+ }
+
+ @Test
+ void flowTracingString_ObjectArray2() {
+ final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit(msg, 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ void flowTracingVoidReturn() {
+ final EntryMessage msg = logger.traceEntry("doFoo(a={}, b={})", 1, 2);
+ logger.traceExit(msg);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").endsWith("doFoo(a=1, b=2)");
+ }
+
+ @Test
+ void flowTracingNoExitArgs() {
+ logger.traceEntry();
+ logger.traceExit();
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit");
+ }
+
+ @Test
+ void flowTracingNoArgs() {
+ final EntryMessage message = logger.traceEntry();
+ logger.traceExit(message);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit");
+ }
+
+ @Test
+ void flowTracingString_SupplierOfObjectMessages() {
+ final EntryMessage msg = logger.traceEntry(
+ "doFoo(a={}, b={})", (Supplier) () -> new ObjectMessage(1), (Supplier)
+ () -> new ObjectMessage(2));
+ logger.traceExit(msg, 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ void flowTracingString_SupplierOfStrings() {
+ final EntryMessage msg =
+ logger.traceEntry("doFoo(a={}, b={})", (Supplier) () -> "1", (Supplier) () -> "2");
+ logger.traceExit(msg, 3);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).startsWith("ENTER[ FLOW ] TRACE Enter").contains("doFoo(a=1, b=2)");
+ assertThat(results.get(1)).startsWith("EXIT[ FLOW ] TRACE Exit").contains("doFoo(a=1, b=2): 3");
+ }
+
+ @Test
+ void flowTracingNoFormat() {
+ logger.traceEntry(null, 1, "2", new ObjectMessage(3));
+ logger.traceExit((String) null, 4);
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0)).isEqualTo("ENTER[ FLOW ] TRACE Enter params(1, 2, 3)");
+ assertThat(results.get(1)).isEqualTo("EXIT[ FLOW ] TRACE Exit with(4)");
+ }
+
+ @Test
+ void catching() {
+ try {
+ throw new NullPointerException();
+ } catch (final Exception e) {
+ logger.catching(e);
+ assertEquals(1, results.size());
+ assertThat(
+ "Incorrect Catching",
+ results.get(0),
+ startsWith("CATCHING[ EXCEPTION ] ERROR Catching java.lang.NullPointerException"));
+ }
+ }
+
+ @Test
+ void debug() {
+ logger.debug("Debug message");
+ assertEquals(1, results.size());
+ assertTrue(results.get(0).startsWith(" DEBUG Debug message"), "Incorrect message");
+ }
+
+ @Test
+ void debugObject() {
+ logger.debug(new Date());
+ assertEquals(1, results.size());
+ assertTrue(results.get(0).length() > 7, "Invalid length");
+ }
+
+ @Test
+ void debugWithParms() {
+ logger.debug("Hello, {}", "World");
+ assertEquals(1, results.size());
+ assertTrue(results.get(0).startsWith(" DEBUG Hello, World"), "Incorrect substitution");
+ }
+
+ @Test
+ void debugWithParmsAndThrowable() {
+ logger.debug("Hello, {}", "World", new RuntimeException("Test Exception"));
+ assertEquals(1, results.size());
+ assertTrue(
+ results.get(0).startsWith(" DEBUG Hello, World java.lang.RuntimeException: Test Exception"),
+ "Unexpected results: " + results.get(0));
+ }
+
+ @Test
+ @ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
+ void getFormatterLogger() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger = (TestLogger) LogManager.getFormatterLogger();
+ final TestLogger altLogger = (TestLogger) LogManager.getFormatterLogger(getClass());
+ assertEquals(testLogger.getName(), altLogger.getName());
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
+ void getFormatterLogger_Class() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getFormatterLogger(TestStringFormatterMessageFactory.class);
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ private static void assertMessageFactoryInstanceOf(MessageFactory factory, final Class> cls) {
+ if (factory instanceof MessageFactory2Adapter) {
+ factory = ((MessageFactory2Adapter) factory).getOriginal();
+ }
+ assertTrue(factory.getClass().isAssignableFrom(cls));
+ }
+
+ @Test
+ @ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
+ void getFormatterLogger_Object() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getFormatterLogger(new TestStringFormatterMessageFactory());
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ @Test
+ @ResourceLock(value = org.junit.jupiter.api.parallel.Resources.LOCALE, mode = ResourceAccessMode.READ)
+ void getFormatterLogger_String() {
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getFormatterLogger("getLogger_String_StringFormatterMessageFactory");
+ assertNotNull(testLogger);
+ assertMessageFactoryInstanceOf(testLogger.getMessageFactory(), StringFormatterMessageFactory.class);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLogger_Class_ParameterizedMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getLogger(TestParameterizedMessageFactory.class, messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLogger_Class_StringFormatterMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final TestLogger testLogger = (TestLogger)
+ LogManager.getLogger(TestStringFormatterMessageFactory.class, StringFormatterMessageFactory.INSTANCE);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(StringFormatterMessageFactory.INSTANCE, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLogger_Object_ParameterizedMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getLogger(new TestParameterizedMessageFactory(), messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
+ }
+
+ private void assertEqualMessageFactory(final MessageFactory messageFactory, final TestLogger testLogger) {
+ MessageFactory actual = testLogger.getMessageFactory();
+ if (actual instanceof MessageFactory2Adapter) {
+ actual = ((MessageFactory2Adapter) actual).getOriginal();
+ }
+ assertEquals(messageFactory, actual);
+ }
+
+ @Test
+ void getLogger_Object_StringFormatterMessageFactory() {
+ // The TestLogger logger was already created in an instance variable for this class.
+ // The message factory is only used when the logger is created.
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getLogger(new TestStringFormatterMessageFactory(), messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLogger_String_MessageFactoryMismatch() {
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getLogger("getLogger_String_MessageFactoryMismatch", messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ final TestLogger testLogger2 = (TestLogger)
+ LogManager.getLogger("getLogger_String_MessageFactoryMismatch", ParameterizedMessageFactory.INSTANCE);
+ assertNotNull(testLogger2);
+ // TODO: How to test?
+ // This test context always creates new loggers, other test context impls I tried fail other tests.
+ // assertEquals(messageFactory, testLogger2.getMessageFactory());
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLogger_String_ParameterizedMessageFactory() {
+ final ParameterizedMessageFactory messageFactory = ParameterizedMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getLogger("getLogger_String_ParameterizedMessageFactory", messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG " + Integer.MAX_VALUE, testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLogger_String_SimpleMessageFactory() {
+ final SimpleMessageFactory messageFactory = SimpleMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getLogger("getLogger_String_StringFormatterMessageFactory", messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("{} %,d {foo}", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(" DEBUG {} %,d {foo}", testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLogger_String_StringFormatterMessageFactory() {
+ final StringFormatterMessageFactory messageFactory = StringFormatterMessageFactory.INSTANCE;
+ final TestLogger testLogger =
+ (TestLogger) LogManager.getLogger("getLogger_String_StringFormatterMessageFactory", messageFactory);
+ assertNotNull(testLogger);
+ assertEqualMessageFactory(messageFactory, testLogger);
+ testLogger.debug("%,d", Integer.MAX_VALUE);
+ assertEquals(1, testLogger.getEntries().size());
+ assertEquals(
+ String.format(" DEBUG %,d", Integer.MAX_VALUE),
+ testLogger.getEntries().get(0));
+ }
+
+ @Test
+ void getLoggerByClass() {
+ final Logger classLogger = LogManager.getLogger(LoggerTest.class);
+ assertNotNull(classLogger);
+ }
+
+ @Test
+ void getLoggerByNullClass() {
+ // Returns a SimpleLogger
+ assertNotNull(LogManager.getLogger((Class>) null));
+ }
+
+ @Test
+ void getLoggerByNullObject() {
+ // Returns a SimpleLogger
+ assertNotNull(LogManager.getLogger((Object) null));
+ }
+
+ @Test
+ void getLoggerByNullString() {
+ // Returns a SimpleLogger
+ assertNotNull(LogManager.getLogger((String) null));
+ }
+
+ @Test
+ void getLoggerByObject() {
+ final Logger classLogger = LogManager.getLogger(this);
+ assertNotNull(classLogger);
+ assertEquals(classLogger, LogManager.getLogger(LoggerTest.class));
+ }
+
+ @Test
+ void getRootLogger() {
+ assertNotNull(LogManager.getRootLogger());
+ assertNotNull(LogManager.getLogger(Strings.EMPTY));
+ assertNotNull(LogManager.getLogger(LogManager.ROOT_LOGGER_NAME));
+ assertEquals(LogManager.getRootLogger(), LogManager.getLogger(Strings.EMPTY));
+ assertEquals(LogManager.getRootLogger(), LogManager.getLogger(LogManager.ROOT_LOGGER_NAME));
+ }
+
+ @Test
+ void isAllEnabled() {
+ assertTrue(logger.isEnabled(Level.ALL), "Incorrect level");
+ }
+
+ @Test
+ void isDebugEnabled() {
+ assertTrue(logger.isDebugEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.DEBUG), "Incorrect level");
+ }
+
+ @Test
+ void isErrorEnabled() {
+ assertTrue(logger.isErrorEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.ERROR), "Incorrect level");
+ }
+
+ @Test
+ void isFatalEnabled() {
+ assertTrue(logger.isFatalEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.FATAL), "Incorrect level");
+ }
+
+ @Test
+ void isInfoEnabled() {
+ assertTrue(logger.isInfoEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.INFO), "Incorrect level");
+ }
+
+ @Test
+ void isOffEnabled() {
+ assertTrue(logger.isEnabled(Level.OFF), "Incorrect level");
+ }
+
+ @Test
+ void isTraceEnabled() {
+ assertTrue(logger.isTraceEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.TRACE), "Incorrect level");
+ }
+
+ @Test
+ void isWarnEnabled() {
+ assertTrue(logger.isWarnEnabled(), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.WARN), "Incorrect level");
+ }
+
+ @Test
+ void isAllEnabledWithMarker() {
+ assertTrue(logger.isEnabled(Level.ALL, marker), "Incorrect level");
+ }
+
+ @Test
+ void isDebugEnabledWithMarker() {
+ assertTrue(logger.isDebugEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.DEBUG, marker), "Incorrect level");
+ }
+
+ @Test
+ void isErrorEnabledWithMarker() {
+ assertTrue(logger.isErrorEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.ERROR, marker), "Incorrect level");
+ }
+
+ @Test
+ void isFatalEnabledWithMarker() {
+ assertTrue(logger.isFatalEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.FATAL, marker), "Incorrect level");
+ }
+
+ @Test
+ void isInfoEnabledWithMarker() {
+ assertTrue(logger.isInfoEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.INFO, marker), "Incorrect level");
+ }
+
+ @Test
+ void isOffEnabledWithMarker() {
+ assertTrue(logger.isEnabled(Level.OFF, marker), "Incorrect level");
+ }
+
+ @Test
+ void isTraceEnabledWithMarker() {
+ assertTrue(logger.isTraceEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.TRACE, marker), "Incorrect level");
+ }
+
+ @Test
+ void isWarnEnabledWithMarker() {
+ assertTrue(logger.isWarnEnabled(marker), "Incorrect level");
+ assertTrue(logger.isEnabled(Level.WARN, marker), "Incorrect level");
+ }
+
+ @Test
+ @UsingThreadContextMap
+ void mdc() {
+ ThreadContext.put("TestYear", Integer.toString(2010));
+ logger.debug("Debug message");
+ final String testYear = ThreadContext.get("TestYear");
+ assertNotNull(testYear, "Test Year is null");
+ assertEquals("2010", testYear, "Incorrect test year: " + testYear);
+ ThreadContext.clearMap();
+ logger.debug("Debug message");
+ assertEquals(2, results.size());
+ System.out.println("Log line 1: " + results.get(0));
+ System.out.println("log line 2: " + results.get(1));
+ assertTrue(
+ results.get(0).startsWith(" DEBUG Debug message {TestYear=2010}"), "Incorrect MDC: " + results.get(0));
+ assertTrue(results.get(1).startsWith(" DEBUG Debug message"), "MDC not cleared?: " + results.get(1));
+ }
+
+ @Test
+ void printf() {
+ logger.printf(Level.DEBUG, "Debug message %d", 1);
+ logger.printf(Level.DEBUG, MarkerManager.getMarker("Test"), "Debug message %d", 2);
+ assertEquals(2, results.size());
+ assertThat("Incorrect message", results.get(0), startsWith(" DEBUG Debug message 1"));
+ assertThat("Incorrect message", results.get(1), startsWith("Test DEBUG Debug message 2"));
+ }
+
+ @BeforeEach
+ void setup() {
+ results.clear();
+ }
+
+ @Test
+ void structuredData() {
+ ThreadContext.put("loginId", "JohnDoe");
+ ThreadContext.put("ipAddress", "192.168.0.120");
+ ThreadContext.put("locale", Locale.US.getDisplayName());
+ final StructuredDataMessage msg = new StructuredDataMessage("Audit@18060", "Transfer Complete", "Transfer");
+ msg.put("ToAccount", "123456");
+ msg.put("FromAccount", "123457");
+ msg.put("Amount", "200.00");
+ logger.info(MarkerManager.getMarker("EVENT"), msg);
+ ThreadContext.clearMap();
+ assertEquals(1, results.size());
+ assertThat(
+ "Incorrect structured data: ",
+ results.get(0),
+ startsWith(
+ "EVENT INFO Transfer [Audit@18060 Amount=\"200.00\" FromAccount=\"123457\" ToAccount=\"123456\"] Transfer Complete"));
+ }
+
+ @Test
+ void throwing() {
+ logger.throwing(new IllegalArgumentException("Test Exception"));
+ assertEquals(1, results.size());
+ assertThat(
+ "Incorrect Throwing",
+ results.get(0),
+ startsWith("THROWING[ EXCEPTION ] ERROR Throwing java.lang.IllegalArgumentException: Test Exception"));
+ }
+
+ private static class Response {
+ int status;
+ String message;
+
+ public Response(final int status, final String message) {
+ this.status = status;
+ this.message = message;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(final int status) {
+ this.status = status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(final String message) {
+ this.message = message;
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
new file mode 100644
index 00000000000..411dd5766cd
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/MarkerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.logging.log4j.test.junit.Log4jStaticResources;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+
+@ResourceLock(value = Log4jStaticResources.MARKER_MANAGER, mode = ResourceAccessMode.READ_WRITE)
+class MarkerTest {
+
+ @BeforeEach
+ void setUp() {
+ MarkerManager.clear();
+ }
+
+ @Test
+ void testGetMarker() {
+ final Marker expected = MarkerManager.getMarker("A");
+ assertNull(expected.getParents());
+ }
+
+ @Test
+ void testGetMarkerWithParents() {
+ final Marker expected = MarkerManager.getMarker("A");
+ final Marker p1 = MarkerManager.getMarker("P1");
+ p1.addParents(MarkerManager.getMarker("PP1"));
+ final Marker p2 = MarkerManager.getMarker("P2");
+ expected.addParents(p1);
+ expected.addParents(p2);
+ assertEquals(2, expected.getParents().length);
+ }
+
+ @Test
+ void testHasParents() {
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ final Marker existing = MarkerManager.getMarker("EXISTING");
+ assertFalse(existing.hasParents());
+ existing.setParents(parent);
+ assertTrue(existing.hasParents());
+ }
+
+ @Test
+ void testMarker() {
+ // root (level 1)
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ // level 2
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(parent);
+ final Marker test2 = MarkerManager.getMarker("TEST2").addParents(parent);
+ assertTrue(test1.isInstanceOf(parent), "TEST1 is not an instance of PARENT");
+ assertTrue(test2.isInstanceOf(parent), "TEST2 is not an instance of PARENT");
+ assertFalse(parent.isInstanceOf(test1));
+ assertFalse(parent.isInstanceOf(test2));
+ // level 3
+ final Marker test3 = MarkerManager.getMarker("TEST3").addParents(test2);
+ assertTrue(test3.isInstanceOf(test2));
+ assertTrue(test3.isInstanceOf("TEST2"));
+ assertTrue(test3.isInstanceOf("PARENT"));
+ assertTrue(test2.isInstanceOf("PARENT"));
+ assertFalse(parent.isInstanceOf(test3));
+ assertFalse(parent.isInstanceOf(test3));
+ }
+
+ @Test
+ void testMarkerSharedIntermediaryMarker() {
+ final Marker parent1 = MarkerManager.getMarker("PARENT1");
+ final Marker parent2 = MarkerManager.getMarker("PARENT2");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(parent1, parent2);
+ assertTrue(test1.isInstanceOf(parent1));
+ // Leaf
+ final Marker leaf = MarkerManager.getMarker("LEAF").setParents(test1);
+ assertTrue(leaf.isInstanceOf("TEST1"));
+ assertTrue(leaf.isInstanceOf("PARENT1"));
+ assertTrue(leaf.isInstanceOf("PARENT2"));
+ }
+
+ @Test
+ void testMultipleParents() {
+ final Marker parent1 = MarkerManager.getMarker("PARENT1");
+ final Marker parent2 = MarkerManager.getMarker("PARENT2");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(parent1, parent2);
+ final Marker test2 = MarkerManager.getMarker("TEST2").addParents(parent1, parent2);
+ assertTrue(test1.isInstanceOf(parent1), "TEST1 is not an instance of PARENT1");
+ assertTrue(test1.isInstanceOf("PARENT1"), "TEST1 is not an instance of PARENT1");
+ assertTrue(test1.isInstanceOf(parent2), "TEST1 is not an instance of PARENT2");
+ assertTrue(test1.isInstanceOf("PARENT2"), "TEST1 is not an instance of PARENT2");
+ assertTrue(test2.isInstanceOf(parent1), "TEST2 is not an instance of PARENT1");
+ assertTrue(test2.isInstanceOf("PARENT1"), "TEST2 is not an instance of PARENT1");
+ assertTrue(test2.isInstanceOf(parent2), "TEST2 is not an instance of PARENT2");
+ assertTrue(test2.isInstanceOf("PARENT2"), "TEST2 is not an instance of PARENT2");
+ }
+
+ @Test
+ void testAddToExistingParents() {
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ final Marker existing = MarkerManager.getMarker("EXISTING");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(existing);
+ test1.addParents(parent);
+ assertTrue(test1.isInstanceOf(parent), "TEST1 is not an instance of PARENT");
+ assertTrue(test1.isInstanceOf("PARENT"), "TEST1 is not an instance of PARENT");
+ assertTrue(test1.isInstanceOf(existing), "TEST1 is not an instance of EXISTING");
+ assertTrue(test1.isInstanceOf("EXISTING"), "TEST1 is not an instance of EXISTING");
+ }
+
+ @Test
+ void testDuplicateParents() {
+ final Marker parent = MarkerManager.getMarker("PARENT");
+ final Marker existing = MarkerManager.getMarker("EXISTING");
+ final Marker test1 = MarkerManager.getMarker("TEST1").setParents(existing);
+ test1.addParents(parent);
+ final Marker[] parents = test1.getParents();
+ test1.addParents(existing);
+ assertEquals(parents.length, test1.getParents().length, "duplicate add allowed");
+ test1.addParents(existing, MarkerManager.getMarker("EXTRA"));
+ assertEquals(parents.length + 1, test1.getParents().length, "incorrect add");
+ assertTrue(test1.isInstanceOf(parent), "TEST1 is not an instance of PARENT");
+ assertTrue(test1.isInstanceOf("PARENT"), "TEST1 is not an instance of PARENT");
+ assertTrue(test1.isInstanceOf(existing), "TEST1 is not an instance of EXISTING");
+ assertTrue(test1.isInstanceOf("EXISTING"), "TEST1 is not an instance of EXISTING");
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
new file mode 100644
index 00000000000..c4d9bebd81c
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/NoopThreadContextTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.apache.logging.log4j.test.junit.SetTestProperty;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link ThreadContext}.
+ */
+@SetTestProperty(key = "log4j2.disableThreadContext", value = "true")
+@SetTestProperty(key = "log4j2.disableThreadContextMap", value = "true")
+@UsingThreadContextMap
+class NoopThreadContextTest {
+
+ @Test
+ void testNoop() {
+ ThreadContext.put("Test", "Test");
+ final String value = ThreadContext.get("Test");
+ assertNull(value, "value was saved");
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/TestProvider.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/TestProvider.java
new file mode 100644
index 00000000000..1aa91ae529d
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/TestProvider.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+import org.apache.logging.log4j.spi.Provider;
+import org.apache.logging.log4j.test.TestLoggerContextFactory;
+
+/**
+ * Binding for the Log4j API.
+ */
+public class TestProvider extends Provider {
+ public TestProvider() {
+ super(5, CURRENT_VERSION, TestLoggerContextFactory.class);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
new file mode 100644
index 00000000000..8e398910d14
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextInheritanceTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.logging.log4j.spi.DefaultThreadContextMap;
+import org.apache.logging.log4j.test.ThreadContextUtilityClass;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
+import org.apache.logging.log4j.test.junit.UsingThreadContextMap;
+import org.apache.logging.log4j.test.junit.UsingThreadContextStack;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link ThreadContext}.
+ */
+@SetTestProperty(key = DefaultThreadContextMap.INHERITABLE_MAP, value = "true")
+@UsingThreadContextMap
+@UsingThreadContextStack
+class ThreadContextInheritanceTest {
+
+ @BeforeAll
+ static void setupClass() {
+ System.setProperty(DefaultThreadContextMap.INHERITABLE_MAP, "true");
+ ThreadContext.init();
+ }
+
+ @AfterAll
+ static void tearDownClass() {
+ System.clearProperty(DefaultThreadContextMap.INHERITABLE_MAP);
+ ThreadContext.init();
+ }
+
+ @Test
+ void testPush() {
+ ThreadContext.push("Hello");
+ ThreadContext.push("{} is {}", ThreadContextInheritanceTest.class.getSimpleName(), "running");
+ assertEquals(
+ "ThreadContextInheritanceTest is running", ThreadContext.pop(), "Incorrect parameterized stack value");
+ assertEquals("Hello", ThreadContext.pop(), "Incorrect simple stack value");
+ }
+
+ @Test
+ @Tag("performance")
+ void perfTest() {
+ ThreadContextUtilityClass.perfTest();
+ }
+
+ @Test
+ void testGetContextReturnsEmptyMapIfEmpty() {
+ ThreadContextUtilityClass.testGetContextReturnsEmptyMapIfEmpty();
+ }
+
+ @Test
+ void testGetContextReturnsMutableCopy() {
+ ThreadContextUtilityClass.testGetContextReturnsMutableCopy();
+ }
+
+ @Test
+ void testGetImmutableContextReturnsEmptyMapIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsEmptyMapIfEmpty();
+ }
+
+ @Test
+ void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfNonEmpty();
+ }
+
+ @Test
+ void testGetImmutableContextReturnsImmutableMapIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfEmpty();
+ }
+
+ @Test
+ void testGetImmutableStackReturnsEmptyStackIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableStackReturnsEmptyStackIfEmpty();
+ }
+
+ @Test
+ void testPut() {
+ ThreadContextUtilityClass.testPut();
+ }
+
+ @Test
+ void testRemove() {
+ ThreadContext.clearMap();
+ assertNull(ThreadContext.get("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertEquals("testValue", ThreadContext.get("testKey"));
+
+ ThreadContext.remove("testKey");
+ assertNull(ThreadContext.get("testKey"));
+ assertTrue(ThreadContext.isEmpty());
+ }
+
+ @Test
+ void testContainsKey() {
+ ThreadContext.clearMap();
+ assertFalse(ThreadContext.containsKey("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertTrue(ThreadContext.containsKey("testKey"));
+
+ ThreadContext.remove("testKey");
+ assertFalse(ThreadContext.containsKey("testKey"));
+ }
+
+ private static class TestThread extends Thread {
+
+ private final StringBuilder sb;
+
+ public TestThread(final StringBuilder sb) {
+ this.sb = sb;
+ }
+
+ @Override
+ public void run() {
+ final String greeting = ThreadContext.get("Greeting");
+ if (greeting == null) {
+ sb.append("null");
+ } else {
+ sb.append(greeting);
+ }
+ ThreadContext.clearMap();
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
new file mode 100644
index 00000000000..a06fd7c2611
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ThreadContextTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.logging.log4j.test.ThreadContextUtilityClass;
+import org.apache.logging.log4j.test.junit.UsingAnyThreadContext;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+@UsingAnyThreadContext
+class ThreadContextTest {
+
+ @Test
+ void testPush() {
+ ThreadContext.push("Hello");
+ ThreadContext.push("{} is {}", ThreadContextTest.class.getSimpleName(), "running");
+ assertEquals("ThreadContextTest is running", ThreadContext.pop(), "Incorrect parameterized stack value");
+ assertEquals("Hello", ThreadContext.pop(), "Incorrect simple stack value");
+ }
+
+ @Test
+ void testInheritanceSwitchedOffByDefault() throws Exception {
+ ThreadContext.put("Greeting", "Hello");
+ StringBuilder sb = new StringBuilder();
+ TestThread thread = new TestThread(sb);
+ thread.start();
+ thread.join();
+ String str = sb.toString();
+ assertEquals("null", str, "Unexpected ThreadContext value. Expected null. Actual " + str);
+ sb = new StringBuilder();
+ thread = new TestThread(sb);
+ thread.start();
+ thread.join();
+ str = sb.toString();
+ assertEquals("null", str, "Unexpected ThreadContext value. Expected null. Actual " + str);
+ }
+
+ @Test
+ @Tag("performance")
+ void perfTest() {
+ ThreadContextUtilityClass.perfTest();
+ }
+
+ @Test
+ void testGetContextReturnsEmptyMapIfEmpty() {
+ ThreadContextUtilityClass.testGetContextReturnsEmptyMapIfEmpty();
+ }
+
+ @Test
+ void testGetContextReturnsMutableCopy() {
+ ThreadContextUtilityClass.testGetContextReturnsMutableCopy();
+ }
+
+ @Test
+ void testGetImmutableContextReturnsEmptyMapIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsEmptyMapIfEmpty();
+ }
+
+ @Test
+ void testGetImmutableContextReturnsImmutableMapIfNonEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfNonEmpty();
+ }
+
+ @Test
+ void testGetImmutableContextReturnsImmutableMapIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableContextReturnsImmutableMapIfEmpty();
+ }
+
+ @Test
+ void testGetImmutableStackReturnsEmptyStackIfEmpty() {
+ ThreadContextUtilityClass.testGetImmutableStackReturnsEmptyStackIfEmpty();
+ }
+
+ @Test
+ void testPut() {
+ ThreadContextUtilityClass.testPut();
+ }
+
+ @Test
+ void testPutIfNotNull() {
+ ThreadContext.clearMap();
+ assertNull(ThreadContext.get("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertEquals("testValue", ThreadContext.get("testKey"));
+ assertEquals("testValue", ThreadContext.get("testKey"), "Incorrect value in test key");
+ ThreadContext.putIfNull("testKey", "new Value");
+ assertEquals("testValue", ThreadContext.get("testKey"), "Incorrect value in test key");
+ ThreadContext.clearMap();
+ }
+
+ @Test
+ void testPutAll() {
+ assertTrue(ThreadContext.isEmpty());
+ assertFalse(ThreadContext.containsKey("key"));
+ final int mapSize = 10;
+ final Map newMap = new HashMap<>(mapSize);
+ for (int i = 1; i <= mapSize; i++) {
+ newMap.put("key" + i, "value" + i);
+ }
+ ThreadContext.putAll(newMap);
+ assertFalse(ThreadContext.isEmpty());
+ for (int i = 1; i <= mapSize; i++) {
+ assertTrue(ThreadContext.containsKey("key" + i));
+ assertEquals("value" + i, ThreadContext.get("key" + i));
+ }
+ }
+
+ @Test
+ void testRemove() {
+ assertNull(ThreadContext.get("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertEquals("testValue", ThreadContext.get("testKey"));
+
+ ThreadContext.remove("testKey");
+ assertNull(ThreadContext.get("testKey"));
+ assertTrue(ThreadContext.isEmpty());
+ }
+
+ @Test
+ void testRemoveAll() {
+ ThreadContext.put("testKey1", "testValue1");
+ ThreadContext.put("testKey2", "testValue2");
+ assertEquals("testValue1", ThreadContext.get("testKey1"));
+ assertEquals("testValue2", ThreadContext.get("testKey2"));
+ assertFalse(ThreadContext.isEmpty());
+
+ ThreadContext.removeAll(Arrays.asList("testKey1", "testKey2"));
+ assertNull(ThreadContext.get("testKey1"));
+ assertNull(ThreadContext.get("testKey2"));
+ assertTrue(ThreadContext.isEmpty());
+ }
+
+ @Test
+ void testContainsKey() {
+ assertFalse(ThreadContext.containsKey("testKey"));
+ ThreadContext.put("testKey", "testValue");
+ assertTrue(ThreadContext.containsKey("testKey"));
+
+ ThreadContext.remove("testKey");
+ assertFalse(ThreadContext.containsKey("testKey"));
+ }
+
+ private static class TestThread extends Thread {
+
+ private final StringBuilder sb;
+
+ public TestThread(final StringBuilder sb) {
+ this.sb = sb;
+ }
+
+ @Override
+ public void run() {
+ final String greeting = ThreadContext.get("Greeting");
+ if (greeting == null) {
+ sb.append("null");
+ } else {
+ sb.append(greeting);
+ }
+ ThreadContext.clearMap();
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
new file mode 100644
index 00000000000..dcc16803baa
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/TraceLoggingTest.java
@@ -0,0 +1,375 @@
+/*
+ * 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;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.apache.logging.log4j.message.DefaultFlowMessageFactory;
+import org.apache.logging.log4j.message.EntryMessage;
+import org.apache.logging.log4j.message.FlowMessageFactory;
+import org.apache.logging.log4j.message.Message;
+import org.apache.logging.log4j.message.ObjectMessage;
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.logging.log4j.message.ReusableParameterizedMessage;
+import org.apache.logging.log4j.message.ReusableParameterizedMessageTest;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.spi.AbstractLogger;
+import org.junit.jupiter.api.Test;
+
+class TraceLoggingTest extends AbstractLogger {
+ static final StringBuilder CHAR_SEQ = new StringBuilder("CharSeq");
+ private int charSeqCount;
+ private int objectCount;
+
+ private static class LogEvent {
+
+ String markerName;
+ Message data;
+ Throwable t;
+
+ public LogEvent(final String markerName, final Message data, final Throwable t) {
+ this.markerName = markerName;
+ this.data = data;
+ this.t = t;
+ }
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ private static Level currentLevel;
+
+ private LogEvent currentEvent;
+
+ private static final Throwable t = new UnsupportedOperationException("Test");
+
+ private static final Class obj = AbstractLogger.class;
+ private static final String pattern = "{}, {}";
+ private static final String p1 = "Long Beach";
+
+ private static final String p2 = "California";
+ private static final Message charSeq = new SimpleMessage(CHAR_SEQ);
+ private static final Message simple = new SimpleMessage("Hello");
+ private static final Message object = new ObjectMessage(obj);
+
+ private static final Message param = new ParameterizedMessage(pattern, p1, p2);
+
+ private static final String marker = "TEST";
+
+ @Override
+ public Level getLevel() {
+ return currentLevel;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Message data, final Throwable t) {
+ return true;
+ // assertTrue("Incorrect Level. Expected " + currentLevel + ", actual " + level,
+ // level.equals(currentLevel));
+ // if (marker == null) {
+ // if (currentEvent.markerName != null) {
+ // fail("Incorrect marker. Expected " + currentEvent.markerName + ", actual is null");
+ // }
+ // } else {
+ // if (currentEvent.markerName == null) {
+ // fail("Incorrect marker. Expected null. Actual is " + marker.getName());
+ // } else {
+ // assertTrue("Incorrect marker. Expected " + currentEvent.markerName + ", actual " +
+ // marker.getName(), currentEvent.markerName.equals(marker.getName()));
+ // }
+ // }
+ // if (data == null) {
+ // if (currentEvent.data != null) {
+ // fail("Incorrect message. Expected " + currentEvent.data + ", actual is null");
+ // }
+ // } else {
+ // if (currentEvent.data == null) {
+ // fail("Incorrect message. Expected null. Actual is " + data.getFormattedMessage());
+ // } else {
+ // assertTrue("Incorrect message type. Expected " + currentEvent.data + ", actual " + data,
+ // data.getClass().isAssignableFrom(currentEvent.data.getClass()));
+ // assertTrue("Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ",
+ // actual " +
+ // data.getFormattedMessage(),
+ // currentEvent.data.getFormattedMessage().equals(data.getFormattedMessage()));
+ // }
+ // }
+ // if (t == null) {
+ // if (currentEvent.t != null) {
+ // fail("Incorrect Throwable. Expected " + currentEvent.t + ", actual is null");
+ // }
+ // } else {
+ // if (currentEvent.t == null) {
+ // fail("Incorrect Throwable. Expected null. Actual is " + t);
+ // } else {
+ // assertTrue("Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t,
+ // currentEvent.t.equals(t));
+ // }
+ // }
+ // return true;
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final CharSequence data, final Throwable t) {
+ charSeqCount++;
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), t);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final Object data, final Throwable t) {
+ objectCount++;
+ return isEnabled(level, marker, new ObjectMessage(data), t);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data) {
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data, final Object... p1) {
+ return isEnabled(level, marker, new ParameterizedMessage(data, p1), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String message, final Object p0) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level, final Marker marker, final String message, final Object p0, final Object p1) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8) {
+ return isEnabled(level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8), null);
+ }
+
+ @Override
+ public boolean isEnabled(
+ final Level level,
+ final Marker marker,
+ final String message,
+ final Object p0,
+ final Object p1,
+ final Object p2,
+ final Object p3,
+ final Object p4,
+ final Object p5,
+ final Object p6,
+ final Object p7,
+ final Object p8,
+ final Object p9) {
+ return isEnabled(
+ level, marker, new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9), null);
+ }
+
+ @Override
+ public boolean isEnabled(final Level level, final Marker marker, final String data, final Throwable t) {
+ return isEnabled(level, marker, (Message) new SimpleMessage(data), t);
+ }
+
+ @Override
+ public void logMessage(
+ final String fqcn, final Level level, final Marker marker, final Message data, final Throwable t) {
+ assertEquals(level, currentLevel, "Incorrect Level. Expected " + currentLevel + ", actual " + level);
+ if (marker == null) {
+ if (currentEvent.markerName != null) {
+ fail("Incorrect marker. Expected " + currentEvent.markerName + ", actual is null");
+ }
+ } else if (currentEvent.markerName == null) {
+ fail("Incorrect marker. Expected null. Actual is " + marker.getName());
+ } else {
+ assertEquals(
+ currentEvent.markerName,
+ marker.getName(),
+ "Incorrect marker. Expected " + currentEvent.markerName + ", actual " + marker.getName());
+ }
+ if (data == null) {
+ if (currentEvent.data != null) {
+ fail("Incorrect message. Expected " + currentEvent.data + ", actual is null");
+ }
+ } else if (currentEvent.data == null) {
+ fail("Incorrect message. Expected null. Actual is " + data.getFormattedMessage());
+ } else {
+ assertTrue(
+ data.getClass().isAssignableFrom(currentEvent.data.getClass()),
+ "Incorrect message type. Expected " + currentEvent.data + ", actual " + data);
+ assertEquals(
+ currentEvent.data.getFormattedMessage(),
+ data.getFormattedMessage(),
+ "Incorrect message. Expected " + currentEvent.data.getFormattedMessage() + ", actual "
+ + data.getFormattedMessage());
+ }
+ if (t == null) {
+ if (currentEvent.t != null) {
+ fail("Incorrect Throwable. Expected " + currentEvent.t + ", actual is null");
+ }
+ } else if (currentEvent.t == null) {
+ fail("Incorrect Throwable. Expected null. Actual is " + t);
+ } else {
+ assertEquals(currentEvent.t, t, "Incorrect Throwable. Expected " + currentEvent.t + ", actual " + t);
+ }
+ }
+
+ @Test
+ void testTraceEntryExit() {
+ currentLevel = Level.TRACE;
+ final FlowMessageFactory fact = new DefaultFlowMessageFactory();
+
+ final ParameterizedMessage paramMsg = new ParameterizedMessage("Tracy {}", "Logan");
+ currentEvent = new LogEvent(ENTRY_MARKER.getName(), fact.newEntryMessage(paramMsg), null);
+ final EntryMessage entry = traceEntry("Tracy {}", "Logan");
+
+ final ReusableParameterizedMessage msg =
+ ReusableParameterizedMessageTest.set(new ReusableParameterizedMessage(), "Tracy {}", "Logan");
+ ReusableParameterizedMessageTest.set(msg, "Some other message {}", 123);
+ currentEvent = new LogEvent(null, msg, null);
+ trace("Some other message {}", 123);
+
+ // ensure original entry message not overwritten
+ assertEquals("Tracy Logan", entry.getMessage().getFormattedMessage());
+
+ currentEvent = new LogEvent(EXIT_MARKER.getName(), fact.newExitMessage(entry), null);
+ traceExit(entry);
+
+ // ensure original entry message not overwritten
+ assertEquals("Tracy Logan", entry.getMessage().getFormattedMessage());
+ }
+
+ @Test
+ void testTraceEntryMessage() {
+ currentLevel = Level.TRACE;
+ final FlowMessageFactory fact = new DefaultFlowMessageFactory();
+
+ final ParameterizedMessage paramMsg = new ParameterizedMessage("Tracy {}", "Logan");
+ currentEvent = new LogEvent(ENTRY_MARKER.getName(), fact.newEntryMessage(paramMsg), null);
+
+ final ReusableParameterizedMessage msg =
+ ReusableParameterizedMessageTest.set(new ReusableParameterizedMessage(), "Tracy {}", "Logan");
+ final EntryMessage entry = traceEntry(msg);
+
+ ReusableParameterizedMessageTest.set(msg, "Some other message {}", 123);
+ currentEvent = new LogEvent(null, msg, null);
+ trace("Some other message {}", 123);
+
+ // ensure original entry message not overwritten
+ assertEquals("Tracy Logan", entry.getMessage().getFormattedMessage());
+
+ currentEvent = new LogEvent(EXIT_MARKER.getName(), fact.newExitMessage(entry), null);
+ traceExit(entry);
+
+ // ensure original entry message not overwritten
+ assertEquals("Tracy Logan", entry.getMessage().getFormattedMessage());
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/internal/DefaultLogBuilderTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/internal/DefaultLogBuilderTest.java
new file mode 100644
index 00000000000..4bff93565cd
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/internal/DefaultLogBuilderTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.internal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.logging.log4j.LogBuilder;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.test.TestLogger;
+import org.junit.jupiter.api.Test;
+
+class DefaultLogBuilderTest {
+
+ private final TestLogger logger1 = (TestLogger) LogManager.getLogger(DefaultLogBuilderTest.class);
+ private final TestLogger logger2 = (TestLogger) LogManager.getLogger("second.logger");
+
+ @Test
+ void testConcurrentUsage() {
+ logger1.getEntries().clear();
+ logger2.getEntries().clear();
+ final List logBuilders =
+ Arrays.asList(logger1.atDebug(), logger1.atInfo(), logger2.atDebug(), logger2.atInfo());
+ logBuilders.forEach(logBuilder -> logBuilder.log("Hello LogBuilder!"));
+ assertThat(logger1.getEntries())
+ .hasSize(2)
+ .containsExactly(" DEBUG Hello LogBuilder!", " INFO Hello LogBuilder!");
+ assertThat(logger2.getEntries())
+ .hasSize(2)
+ .containsExactly(" DEBUG Hello LogBuilder!", " INFO Hello LogBuilder!");
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/internal/map/UnmodifiableArrayBackedMapTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/internal/map/UnmodifiableArrayBackedMapTest.java
new file mode 100644
index 00000000000..612840bbf5e
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/internal/map/UnmodifiableArrayBackedMapTest.java
@@ -0,0 +1,397 @@
+/*
+ * 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.internal.map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.TriConsumer;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class UnmodifiableArrayBackedMapTest {
+ private static final int TEST_DATA_SIZE = 5;
+
+ private HashMap getTestParameters() {
+ return getTestParameters(TEST_DATA_SIZE);
+ }
+
+ private HashMap getTestParameters(int numParams) {
+ HashMap params = new LinkedHashMap<>();
+ for (int i = 0; i < numParams; i++) {
+ params.put("" + i, "value" + i);
+ }
+
+ return params;
+ }
+
+ @Test
+ void testCopyAndPut() {
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
+ testMap = testMap.copyAndPut("6", "value6");
+ assertTrue(testMap.containsKey("6"));
+ assertEquals("value6", testMap.get("6"));
+
+ testMap = testMap.copyAndPut("6", "another value");
+ assertTrue(testMap.containsKey("6"));
+ assertEquals("another value", testMap.get("6"));
+
+ HashMap newValues = getTestParameters();
+ testMap = testMap.copyAndPutAll(newValues);
+ assertEquals("value1", testMap.get("1"));
+ assertEquals("value4", testMap.get("4"));
+ assertEquals("another value", testMap.get("6"));
+ }
+
+ @Test
+ void testCopyAndRemove() {
+ // general removing from well-populated set
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+ testMap = testMap.copyAndRemove("2");
+ testMap = testMap.copyAndRemove("not_present");
+ assertEquals(4, testMap.size());
+ assertFalse(testMap.containsKey("2"));
+ assertTrue(testMap.containsKey("1"));
+ assertFalse(testMap.containsValue("value2"));
+
+ // test removing from empty set
+ testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test", "test");
+ testMap = testMap.copyAndRemove("test");
+ assertTrue(testMap.isEmpty());
+
+ // test removing first of two elements
+ testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test1", "test1");
+ testMap = testMap.copyAndPut("test2", "test2");
+ testMap = testMap.copyAndRemove("test1");
+ assertFalse(testMap.containsKey("test1"));
+ assertTrue(testMap.containsKey("test2"));
+
+ // test removing second of two elements
+ testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test1", "test1");
+ testMap = testMap.copyAndPut("test2", "test2");
+ testMap = testMap.copyAndRemove("test2");
+ assertTrue(testMap.containsKey("test1"));
+ assertFalse(testMap.containsKey("test2"));
+ }
+
+ @Test
+ void testCopyAndRemoveAll() {
+ HashMap initialMapContents = getTestParameters(15);
+ initialMapContents.put("extra_key", "extra_value");
+
+ HashSet keysToRemove = new LinkedHashSet<>();
+ keysToRemove.add("3");
+ keysToRemove.add("11");
+ keysToRemove.add("definitely_not_found");
+
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(initialMapContents);
+ testMap = testMap.copyAndRemoveAll(keysToRemove);
+ assertEquals(14, testMap.size());
+
+ assertFalse(testMap.containsKey("3"));
+ assertFalse(testMap.containsValue("value3"));
+ assertFalse(testMap.containsKey("11"));
+ assertFalse(testMap.containsValue("value11"));
+
+ assertTrue(testMap.containsKey("extra_key"));
+ assertTrue(testMap.containsValue("extra_value"));
+ assertTrue(testMap.containsKey("1"));
+ assertTrue(testMap.containsValue("value1"));
+ assertTrue(testMap.containsKey("0"));
+ assertTrue(testMap.containsValue("value0"));
+ assertTrue(testMap.containsKey("14"));
+ assertTrue(testMap.containsValue("value14"));
+
+ testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(initialMapContents);
+ UnmodifiableArrayBackedMap testMapWithArrayListRemoval =
+ testMap.copyAndRemoveAll(new ArrayList<>(keysToRemove));
+ UnmodifiableArrayBackedMap testMapWithSetRemoval = testMap.copyAndRemoveAll(keysToRemove);
+ assertEquals(testMapWithSetRemoval, testMapWithArrayListRemoval);
+
+ testMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
+ assertEquals(0, testMap.copyAndRemoveAll(initialMapContents.keySet()).size());
+
+ testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test", "test");
+ assertEquals(1, testMap.copyAndRemoveAll(initialMapContents.keySet()).size());
+ testMap = testMap.copyAndRemoveAll(Collections.singleton("not found"));
+ assertEquals(0, testMap.copyAndRemoveAll(testMap.keySet()).size());
+ testMap = testMap.copyAndRemoveAll(Collections.singleton("test"));
+ assertEquals(0, testMap.copyAndRemoveAll(testMap.keySet()).size());
+ }
+
+ @Test
+ void testEmptyMap() {
+ assertNull(UnmodifiableArrayBackedMap.EMPTY_MAP.get("test"));
+ }
+
+ @Test
+ void testEntrySetIteratorAndSize() {
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
+ Set> entrySet = testMap.entrySet();
+ int numEntriesFound = 0;
+ for (@SuppressWarnings("unused") Map.Entry entry : entrySet) {
+ numEntriesFound++;
+ }
+
+ assertEquals(testMap.size(), numEntriesFound);
+ assertEquals(testMap.size(), entrySet.size());
+ }
+
+ @Test
+ void testEntrySetMutatorsBlocked() {
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
+ Set> entrySet = testMap.entrySet();
+ for (Map.Entry entry : entrySet) {
+ try {
+ entry.setValue("test");
+ fail("Entry.setValue() wasn't blocked");
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+ for (@SuppressWarnings("unused") Map.Entry entry : entrySet) {
+ try {
+ entrySet.add(null);
+ fail("EntrySet.add() wasn't blocked");
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+ for (@SuppressWarnings("unused") Map.Entry entry : entrySet) {
+ try {
+ entrySet.addAll(new HashSet<>());
+ fail("EntrySet.addAll() wasn't blocked");
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+ }
+
+ /**
+ * Tests various situations with .equals(). Test tries comparisons in both
+ * directions, to make sure that HashMap.equals(UnmodifiableArrayBackedMap) work
+ * as well as UnmodifiableArrayBackedMap.equals(HashMap).
+ */
+ @Test
+ void testEqualsHashCodeWithIdenticalContent() {
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+ assertEquals(params, testMap);
+ assertEquals(testMap, params);
+ assertEquals(params.hashCode(), testMap.hashCode());
+ }
+
+ @Test
+ void testEqualsHashCodeWithOneEmptyMap() {
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+ // verify empty maps are not equal to non-empty maps
+ assertNotEquals(UnmodifiableArrayBackedMap.EMPTY_MAP, params);
+ assertNotEquals(new HashMap<>(), testMap);
+ assertNotEquals(UnmodifiableArrayBackedMap.EMPTY_MAP, params);
+ assertNotEquals(new HashMap<>(), testMap);
+ }
+
+ @Test
+ void testEqualsHashCodeWithOneKeyRemoved() {
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+
+ params.remove("1");
+ assertNotEquals(params, testMap);
+ assertNotEquals(testMap, params);
+
+ testMap = testMap.copyAndRemove("1").copyAndRemove("2");
+ assertNotEquals(params, testMap);
+ assertNotEquals(testMap, params);
+ }
+
+ @Test
+ void testEqualsWhenOneValueDiffers() {
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+ assertNotEquals(params, testMap.copyAndPut("1", "different value"));
+ assertNotEquals(testMap.copyAndPut("1", "different value"), params);
+ }
+
+ @Test
+ void testForEachBiConsumer_JavaUtil() {
+ final Map map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
+ final Collection keys = new HashSet<>();
+ map.forEach((key, value) -> keys.add(key));
+ assertEquals(map.keySet(), keys);
+ }
+
+ @Test
+ void testForEachBiConsumer_Log4jUtil() {
+ final ReadOnlyStringMap map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
+ final Collection keys = new HashSet<>();
+ map.forEach((key, value) -> keys.add(key));
+ assertEquals(map.toMap().keySet(), keys);
+ }
+
+ @Test
+ void testForEachTriConsumer() {
+ UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
+ HashMap iterationResultMap = new HashMap<>();
+ TriConsumer> triConsumer =
+ new TriConsumer>() {
+ @Override
+ public void accept(String k, String v, Map s) {
+ s.put(k, v);
+ }
+ };
+ map.forEach(triConsumer, iterationResultMap);
+ assertEquals(map, iterationResultMap);
+ }
+
+ @Test
+ void testImmutability() {
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap originalMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+ UnmodifiableArrayBackedMap modifiedMap = originalMap.copyAndPutAll(getTestParameters());
+ assertEquals(originalMap, params);
+
+ modifiedMap = modifiedMap.copyAndRemoveAll(modifiedMap.keySet());
+ assertTrue(modifiedMap.isEmpty());
+
+ assertEquals(originalMap, params);
+ }
+
+ @Test
+ void testInstanceCopy() {
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+
+ UnmodifiableArrayBackedMap testMap2 = new UnmodifiableArrayBackedMap(testMap);
+ assertEquals(testMap, testMap2);
+ }
+
+ @Test
+ void testMutatorsBlocked() {
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
+ try {
+ testMap.put("a", "a");
+ fail("put() wasn't blocked");
+ } catch (UnsupportedOperationException e) {
+ }
+
+ try {
+ testMap.putAll(new HashMap<>());
+ fail("putAll() wasn't blocked");
+ } catch (UnsupportedOperationException e) {
+ }
+
+ try {
+ testMap.remove("1");
+ fail("remove() wasn't blocked");
+ } catch (UnsupportedOperationException e) {
+ }
+
+ try {
+ testMap.clear();
+ fail("clear() wasn't blocked");
+ } catch (UnsupportedOperationException e) {
+ }
+ }
+
+ @Test
+ void testNullValue() {
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
+ testMap = testMap.copyAndPut("key", null);
+ assertTrue(testMap.containsKey("key"));
+ assertTrue(testMap.containsValue(null));
+ assertEquals(1, testMap.size());
+ assertNull(testMap.get("key"));
+ }
+
+ @Test
+ void testReads() {
+ assertNull(UnmodifiableArrayBackedMap.EMPTY_MAP.get("test"));
+ HashMap params = getTestParameters();
+ UnmodifiableArrayBackedMap testMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(params);
+ for (Map.Entry entry : params.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue();
+ assertTrue(testMap.containsKey(key));
+ assertTrue(testMap.containsValue(value));
+ assertEquals(testMap.get(key), params.get(key));
+ }
+ assertFalse(testMap.containsKey("not_present"));
+ assertFalse(testMap.containsValue("not_present"));
+ assertNull(testMap.get("not_present"));
+ }
+
+ @Test
+ void testState() {
+ UnmodifiableArrayBackedMap originalMap;
+ UnmodifiableArrayBackedMap newMap;
+
+ originalMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
+ newMap = UnmodifiableArrayBackedMap.getMap(originalMap.getBackingArray());
+ assertEquals(originalMap, newMap);
+
+ originalMap = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPutAll(getTestParameters());
+ newMap = UnmodifiableArrayBackedMap.getMap(originalMap.getBackingArray());
+ assertEquals(originalMap, newMap);
+
+ originalMap = UnmodifiableArrayBackedMap.EMPTY_MAP
+ .copyAndPutAll(getTestParameters())
+ .copyAndRemove("1");
+ newMap = UnmodifiableArrayBackedMap.getMap(originalMap.getBackingArray());
+ assertEquals(originalMap, newMap);
+ }
+
+ @Test
+ void testToMap() {
+ UnmodifiableArrayBackedMap map = UnmodifiableArrayBackedMap.EMPTY_MAP.copyAndPut("test", "test");
+ // verify same instance, not just equals()
+ assertSame(map, map.toMap());
+ }
+
+ @Test
+ void copyAndRemoveAll_should_work() {
+
+ // Create the actual map
+ UnmodifiableArrayBackedMap actualMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
+ actualMap = actualMap.copyAndPut("outer", "two");
+ actualMap = actualMap.copyAndPut("inner", "one");
+ actualMap = actualMap.copyAndPut("not-in-closeable", "true");
+
+ // Create the expected map
+ UnmodifiableArrayBackedMap expectedMap = UnmodifiableArrayBackedMap.EMPTY_MAP;
+ expectedMap = expectedMap.copyAndPut("outer", "two");
+ expectedMap = expectedMap.copyAndPut("not-in-closeable", "true");
+
+ // Remove the key and verify
+ actualMap = actualMap.copyAndRemoveAll(Collections.singleton("inner"));
+ Assertions.assertThat(actualMap).isEqualTo(expectedMap);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
new file mode 100644
index 00000000000..f2a35b2474a
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/FormattedMessageTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.message;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Locale;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.apache.logging.log4j.util.Constants;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+class FormattedMessageTest {
+
+ private static final String SPACE = Constants.JAVA_MAJOR_VERSION < 9 ? " " : "\u00a0";
+
+ private static final int LOOP_CNT = 500;
+ String[] array = new String[LOOP_CNT];
+
+ @Test
+ void testStringNoArgs() {
+ final String testMsg = "Test message %1s";
+ FormattedMessage msg = new FormattedMessage(testMsg, (Object[]) null);
+ String result = msg.getFormattedMessage();
+ final String expected = "Test message null";
+ assertEquals(expected, result);
+ final Object[] array = null;
+ msg = new FormattedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void tesStringOneStringArg() {
+ final String testMsg = "Test message %1s";
+ final FormattedMessage msg = new FormattedMessage(testMsg, "Apache");
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message Apache";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void tesStringOneArgLocaleFrance_StringFormattedMessage() {
+ final String testMsg = "Test message e = %+10.4f";
+ final FormattedMessage msg = new FormattedMessage(Locale.FRANCE, testMsg, Math.E);
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message e = +2,7183";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void tesStringOneArgLocaleFrance_MessageFormatMessage() {
+ final String testMsg = "Test message {0,number,currency}";
+ final FormattedMessage msg = new FormattedMessage(Locale.FRANCE, testMsg, 12);
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message 12,00" + SPACE + "€";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void tesStringOneArgLocaleUs_MessageFormatMessage() {
+ final String testMsg = "Test message {0,number,currency}";
+ final FormattedMessage msg = new FormattedMessage(Locale.US, testMsg, 12);
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message $12.00";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void tesStringOneArgLocaleUs() {
+ final String testMsg = "Test message e = %+10.4f";
+ final FormattedMessage msg = new FormattedMessage(Locale.US, testMsg, Math.E);
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message e = +2.7183";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testNoArgs() {
+ final String testMsg = "Test message {0}";
+ FormattedMessage msg = new FormattedMessage(testMsg, (Object[]) null);
+ String result = msg.getFormattedMessage();
+ final String expected = "Test message {0}";
+ assertEquals(expected, result);
+ final Object[] array = null;
+ msg = new FormattedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testOneArg() {
+ final String testMsg = "Test message {0}";
+ final FormattedMessage msg = new FormattedMessage(testMsg, "Apache");
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message Apache";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testParamNoArgs() {
+ final String testMsg = "Test message {}";
+ FormattedMessage msg = new FormattedMessage(testMsg, (Object[]) null);
+ String result = msg.getFormattedMessage();
+ assertEquals(testMsg, result);
+ final Object[] array = null;
+ msg = new FormattedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertEquals(testMsg, result);
+ }
+
+ @Test
+ void testUnsafeWithMutableParams() { // LOG4J2-763
+ final String testMsg = "Test message %s";
+ final Mutable param = new Mutable().set("abc");
+ final FormattedMessage msg = new FormattedMessage(testMsg, param);
+
+ // modify parameter before calling msg.getFormattedMessage
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message XYZ", actual, "Expected most recent param value");
+ }
+
+ @Test
+ void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
+ final String testMsg = "Test message %s";
+ final Mutable param = new Mutable().set("abc");
+ final FormattedMessage msg = new FormattedMessage(testMsg, param);
+
+ // modify parameter after calling msg.getFormattedMessage
+ msg.getFormattedMessage(); // freeze the formatted message
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message abc", actual, "Should use initial param value");
+ }
+
+ @Test
+ void testSerialization() throws IOException, ClassNotFoundException {
+ final FormattedMessage expected = new FormattedMessage("Msg", "a", "b", "c");
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (final ObjectOutputStream out = new ObjectOutputStream(baos)) {
+ out.writeObject(expected);
+ }
+ final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ final ObjectInputStream in = new ObjectInputStream(bais);
+ final FormattedMessage actual = (FormattedMessage) in.readObject();
+ assertEquals(expected, actual);
+ assertEquals(expected.getFormat(), actual.getFormat());
+ assertEquals(expected.getFormattedMessage(), actual.getFormattedMessage());
+ assertArrayEquals(expected.getParameters(), actual.getParameters());
+ }
+}
diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
similarity index 85%
rename from log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
rename to log4j-api-test/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
index 5107841f380..b415a17f615 100644
--- a/log4j-api/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/JsonMessage.java
@@ -1,25 +1,24 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
+ * 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 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
+ * 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package org.apache.logging.log4j.message;
-import org.apache.logging.log4j.status.StatusLogger;
-
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.logging.log4j.status.StatusLogger;
/**
* Converts an Object to a JSON String.
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageFactoryTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageFactoryTest.java
new file mode 100644
index 00000000000..06d3fb8088e
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageFactoryTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.message;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link LocalizedMessageFactory}.
+ */
+class LocalizedMessageFactoryTest {
+
+ @Test
+ void testMessageMarkersDataNo() {
+ final LocalizedMessageFactory localizedMessageFactory =
+ new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("msg1");
+ assertEquals("This is test number {0} with string argument {1}.", message.getFormattedMessage());
+ }
+
+ @Test
+ void testMessageMarkersNoDataYes() {
+ final LocalizedMessageFactory localizedMessageFactory =
+ new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("msg1", 1, "two");
+ assertEquals("This is test number 1 with string argument two.", message.getFormattedMessage());
+ }
+
+ @Test
+ void testNewMessage() {
+ final LocalizedMessageFactory localizedMessageFactory =
+ new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("hello_world");
+ assertEquals("Hello world.", message.getFormattedMessage());
+ }
+
+ @Test
+ void testNoMatch() {
+ final LocalizedMessageFactory localizedMessageFactory =
+ new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage("no match");
+ assertEquals("no match", message.getFormattedMessage());
+ }
+
+ @Test
+ void testNoMatchPercentInMessageNoArgsNo() {
+ // LOG4J2-3458 LocalizedMessage causes a lot of noise on the console
+ //
+ // ERROR StatusLogger Unable to format msg: C:/Program%20Files/Some%20Company/Some%20Product%20Name/
+ // java.util.UnknownFormatConversionException: Conversion = 'F'
+ // at java.util.Formatter$FormatSpecifier.conversion(Formatter.java:2691)
+ // at java.util.Formatter$FormatSpecifier.(Formatter.java:2720)
+ // at java.util.Formatter.parse(Formatter.java:2560)
+ // at java.util.Formatter.format(Formatter.java:2501)
+ // at java.util.Formatter.format(Formatter.java:2455)
+ // at java.lang.String.format(String.java:2981)
+ // at org.apache.logging.log4j.message.StringFormattedMessage.formatMessage(StringFormattedMessage.java:116)
+ // at
+ // org.apache.logging.log4j.message.StringFormattedMessage.getFormattedMessage(StringFormattedMessage.java:88)
+ // at org.apache.logging.log4j.message.FormattedMessage.getFormattedMessage(FormattedMessage.java:178)
+ // at org.apache.logging.log4j.message.LocalizedMessage.getFormattedMessage(LocalizedMessage.java:196)
+ // at
+ // org.apache.logging.log4j.message.LocalizedMessageFactoryTest.testNoMatchPercentInMessage(LocalizedMessageFactoryTest.java:60)
+ //
+ final LocalizedMessageFactory localizedMessageFactory =
+ new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message =
+ localizedMessageFactory.newMessage("C:/Program%20Files/Some%20Company/Some%20Product%20Name/");
+ assertEquals("C:/Program%20Files/Some%20Company/Some%20Product%20Name/", message.getFormattedMessage());
+ }
+
+ @Test
+ void testNoMatchPercentInMessageArgsYes() {
+ final LocalizedMessageFactory localizedMessageFactory =
+ new LocalizedMessageFactory(ResourceBundle.getBundle("MF", Locale.US));
+ final Message message = localizedMessageFactory.newMessage(
+ "C:/Program%20Files/Some%20Company/Some%20Product%20Name/{0}", "One");
+ assertEquals("C:/Program%20Files/Some%20Company/Some%20Product%20Name/One", message.getFormattedMessage());
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
new file mode 100644
index 00000000000..832230d53c8
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/LocalizedMessageTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.message;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.Serializable;
+import java.util.Locale;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+/**
+ * Tests LocalizedMessage.
+ */
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+class LocalizedMessageTest {
+
+ private T roundtrip(final T msg) {
+ return SerializationUtils.roundtrip(msg);
+ }
+
+ @Test
+ void testMessageFormat() {
+ final LocalizedMessage msg =
+ new LocalizedMessage("MF", new Locale("en", "US"), "msg1", new Object[] {"1", "Test"});
+ assertEquals("This is test number 1 with string argument Test.", msg.getFormattedMessage());
+ }
+
+ @Test
+ void testSerializationMessageFormat() {
+ final LocalizedMessage msg =
+ new LocalizedMessage("MF", new Locale("en", "US"), "msg1", new Object[] {"1", "Test"});
+ assertEquals("This is test number 1 with string argument Test.", msg.getFormattedMessage());
+ final LocalizedMessage msg2 = roundtrip(msg);
+ assertEquals("This is test number 1 with string argument Test.", msg2.getFormattedMessage());
+ }
+
+ @Test
+ void testSerializationStringFormat() {
+ final LocalizedMessage msg =
+ new LocalizedMessage("SF", new Locale("en", "US"), "msg1", new Object[] {"1", "Test"});
+ assertEquals("This is test number 1 with string argument Test.", msg.getFormattedMessage());
+ final LocalizedMessage msg2 = roundtrip(msg);
+ assertEquals("This is test number 1 with string argument Test.", msg2.getFormattedMessage());
+ }
+
+ @Test
+ void testStringFormat() {
+ final LocalizedMessage msg =
+ new LocalizedMessage("SF", new Locale("en", "US"), "msg1", new Object[] {"1", "Test"});
+ assertEquals("This is test number 1 with string argument Test.", msg.getFormattedMessage());
+ }
+
+ @Test
+ void testUnsafeWithMutableParams() { // LOG4J2-763
+ final String testMsg = "Test message %s";
+ final Mutable param = new Mutable().set("abc");
+ final LocalizedMessage msg = new LocalizedMessage(testMsg, param);
+
+ // modify parameter before calling msg.getFormattedMessage
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message XYZ", actual, "Expected most recent param value");
+ }
+
+ @Test
+ void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
+ final String testMsg = "Test message %s";
+ final Mutable param = new Mutable().set("abc");
+ final LocalizedMessage msg = new LocalizedMessage(testMsg, param);
+
+ // modify parameter after calling msg.getFormattedMessage
+ msg.getFormattedMessage();
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message abc", actual, "Should use initial param value");
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
new file mode 100644
index 00000000000..a36bb6b4ae9
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MapMessageTest.java
@@ -0,0 +1,330 @@
+/*
+ * 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.message;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.util.StringBuilderFormattable;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ */
+class MapMessageTest {
+
+ @Test
+ void testMap() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage();
+ final String expected = "message=\"Test message {}\" project=\"Log4j\"";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testBuilder() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg =
+ new StringMapMessage().with("message", testMsg).with("project", "Log4j");
+ final String result = msg.getFormattedMessage();
+ final String expected = "message=\"Test message {}\" project=\"Log4j\"";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testXML() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage(new String[] {"XML"});
+ final String expected = "\n Test message {} \n"
+ + " Log4j \n" + " ";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testXMLEscape() {
+ final String testMsg = "Test message ";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ final String result = msg.getFormattedMessage(new String[] {"XML"});
+ final String expected = "\n Test message <foo> \n" + " ";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testJSON() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage(new String[] {"JSON"});
+ final String expected = "{'message':'Test message {}','project':'Log4j'}".replace('\'', '"');
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testJSONEscape() {
+ final String testMsg = "Test message \"Hello, World!\"";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ final String result = msg.getFormattedMessage(new String[] {"JSON"});
+ final String expected = "{\"message\":\"Test message \\\"Hello, World!\\\"\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testJSONEscapeNewlineAndOtherControlCharacters() {
+ final String testMsg = "hello\tworld\r\nhh\bere is it\f";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("one\ntwo", testMsg);
+ final String result = msg.getFormattedMessage(new String[] {"JSON"});
+ final String expected = "{\"one\\ntwo\":\"hello\\tworld\\r\\nhh\\bere is it\\f\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testJsonFormatterNestedObjectSupport() {
+ final Map map = new LinkedHashMap<>();
+ map.put("chars", new char[] {'a', 'b', 'c'});
+ map.put("booleans", new boolean[] {true, false});
+ map.put("bytes", new byte[] {1, 2});
+ map.put("shorts", new short[] {3, 4});
+ map.put("ints", new int[] {256, 257});
+ map.put("longs", new long[] {2147483648L, 2147483649L});
+ map.put("floats", new float[] {1.0F, 1.1F});
+ map.put("doubles", new double[] {2.0D, 2.1D});
+ map.put("objects", new Object[] {"foo", "bar"});
+ final String actualJson = new ObjectMapMessage()
+ .with("key1", "val1")
+ .with("key2", Collections.singletonMap("key2.1", "val2.1"))
+ .with(
+ "key3",
+ Arrays.asList(
+ 3,
+ (byte) 127,
+ 4.5D,
+ 4.6F,
+ Arrays.asList(true, false),
+ new BigDecimal(30),
+ Collections.singletonMap("key3.3", "val3.3")))
+ .with("key4", map)
+ .getFormattedMessage(new String[] {"JSON"});
+ final String expectedJson = ("{" + "'key1':'val1',"
+ + "'key2':{'key2.1':'val2.1'},"
+ + "'key3':[3,127,4.5,4.6,[true,false],30,{'key3.3':'val3.3'}],"
+ + "'key4':{"
+ + "'chars':['a','b','c'],"
+ + "'booleans':[true,false],"
+ + "'bytes':[1,2],"
+ + "'shorts':[3,4],"
+ + "'ints':[256,257],"
+ + "'longs':[2147483648,2147483649],"
+ + "'floats':[1.0,1.1],"
+ + "'doubles':[2.0,2.1],"
+ + "'objects':['foo','bar']"
+ + "}}")
+ .replace('\'', '"');
+ assertEquals(expectedJson, actualJson);
+ }
+
+ @Test
+ void testJsonFormatterInfiniteRecursionPrevention() {
+ final List recursiveValue = Arrays.asList(1, null);
+ // noinspection CollectionAddedToSelf
+ recursiveValue.set(1, recursiveValue);
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new ObjectMapMessage().with("key", recursiveValue).getFormattedMessage(new String[] {"JSON"}));
+ }
+
+ @Test
+ void testJsonFormatterMaxDepthViolation() {
+ assertThrows(
+ IllegalArgumentException.class, () -> testJsonFormatterMaxDepth(MapMessageJsonFormatter.MAX_DEPTH - 1));
+ }
+
+ @Test
+ void testJsonFormatterMaxDepthConformance() {
+ final int depth = MapMessageJsonFormatter.MAX_DEPTH - 2;
+ final String expectedJson = String.format(
+ "{'key':%s1%s}", StringUtils.repeat("[", depth), StringUtils.repeat("]", depth))
+ .replace('\'', '"');
+ final String actualJson = testJsonFormatterMaxDepth(depth);
+ assertEquals(expectedJson, actualJson);
+ }
+
+ public static String testJsonFormatterMaxDepth(final int depth) {
+ List list = new LinkedList<>();
+ list.add(1);
+ int currentDepth = depth;
+ while (--currentDepth > 0) {
+ list = new LinkedList<>(Collections.singletonList(list));
+ }
+ return new ObjectMapMessage().with("key", list).getFormattedMessage(new String[] {"JSON"});
+ }
+
+ @Test
+ void testJava() {
+ final String testMsg = "Test message {}";
+ final StringMapMessage msg = new StringMapMessage();
+ msg.put("message", testMsg);
+ msg.put("project", "Log4j");
+ final String result = msg.getFormattedMessage(new String[] {"Java"});
+ final String expected = "{message=\"Test message {}\", project=\"Log4j\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testMutableByDesign() { // LOG4J2-763
+ final StringMapMessage msg = new StringMapMessage();
+
+ // modify parameter before calling msg.getFormattedMessage
+ msg.put("key1", "value1");
+ msg.put("key2", "value2");
+ final String result = msg.getFormattedMessage(new String[] {"Java"});
+ final String expected = "{key1=\"value1\", key2=\"value2\"}";
+ assertEquals(expected, result);
+
+ // modify parameter after calling msg.getFormattedMessage
+ msg.put("key3", "value3");
+ final String result2 = msg.getFormattedMessage(new String[] {"Java"});
+ final String expected2 = "{key1=\"value1\", key2=\"value2\", key3=\"value3\"}";
+ assertEquals(expected2, result2);
+ }
+
+ @Test
+ void testGetNonStringValue() {
+ final String key = "Key";
+ final ObjectMapMessage msg = new ObjectMapMessage().with(key, 1L);
+ assertEquals("1", msg.get(key));
+ }
+
+ @Test
+ void testRemoveNonStringValue() {
+ final String key = "Key";
+ final ObjectMapMessage msg = new ObjectMapMessage().with(key, 1L);
+ assertEquals("1", msg.remove(key));
+ }
+
+ @Test
+ void testJSONFormatNonStringValue() {
+ final ObjectMapMessage msg = new ObjectMapMessage().with("key", 1L);
+ final String result = msg.getFormattedMessage(new String[] {"JSON"});
+ final String expected = "{'key':1}".replace('\'', '"');
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testXMLFormatNonStringValue() {
+ final ObjectMapMessage msg = new ObjectMapMessage().with("key", 1L);
+ final String result = msg.getFormattedMessage(new String[] {"XML"});
+ final String expected = "\n 1 \n ";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testFormatToUsedInOutputXml() {
+ final ObjectMapMessage msg = new ObjectMapMessage().with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(new String[] {"XML"});
+ final String expected = "\n formatTo \n ";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testFormatToUsedInOutputJson() {
+ final ObjectMapMessage msg = new ObjectMapMessage().with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(new String[] {"JSON"});
+ final String expected = "{\"key\":\"formatTo\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testFormatToUsedInOutputJava() {
+ final ObjectMapMessage msg = new ObjectMapMessage().with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(new String[] {"JAVA"});
+ final String expected = "{key=\"formatTo\"}";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testFormatToUsedInOutputDefault() {
+ final ObjectMapMessage msg = new ObjectMapMessage().with("key", new FormattableTestType());
+ final String result = msg.getFormattedMessage(null);
+ final String expected = "key=\"formatTo\"";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testGetUsesDeepToString() {
+ final String key = "key";
+ final ObjectMapMessage msg = new ObjectMapMessage().with(key, new FormattableTestType());
+ final String result = msg.get(key);
+ final String expected = "formatTo";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testRemoveUsesDeepToString() {
+ final String key = "key";
+ final ObjectMapMessage msg = new ObjectMapMessage().with(key, new FormattableTestType());
+ final String result = msg.remove(key);
+ final String expected = "formatTo";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testTime() {
+ final Time time = new Time(12, 5, 5);
+ final ObjectMapMessage message = new ObjectMapMessage().with("time", time);
+ assertEquals("time=\"" + time + "\"", message.getFormattedMessage(), "Incorrect time format");
+ }
+
+ @Test
+ void testDate() {
+ final Date date = new Date(System.currentTimeMillis());
+ final ObjectMapMessage message = new ObjectMapMessage().with("date", date);
+ assertEquals("date=\"" + date + "\"", message.getFormattedMessage(), "Incorrect date format");
+ }
+
+ private static final class FormattableTestType implements StringBuilderFormattable {
+
+ @Override
+ public String toString() {
+ return "toString";
+ }
+
+ @Override
+ public void formatTo(final StringBuilder buffer) {
+ buffer.append("formatTo");
+ }
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageSerializationTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageSerializationTest.java
new file mode 100644
index 00000000000..86823f14788
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageSerializationTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.message;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.apache.logging.log4j.test.AbstractSerializationTest;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+import org.junit.runners.Parameterized;
+
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+public class MessageFormatMessageSerializationTest extends AbstractSerializationTest {
+
+ @Parameterized.Parameters
+ public static Collection data() {
+ return Arrays.asList(new Object[][] {
+ {new MessageFormatMessage("Test")},
+ {new MessageFormatMessage("Test {0} {1}", "message", "test")},
+ {new MessageFormatMessage("{0}{1}{2}", 3, '.', 14L)}
+ });
+ }
+
+ public MessageFormatMessageSerializationTest(final MessageFormatMessage message) {
+ super(message);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
new file mode 100644
index 00000000000..44fc67cc383
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatMessageTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.message;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.util.Locale;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.apache.logging.log4j.util.Constants;
+import org.assertj.core.presentation.UnicodeRepresentation;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+class MessageFormatMessageTest {
+
+ private static final char SPACE = ' ';
+ private static final char NB_SPACE = '\u00a0';
+ private static final char NARROW_NB_SPACE = '\u202f';
+
+ private static final int LOOP_CNT = 500;
+ String[] array = new String[LOOP_CNT];
+
+ @Test
+ void testNoArgs() {
+ final String testMsg = "Test message {0}";
+ MessageFormatMessage msg = new MessageFormatMessage(testMsg, (Object[]) null);
+ String result = msg.getFormattedMessage();
+ String expected = "Test message {0}";
+ assertEquals(expected, result);
+ final Object[] array = null;
+ msg = new MessageFormatMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ expected = "Test message null";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testOneStringArg() {
+ final String testMsg = "Test message {0}";
+ final MessageFormatMessage msg = new MessageFormatMessage(testMsg, "Apache");
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message Apache";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testOneIntArgLocaleUs() {
+ final String testMsg = "Test message {0,number,currency}";
+ final MessageFormatMessage msg = new MessageFormatMessage(Locale.US, testMsg, 1234567890);
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message $1,234,567,890.00";
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void testOneIntArgLocaleFrance() {
+ final String testMsg = "Test message {0,number,currency}";
+ final MessageFormatMessage msg = new MessageFormatMessage(Locale.FRANCE, testMsg, 1234567890);
+ final String result = msg.getFormattedMessage();
+ final char separator = Constants.JAVA_MAJOR_VERSION < 9 ? SPACE : NB_SPACE;
+ final char groupingSeparator = Constants.JAVA_MAJOR_VERSION < 17 ? NB_SPACE : NARROW_NB_SPACE;
+ assertThat(result)
+ .withRepresentation(UnicodeRepresentation.UNICODE_REPRESENTATION)
+ .isEqualTo("Test message 1%1$c234%1$c567%1$c890,00%2$c€", groupingSeparator, separator);
+ }
+
+ @Test
+ void testException() {
+ final String testMsg = "Test message {0}";
+ final MessageFormatMessage msg = new MessageFormatMessage(testMsg, "Apache", new NullPointerException("Null"));
+ final String result = msg.getFormattedMessage();
+ final String expected = "Test message Apache";
+ assertEquals(expected, result);
+ final Throwable t = msg.getThrowable();
+ assertNotNull(t, "No Throwable");
+ }
+
+ @Test
+ void testUnsafeWithMutableParams() { // LOG4J2-763
+ final String testMsg = "Test message {0}";
+ final Mutable param = new Mutable().set("abc");
+ final MessageFormatMessage msg = new MessageFormatMessage(testMsg, param);
+
+ // modify parameter before calling msg.getFormattedMessage
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message XYZ", actual, "Expected most recent param value");
+ }
+
+ @Test
+ void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
+ final String testMsg = "Test message {0}";
+ final Mutable param = new Mutable().set("abc");
+ final MessageFormatMessage msg = new MessageFormatMessage(testMsg, param);
+
+ // modify parameter after calling msg.getFormattedMessage
+ msg.getFormattedMessage();
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("Test message abc", actual, "Should use initial param value");
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java
new file mode 100644
index 00000000000..90fe41b7aed
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/MessageFormatsPerfTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.message;
+
+import org.apache.logging.log4j.util.Timer;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.ResourceAccessMode;
+import org.junit.jupiter.api.parallel.ResourceLock;
+import org.junit.jupiter.api.parallel.Resources;
+
+/**
+ *
+ */
+@Tag("performance")
+@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)
+class MessageFormatsPerfTest {
+
+ private static final int LOOP_CNT = 500;
+ String[] array = new String[LOOP_CNT];
+ private static long stringTime = 0;
+ private static long paramTime = 0;
+ private static long msgFormatTime = 0;
+ private static long formattedTime = 0;
+
+ @AfterAll
+ static void after() {
+ if (stringTime > paramTime) {
+ System.out.println(String.format(
+ "Parameterized is %1$.2f times faster than StringFormat.", ((float) stringTime / paramTime)));
+ } else if (stringTime < paramTime) {
+ System.out.println(String.format(
+ "Parameterized is %1$.2f times slower than StringFormat.", ((float) paramTime / stringTime)));
+ } else {
+ System.out.println("The speed of Parameterized and StringFormat are the same");
+ }
+ if (msgFormatTime > paramTime) {
+ System.out.println(String.format(
+ "Parameterized is %1$.2f times faster than MessageFormat.", ((float) msgFormatTime / paramTime)));
+ } else if (msgFormatTime < paramTime) {
+ System.out.println(String.format(
+ "Parameterized is %1$.2f times slower than MessageFormat.", ((float) paramTime / msgFormatTime)));
+ } else {
+ System.out.println("The speed of Parameterized and MessageFormat are the same");
+ }
+ if (formattedTime > paramTime) {
+ System.out.println(String.format(
+ "Parameterized is %1$.2f times faster than Formatted.", ((float) formattedTime / paramTime)));
+ } else if (formattedTime < paramTime) {
+ System.out.println(String.format(
+ "Parameterized is %1$.2f times slower than Formatted.", ((float) paramTime / formattedTime)));
+ } else {
+ System.out.println("The speed of Parameterized and Formatted are the same");
+ }
+ }
+
+ @Test
+ void testStringPerf() {
+ final String testMsg = "Test message %1s %2s";
+ final Timer timer = new Timer("StringFormat", LOOP_CNT);
+ timer.start();
+ for (int i = 0; i < LOOP_CNT; ++i) {
+ final StringFormattedMessage msg = new StringFormattedMessage(testMsg, "Apache", "Log4j");
+ array[i] = msg.getFormattedMessage();
+ }
+ timer.stop();
+ stringTime = timer.getElapsedNanoTime();
+ System.out.println(timer);
+ }
+
+ @Test
+ void testMessageFormatPerf() {
+ final String testMsg = "Test message {0} {1}";
+ final Timer timer = new Timer("MessageFormat", LOOP_CNT);
+ timer.start();
+ for (int i = 0; i < LOOP_CNT; ++i) {
+ final MessageFormatMessage msg = new MessageFormatMessage(testMsg, "Apache", "Log4j");
+ array[i] = msg.getFormattedMessage();
+ }
+ timer.stop();
+ msgFormatTime = timer.getElapsedNanoTime();
+ System.out.println(timer);
+ }
+
+ @Test
+ void testParameterizedPerf() {
+ final String testMsg = "Test message {} {}";
+ final Timer timer = new Timer("Parameterized", LOOP_CNT);
+ timer.start();
+ for (int i = 0; i < LOOP_CNT; ++i) {
+ final ParameterizedMessage msg = new ParameterizedMessage(testMsg, "Apache", "Log4j");
+ array[i] = msg.getFormattedMessage();
+ }
+ timer.stop();
+ paramTime = timer.getElapsedNanoTime();
+ System.out.println(timer);
+ }
+
+ @Test
+ void testFormattedParameterizedPerf() {
+ final String testMsg = "Test message {} {}";
+ final Timer timer = new Timer("FormattedParameterized", LOOP_CNT);
+ timer.start();
+ for (int i = 0; i < LOOP_CNT; ++i) {
+ final FormattedMessage msg = new FormattedMessage(testMsg, "Apache", "Log4j");
+ array[i] = msg.getFormattedMessage();
+ }
+ timer.stop();
+ formattedTime = timer.getElapsedNanoTime();
+ System.out.println(timer);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
new file mode 100644
index 00000000000..cdfb2c9bd26
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectArrayMessageTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.message;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * @since 2.4
+ */
+class ObjectArrayMessageTest {
+
+ private static final Object[] ARRAY = {"A", "B", "C"};
+ private static final ObjectArrayMessage OBJECT_ARRAY_MESSAGE = new ObjectArrayMessage(ARRAY);
+
+ @Test
+ void testGetParameters() {
+ assertArrayEquals(ARRAY, OBJECT_ARRAY_MESSAGE.getParameters());
+ }
+
+ @Test
+ void testGetThrowable() {
+ assertNull(OBJECT_ARRAY_MESSAGE.getThrowable());
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMapMessage.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMapMessage.java
new file mode 100644
index 00000000000..70c861208a1
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMapMessage.java
@@ -0,0 +1,22 @@
+/*
+ * 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.message;
+
+class ObjectMapMessage extends MapMessage {
+
+ private static final long serialVersionUID = 1L;
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
new file mode 100644
index 00000000000..2bdc1d7a68a
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ObjectMessageTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.message;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.math.BigDecimal;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.apache.logging.log4j.test.junit.SerialUtil;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link ObjectMessage}.
+ */
+class ObjectMessageTest {
+
+ @Test
+ void testNull() {
+ final ObjectMessage msg = new ObjectMessage(null);
+ final String result = msg.getFormattedMessage();
+ assertEquals("null", result);
+ }
+
+ @Test
+ void testNotNull() {
+ final String testMsg = "Test message {}";
+ final ObjectMessage msg = new ObjectMessage(testMsg);
+ final String result = msg.getFormattedMessage();
+ assertEquals(testMsg, result);
+ }
+
+ @Test
+ void testUnsafeWithMutableParams() { // LOG4J2-763
+ final Mutable param = new Mutable().set("abc");
+ final ObjectMessage msg = new ObjectMessage(param);
+
+ // modify parameter before calling msg.getFormattedMessage
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("XYZ", actual, "Expected most recent param value");
+ }
+
+ @Test
+ void testSafeAfterGetFormattedMessageIsCalled() { // LOG4J2-763
+ final Mutable param = new Mutable().set("abc");
+ final ObjectMessage msg = new ObjectMessage(param);
+
+ // modify parameter after calling msg.getFormattedMessage
+ msg.getFormattedMessage();
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertEquals("abc", actual, "Should use initial param value");
+ }
+
+ @Test
+ void formatTo_usesCachedMessageString() {
+ final StringBuilder charSequence = new StringBuilder("initial value");
+ final ObjectMessage message = new ObjectMessage(charSequence);
+ assertEquals("initial value", message.getFormattedMessage());
+
+ charSequence.setLength(0);
+ charSequence.append("different value");
+
+ final StringBuilder result = new StringBuilder();
+ message.formatTo(result);
+ assertEquals("initial value", result.toString());
+ }
+
+ static Stream testSerializable() {
+ @SuppressWarnings("EqualsHashCode")
+ class NonSerializable {
+ @Override
+ public boolean equals(final Object other) {
+ return other instanceof NonSerializable; // a very lenient equals()
+ }
+ }
+ return Stream.of(
+ "World",
+ new NonSerializable(),
+ new BigDecimal("123.456"),
+ // LOG4J2-3680
+ new RuntimeException(),
+ null);
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void testSerializable(final Object arg) {
+ final Message expected = new ObjectMessage(arg);
+ final Message actual = SerialUtil.deserialize(SerialUtil.serialize(expected));
+ assertThat(actual).isInstanceOf(ObjectMessage.class);
+ assertThat(actual).isEqualTo(expected);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java
new file mode 100644
index 00000000000..e4988f41417
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterFormatterTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.message;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.message.ParameterFormatter.MessagePatternAnalysis;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Tests {@link ParameterFormatter}.
+ */
+@UsingStatusListener
+class ParameterFormatterTest {
+
+ final ListStatusListener statusListener;
+
+ ParameterFormatterTest(ListStatusListener statusListener) {
+ this.statusListener = statusListener;
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ "0,,false,",
+ "0,,false,aaa",
+ "0,,true,\\{}",
+ "1,0,false,{}",
+ "1,0,true,{}\\{}",
+ "1,2,true,\\\\{}",
+ "2,8:10,true,foo \\{} {}{}",
+ "2,8:10,true,foo {\\} {}{}",
+ "2,0:2,false,{}{}",
+ "3,0:2:4,false,{}{}{}",
+ "4,0:2:4:8,false,{}{}{}aa{}",
+ "4,0:2:4:10,false,{}{}{}a{]b{}",
+ "5,0:2:4:7:10,false,{}{}{}a{}b{}"
+ })
+ void test_pattern_analysis(
+ final int placeholderCount,
+ final String placeholderCharIndicesString,
+ final boolean escapedPlaceholderFound,
+ final String pattern) {
+ MessagePatternAnalysis analysis = ParameterFormatter.analyzePattern(pattern, placeholderCount);
+ assertThat(analysis.placeholderCount).isEqualTo(placeholderCount);
+ if (placeholderCount > 0) {
+ final int[] placeholderCharIndices = Arrays.stream(placeholderCharIndicesString.split(":"))
+ .mapToInt(Integer::parseInt)
+ .toArray();
+ assertThat(analysis.placeholderCharIndices).startsWith(placeholderCharIndices);
+ assertThat(analysis.escapedCharFound).isEqualTo(escapedPlaceholderFound);
+ }
+ }
+
+ @ParameterizedTest
+ @CsvSource({"2,pan {} {},a,pan a {}", "3,pan {}{}{},a b,pan ab{}", "1,pan {},a b c,pan a"})
+ void format_should_warn_on_insufficient_args(
+ final int placeholderCount, final String pattern, final String argsStr, final String expectedMessage) {
+ final String[] args = argsStr.split(" ");
+ final int argCount = args.length;
+
+ String actualMessage = ParameterFormatter.format(pattern, args, argCount);
+ assertThat(actualMessage).isEqualTo(expectedMessage);
+ final List statusDataList = statusListener.getStatusData().collect(Collectors.toList());
+ assertThat(statusDataList).hasSize(1);
+ final StatusData statusData = statusDataList.get(0);
+ assertThat(statusData.getLevel()).isEqualTo(Level.WARN);
+ assertThat(statusData.getMessage().getFormattedMessage())
+ .isEqualTo(
+ "found %d argument placeholders, but provided %d for pattern `%s`",
+ placeholderCount, argCount, pattern);
+ }
+
+ @ParameterizedTest
+ @MethodSource("messageFormattingTestCases")
+ void format_should_work(
+ final String pattern, final Object[] args, final int argCount, final String expectedFormattedMessage) {
+ final String actualFormattedMessage = ParameterFormatter.format(pattern, args, argCount);
+ assertThat(actualFormattedMessage).isEqualTo(expectedFormattedMessage);
+ }
+
+ static Object[][] messageFormattingTestCases() {
+ return new Object[][] {
+ new Object[] {"Test message {}{} {}", new Object[] {"a", "b", "c"}, 3, "Test message ab c"},
+ new Object[] {
+ "Test message {} {} {} {} {} {}",
+ new Object[] {"a", null, "c", null, null, null},
+ 6,
+ "Test message a null c null null null"
+ },
+ new Object[] {
+ "Test message {}{} {}",
+ new Object[] {"a", "b", "c", "unnecessary", "superfluous"},
+ 5,
+ "Test message ab c"
+ },
+ new Object[] {"Test message \\{}{} {}", new Object[] {"a", "b", "c"}, 3, "Test message {}a b"},
+ new Object[] {"Test message {}{} {}\\", new Object[] {"a", "b", "c"}, 3, "Test message ab c\\"},
+ new Object[] {"Test message {}{} {}\\\\", new Object[] {"a", "b", "c"}, 3, "Test message ab c\\"},
+ new Object[] {"Test message \\\\{}{} {}", new Object[] {"a", "b", "c"}, 3, "Test message \\ab c"},
+ new Object[] {"Test message {}{} {}", new Object[] {"a", "b", "c"}, 3, "Test message ab c"},
+ new Object[] {
+ "Test message {} {} {} {} {} {}",
+ new Object[] {"a", null, "c", null, null, null},
+ 6,
+ "Test message a null c null null null"
+ },
+ new Object[] {
+ "Test message {}{} {}",
+ new Object[] {"a", "b", "c", "unnecessary", "superfluous"},
+ 5,
+ "Test message ab c"
+ },
+ new Object[] {"Test message \\{}{} {}", new Object[] {"a", "b", "c"}, 3, "Test message {}a b"},
+ new Object[] {"Test message {}{} {}\\", new Object[] {"a", "b", "c"}, 3, "Test message ab c\\"},
+ new Object[] {"Test message {}{} {}\\\\", new Object[] {"a", "b", "c"}, 3, "Test message ab c\\"},
+ new Object[] {"Test message \\\\{}{} {}", new Object[] {"a", "b", "c"}, 3, "Test message \\ab c"},
+ new Object[] {"foo \\\\\\{} {}", new Object[] {"bar"}, 1, "foo \\{} bar"},
+ new Object[] {"missing arg {} {}", new Object[] {1, 2}, 1, "missing arg 1 {}"},
+ new Object[] {"foo {\\} {}", new Object[] {"bar"}, 1, "foo {\\} bar"}
+ };
+ }
+
+ @Test
+ void testIdentityToString() {
+ final List list = new ArrayList<>();
+ list.add(1);
+ // noinspection CollectionAddedToSelf
+ list.add(list);
+ list.add(2);
+ final String actual = ParameterFormatter.identityToString(list);
+ final String expected = list.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(list));
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ void testDeepToString() {
+ final List list = new ArrayList<>();
+ list.add(1);
+ // noinspection CollectionAddedToSelf
+ list.add(list);
+ list.add(2);
+ final String actual = ParameterFormatter.deepToString(list);
+ final String expected = "[1, [..." + ParameterFormatter.identityToString(list) + "...], 2]";
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ void testDeepToStringUsingNonRecursiveButConsequentObjects() {
+ final List list = new ArrayList<>();
+ final Object item = Collections.singletonList(0);
+ list.add(1);
+ list.add(item);
+ list.add(2);
+ list.add(item);
+ list.add(3);
+ final String actual = ParameterFormatter.deepToString(list);
+ final String expected = "[1, [0], 2, [0], 3]";
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @ParameterizedTest
+ @MethodSource("deepToStringArgumentsPrimitiveArrays")
+ void testDeepToStringPrimitiveArrays(Object obj, String expected) {
+ final String actual = ParameterFormatter.deepToString(obj);
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ static Stream deepToStringArgumentsPrimitiveArrays() {
+ return Stream.of(
+ Arguments.of(new byte[] {0, 1, 2, 3, 4}, "[0, 1, 2, 3, 4]"),
+ Arguments.of(new short[] {0, 1, 2, 3, 4}, "[0, 1, 2, 3, 4]"),
+ Arguments.of(new int[] {0, 1, 2, 3, 4}, "[0, 1, 2, 3, 4]"),
+ Arguments.of(new long[] {0, 1, 2, 3, 4}, "[0, 1, 2, 3, 4]"),
+ Arguments.of(new float[] {0, 1, 2, 3, 4}, "[0.0, 1.0, 2.0, 3.0, 4.0]"),
+ Arguments.of(new double[] {0, 1, 2, 3, 4}, "[0.0, 1.0, 2.0, 3.0, 4.0]"),
+ Arguments.of(new char[] {'a', 'b', 'c'}, "[a, b, c]"),
+ Arguments.of(new boolean[] {false, true}, "[false, true]"));
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingTestBase.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingTestBase.java
new file mode 100644
index 00000000000..072696029a7
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingTestBase.java
@@ -0,0 +1,53 @@
+/*
+ * 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.message;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.logging.log4j.util.Constants;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link ParameterizedMessage#getFormattedMessage()} when formatted arguments causes another {@code ParameterizedMessage#getFormattedMessage()} (i.e., recursive) invocation.
+ */
+abstract class ParameterizedMessageRecursiveFormattingTestBase {
+
+ private final boolean threadLocalsEnabled;
+
+ ParameterizedMessageRecursiveFormattingTestBase(boolean threadLocalsEnabled) {
+ this.threadLocalsEnabled = threadLocalsEnabled;
+ }
+
+ @Test
+ void thread_locals_toggle_should_match() {
+ assertThat(Constants.ENABLE_THREADLOCALS).isEqualTo(threadLocalsEnabled);
+ }
+
+ @Test
+ void recursion_should_not_corrupt_formatting() {
+ final Object argInvokingParameterizedMessageFormatting = new Object() {
+ @Override
+ public String toString() {
+ return new ParameterizedMessage("bar {}", "baz").getFormattedMessage();
+ }
+ };
+ final ParameterizedMessage message =
+ new ParameterizedMessage("foo {}", argInvokingParameterizedMessageFormatting);
+ final String actualText = message.getFormattedMessage();
+ assertThat(actualText).isEqualTo("foo bar baz");
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingWithThreadLocalsTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingWithThreadLocalsTest.java
new file mode 100644
index 00000000000..6496a5f12f2
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingWithThreadLocalsTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.message;
+
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+/**
+ * {@link ParameterizedMessageRecursiveFormattingTestBase} subclass with thread locals disabled, i.e, with {@link StringBuilder} recycling.
+ */
+@SetSystemProperty(key = "log4j2.enable.threadlocals", value = "true")
+class ParameterizedMessageRecursiveFormattingWithThreadLocalsTest
+ extends ParameterizedMessageRecursiveFormattingTestBase {
+
+ ParameterizedMessageRecursiveFormattingWithThreadLocalsTest() {
+ super(true);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingWithoutThreadLocalsTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingWithoutThreadLocalsTest.java
new file mode 100644
index 00000000000..456d271d872
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageRecursiveFormattingWithoutThreadLocalsTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.message;
+
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+/**
+ * {@link ParameterizedMessageRecursiveFormattingTestBase} subclass with thread locals disabled, i.e, no {@link StringBuilder} recycling.
+ */
+@SetSystemProperty(key = "log4j2.enable.threadlocals", value = "false")
+class ParameterizedMessageRecursiveFormattingWithoutThreadLocalsTest
+ extends ParameterizedMessageRecursiveFormattingTestBase {
+
+ ParameterizedMessageRecursiveFormattingWithoutThreadLocalsTest() {
+ super(false);
+ }
+}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
new file mode 100644
index 00000000000..4bd5df91bef
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.message;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.apache.logging.log4j.test.junit.SerialUtil;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@UsingStatusListener
+class ParameterizedMessageTest {
+
+ final ListStatusListener statusListener;
+
+ ParameterizedMessageTest(ListStatusListener statusListener) {
+ this.statusListener = statusListener;
+ }
+
+ @Test
+ void testNoArgs() {
+ final String testMsg = "Test message {}";
+ ParameterizedMessage msg = new ParameterizedMessage(testMsg, (Object[]) null);
+ String result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ final Object[] array = null;
+ msg = new ParameterizedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ }
+
+ @Test
+ void testZeroLength() {
+ final String testMsg = "";
+ ParameterizedMessage msg = new ParameterizedMessage(testMsg, new Object[] {"arg"});
+ String result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ final Object[] array = null;
+ msg = new ParameterizedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ }
+
+ @Test
+ void testOneCharLength() {
+ final String testMsg = "d";
+ ParameterizedMessage msg = new ParameterizedMessage(testMsg, new Object[] {"arg"});
+ String result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ final Object[] array = null;
+ msg = new ParameterizedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ }
+
+ @Test
+ void testFormat3StringArgs() {
+ final String testMsg = "Test message {}{} {}";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c");
+ }
+
+ @Test
+ void testFormatNullArgs() {
+ final String testMsg = "Test message {} {} {} {} {} {}";
+ final String[] args = {"a", null, "c", null, null, null};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message a null c null null null");
+ }
+
+ @Test
+ void testFormatStringArgsIgnoresSuperfluousArgs() {
+ final String testMsg = "Test message {}{} {}";
+ final String[] args = {"a", "b", "c", "unnecessary", "superfluous"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c");
+ }
+
+ @Test
+ void testFormatStringArgsWithEscape() {
+ final String testMsg = "Test message \\{}{} {}";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message {}a b");
+ }
+
+ @Test
+ void testFormatStringArgsWithTrailingEscape() {
+ final String testMsg = "Test message {}{} {}\\";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c\\");
+ }
+
+ @Test
+ void testFormatStringArgsWithTrailingText() {
+ final String testMsg = "Test message {}{} {}Text";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab cText");
+ }
+
+ @Test
+ void testFormatStringArgsWithTrailingEscapedEscape() {
+ final String testMsg = "Test message {}{} {}\\\\";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c\\");
+ }
+
+ @Test
+ void testFormatStringArgsWithEscapedEscape() {
+ final String testMsg = "Test message \\\\{}{} {}";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message \\ab c");
+ }
+
+ @Test
+ void testSafeWithMutableParams() { // LOG4J2-763
+ final String testMsg = "Test message {}";
+ final Mutable param = new Mutable().set("abc");
+ final ParameterizedMessage msg = new ParameterizedMessage(testMsg, param);
+
+ // modify parameter before calling msg.getFormattedMessage
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertThat(actual).isEqualTo("Test message XYZ").as("Should use current param value");
+
+ // modify parameter after calling msg.getFormattedMessage
+ param.set("000");
+ final String after = msg.getFormattedMessage();
+ assertThat(after).isEqualTo("Test message XYZ").as("Should not change after rendered once");
+ }
+
+ static Stream testSerializable() {
+ @SuppressWarnings("EqualsHashCode")
+ class NonSerializable {
+ @Override
+ public boolean equals(final Object other) {
+ return other instanceof NonSerializable; // a very lenient equals()
+ }
+ }
+ return Stream.of(
+ "World",
+ new NonSerializable(),
+ new BigDecimal("123.456"),
+ // LOG4J2-3680
+ new RuntimeException(),
+ null);
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void testSerializable(final Object arg) {
+ final Message expected = new ParameterizedMessage("Hello {}!", arg);
+ final Message actual = SerialUtil.deserialize(SerialUtil.serialize(expected));
+ assertThat(actual).isInstanceOf(ParameterizedMessage.class);
+ assertThat(actual.getFormattedMessage()).isEqualTo(expected.getFormattedMessage());
+ }
+
+ /**
+ * In this test cases, constructed the following scenarios: