Merge pull request #314 from supertomcat/LOG4J2-2707
LOG4J2-2707: ArrayIndexOutOfBoundsException in UuidUtil, when MAC add…
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
index e0e5aef..2608c6c 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java
@@ -29,6 +29,7 @@
import org.apache.log4j.or.RendererSupport;
import org.apache.log4j.spi.LoggerFactory;
import org.apache.log4j.spi.LoggingEvent;
+import org.apache.logging.log4j.message.MapMessage;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.message.LocalizedMessage;
@@ -380,12 +381,15 @@
public void forcedLog(final String fqcn, final Priority level, final Object message, final Throwable t) {
final org.apache.logging.log4j.Level lvl = org.apache.logging.log4j.Level.toLevel(level.toString());
- ObjectRenderer renderer = get(message.getClass());
- final Message msg = message instanceof Message ? (Message) message : renderer != null ?
- new RenderedMessage(renderer, message) : new ObjectMessage(message);
if (logger instanceof ExtendedLogger) {
- ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, new ObjectMessage(message), t);
+ @SuppressWarnings("unchecked")
+ Message msg = message instanceof Message ? (Message) message : message instanceof Map ?
+ new MapMessage((Map) message) : new ObjectMessage(message);
+ ((ExtendedLogger) logger).logMessage(fqcn, lvl, null, msg, t);
} else {
+ ObjectRenderer renderer = get(message.getClass());
+ final Message msg = message instanceof Message ? (Message) message : renderer != null ?
+ new RenderedMessage(renderer, message) : new ObjectMessage(message);
logger.log(lvl, msg, t);
}
}
@@ -475,14 +479,16 @@
public void log(final Priority priority, final Object message, final Throwable t) {
if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
+ @SuppressWarnings("unchecked")
+ final Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
forcedLog(FQCN, priority, msg, t);
}
}
public void log(final Priority priority, final Object message) {
if (isEnabledFor(priority)) {
- final Message msg = new ObjectMessage(message);
+ @SuppressWarnings("unchecked")
+ final Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
forcedLog(FQCN, priority, msg, null);
}
}
@@ -497,10 +503,12 @@
private void maybeLog(final String fqcn, final org.apache.logging.log4j.Level level,
final Object message, final Throwable throwable) {
if (logger.isEnabled(level)) {
+ @SuppressWarnings("unchecked")
+ Message msg = message instanceof Map ? new MapMessage((Map) message) : new ObjectMessage(message);
if (logger instanceof ExtendedLogger) {
- ((ExtendedLogger) logger).logMessage(fqcn, level, null, new ObjectMessage(message), throwable);
+ ((ExtendedLogger) logger).logMessage(fqcn, level, null, msg, throwable);
} else {
- logger.log(level, message, throwable);
+ logger.log(level, msg, throwable);
}
}
}
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
index 9ad4d27..9de0476 100644
--- 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
@@ -16,8 +16,6 @@
*/
package org.apache.log4j.bridge;
-import java.lang.reflect.Method;
-
import org.apache.log4j.Category;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LocationInfo;
@@ -29,6 +27,10 @@
import org.apache.logging.log4j.spi.StandardLevel;
import org.apache.logging.log4j.status.StatusLogger;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+
/**
* Converts a Log4j 2 LogEvent into the components needed by a Log4j 1.x LoggingEvent.
* This class requires Log4j 2.
@@ -43,13 +45,47 @@
this.event = event;
}
+ /**
+ * Returns the time when the application started, in milliseconds
+ * elapsed since 01.01.1970.
+ */
+ public static long getStartTime() {
+ 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.
+ * Set the location information for this logging event. The collected
+ * information is cached for future use.
*/
@Override
public LocationInfo getLocationInformation() {
@@ -58,7 +94,8 @@
/**
* Return the level of this event. Use this form instead of directly
- * accessing the <code>level</code> field. */
+ * accessing the <code>level</code> field.
+ */
@Override
public Level getLevel() {
switch (StandardLevel.getStandardLevel(event.getLevel().intLevel())) {
@@ -70,8 +107,6 @@
return Level.INFO;
case WARN:
return Level.WARN;
- case ERROR:
- return Level.ERROR;
case FATAL:
return Level.FATAL;
case OFF:
@@ -104,27 +139,23 @@
Return the message for this logging event.
*/
@Override
- public
- Object getMessage() {
- return event.getMessage().getFormattedMessage();
+ public Object getMessage() {
+ return event.getMessage();
}
/*
* This method returns the NDC for this event.
*/
@Override
- public
- String getNDC() {
+ public String getNDC() {
return event.getContextStack().toString();
}
-
/*
Returns the the context corresponding to the <code>key</code> parameter.
*/
@Override
- public
- Object getMDC(String key) {
+ public Object getMDC(String key) {
if (event.getContextData() != null) {
return event.getContextData().getValue(key);
} else {
@@ -133,44 +164,34 @@
}
/**
- Obtain a copy of this thread's MDC prior to serialization or
- asynchronous logging.
+ * Obtain a copy of this thread's MDC prior to serialization or
+ * asynchronous logging.
*/
@Override
- public
- void getMDCCopy() {
+ public void getMDCCopy() {
}
@Override
- public
- String getRenderedMessage() {
+ public String getRenderedMessage() {
return event.getMessage().getFormattedMessage();
}
- /**
- Returns the time when the application started, in milliseconds
- elapsed since 01.01.1970. */
- public static long getStartTime() {
- return JVM_START_TIME;
- }
-
@Override
- public
- String getThreadName() {
+ public String getThreadName() {
return event.getThreadName();
}
/**
- Returns the throwable information contained within this
- event. May be <code>null</code> if there is no such information.
-
- <p>Note that the {@link Throwable} object contained within a
- {@link ThrowableInformation} does not survive serialization.
-
- @since 1.1 */
+ * Returns the throwable information contained within this
+ * event. May be <code>null</code> if there is no such information.
+ *
+ * <p>Note that the {@link Throwable} object contained within a
+ * {@link ThrowableInformation} does not survive serialization.
+ *
+ * @since 1.1
+ */
@Override
- public
- ThrowableInformation getThrowableInformation() {
+ public ThrowableInformation getThrowableInformation() {
if (event.getThrown() != null) {
return new ThrowableInformation(event.getThrown());
}
@@ -178,40 +199,28 @@
}
/**
- Return this event's throwable's string[] representaion.
+ * Return this event's throwable's string[] representaion.
*/
@Override
- public
- String[] getThrowableStrRep() {
+ public String[] getThrowableStrRep() {
if (event.getThrown() != null) {
return Throwables.toStringList(event.getThrown()).toArray(new String[0]);
}
return null;
}
- /**
- * 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);
+ @Override
+ public String getProperty(final String key) {
+ return event.getContextData().getValue(key);
+ }
- 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();
- }
+ @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 0000000..1e46e12
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/LogEventWrapper.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 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.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;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 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 volatile Thread thread;
+
+ public LogEventWrapper(LoggingEvent event) {
+ this.event = event;
+ this.contextData = new ContextDataMap(event.getProperties());
+ this.contextStack = new MutableThreadContextStack(NDC.cloneStack());
+ }
+
+ @Override
+ public LogEvent toImmutable() {
+ return null;
+ }
+
+ @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() {
+ MutableInstant mutable = new MutableInstant();
+ mutable.initFromEpochMilli(event.getTimeStamp(), 0);
+ return mutable;
+ }
+
+ @Override
+ public StackTraceElement getSource() {
+ 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() {
+ Thread thread = getThread();
+ return thread != null ? thread.getId() : 0;
+ }
+
+ @Override
+ public int getThreadPriority() {
+ Thread thread = getThread();
+ return thread != null ? thread.getPriority() : 0;
+ }
+
+ private Thread getThread() {
+ if (thread == null) {
+ for (Thread thread : Thread.getAllStackTraces().keySet()) {
+ if (thread.getName().equals(event.getThreadName())) {
+ this.thread = thread;
+ return thread;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Throwable getThrown() {
+ if (event.getThrowableInformation() != null) {
+ return event.getThrowableInformation().getThrowable();
+ }
+ return null;
+ }
+
+ @Override
+ public ThrowableProxy getThrownProxy() {
+ return null;
+ }
+
+ @Override
+ public boolean isEndOfBatch() {
+ return false;
+ }
+
+ @Override
+ public boolean isIncludeLocation() {
+ return false;
+ }
+
+ @Override
+ public void setEndOfBatch(boolean endOfBatch) {
+
+ }
+
+ @Override
+ public void setIncludeLocation(boolean locationRequired) {
+
+ }
+
+ @Override
+ public long getNanoTime() {
+ return 0;
+ }
+
+
+ private static class ContextDataMap extends HashMap<String, String> implements ReadOnlyStringMap {
+
+ ContextDataMap(Map<String, String> map) {
+ if (map != null) {
+ super.putAll(map);
+ }
+ }
+
+ @Override
+ public Map<String, String> toMap() {
+ return this;
+ }
+
+ @Override
+ public boolean containsKey(String key) {
+ return super.containsKey(key);
+ }
+
+ @Override
+ public <V> void forEach(BiConsumer<String, ? super V> action) {
+ super.forEach((k,v) -> action.accept(k, (V) v));
+ }
+
+ @Override
+ public <V, S> void forEach(TriConsumer<String, ? super V, S> action, S state) {
+ super.forEach((k,v) -> action.accept(k, (V) v, state));
+ }
+
+ @Override
+ public <V> V getValue(String key) {
+ return (V) super.get(key);
+ }
+ }
+}
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 0000000..cf0b42b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/bridge/RewritePolicyAdapter.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.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.
+ */
+ public RewritePolicyAdapter(RewritePolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public LogEvent rewrite(LogEvent source) {
+ 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 0000000..e0994e1
--- /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.spi.LoggingEvent;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.log4j.rewrite.RewritePolicy;
+
+/**
+ * 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(org.apache.logging.log4j.core.appender.rewrite.RewritePolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public LoggingEvent rewrite(LoggingEvent source) {
+ 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/BuilderManager.java b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/BuilderManager.java
index d6465f0..3319bf0 100644
--- 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
@@ -21,7 +21,9 @@
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.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;
@@ -72,7 +74,7 @@
if (plugin != null) {
AppenderBuilder builder = createBuilder(plugin, prefix, props);
if (builder != null) {
- return builder.parseAppender(name, layoutPrefix, filterPrefix, props, config);
+ return builder.parseAppender(name, prefix, layoutPrefix, filterPrefix, props, config);
}
}
return null;
@@ -127,6 +129,30 @@
return null;
}
+ public RewritePolicy parseRewritePolicy(String className, Element rewriteElement, XmlConfiguration config) {
+ PluginType<?> plugin = plugins.get(className.toLowerCase());
+ if (plugin != null) {
+ try {
+ @SuppressWarnings("unchecked")
+ RewritePolicyBuilder builder = (RewritePolicyBuilder) LoaderUtil.newInstanceOf(plugin.getPluginClass());
+ return builder.parseRewritePolicy(rewriteElement, config);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+ LOGGER.warn("Unable to load plugin: {} due to: {}", plugin.getKey(), ex.getMessage());
+ }
+ }
+ return null;
+ }
+ public RewritePolicy parseRewritePolicy(String className, String policyPrefix, Properties props, PropertiesConfiguration config) {
+ PluginType<?> plugin = plugins.get(className.toLowerCase());
+ if (plugin != null) {
+ RewritePolicyBuilder builder = createBuilder(plugin, policyPrefix, props);
+ if (builder != null) {
+ return builder.parseRewritePolicy(config);
+ }
+ }
+ return null;
+ }
+
private <T extends AbstractBuilder> T createBuilder(PluginType<?> plugin, String prefix, Properties props) {
try {
Class<?> clazz = plugin.getPluginClass();
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
index 0572b86..bb7ff93 100644
--- 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
@@ -30,7 +30,7 @@
Appender parseAppender(Element element, XmlConfiguration configuration);
- Appender parseAppender(String name, String layoutPrefix, String filterPrefix, Properties props,
- PropertiesConfiguration 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
index 86969ed..e60d50b 100644
--- 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
@@ -126,7 +126,7 @@
}
@Override
- public Appender parseAppender(final String name, final String layoutPrefix,
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
String appenderRef = getProperty(APPENDER_REF_TAG);
boolean blocking = getBooleanProperty(BLOCKING_PARAM);
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
index 423fab7..119e6fb 100644
--- 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
@@ -129,7 +129,7 @@
}
@Override
- public Appender parseAppender(final String name, final String layoutPrefix,
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
Layout layout = configuration.parseLayout(layoutPrefix, name, props);
Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
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
index 1dd1885..89c2dba 100644
--- 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
@@ -136,8 +136,8 @@
}
@Override
- public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
- final Properties props, final PropertiesConfiguration configuration) {
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
Layout layout = configuration.parseLayout(layoutPrefix, name, props);
Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
String fileName = getProperty(FILE_PARAM);
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
index 9d108c2..8b7f467 100644
--- 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
@@ -132,8 +132,8 @@
@Override
- public Appender parseAppender(final String name, final String layoutPrefix, final String filterPrefix,
- final Properties props, final PropertiesConfiguration configuration) {
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
Layout layout = configuration.parseLayout(layoutPrefix, name, props);
Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
String level = getProperty(THRESHOLD_PARAM);
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
index 8b69d39..07f2fd5 100644
--- 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
@@ -46,7 +46,7 @@
@Override
- public Appender parseAppender(final String name, final String layoutPrefix,
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
return new AppenderWrapper(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 0000000..37ac70f
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/RewriteAppenderBuilder.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 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.Holder;
+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.Logger;
+import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+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.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+
+/**
+ * Build an Asynch 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(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(final Element appenderElement, final XmlConfiguration config) {
+ String name = appenderElement.getAttribute(NAME_ATTR);
+ Holder<List<String>> appenderRefs = new Holder<>(new ArrayList<>());
+ Holder<RewritePolicy> rewritePolicyHolder = new Holder<>();
+ Holder<String> level = new Holder<>();
+ Holder<Filter> filter = new Holder<>();
+ forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+ switch (currentElement.getTagName()) {
+ case APPENDER_REF_TAG:
+ Appender appender = config.findAppenderByReference(currentElement);
+ if (appender != null) {
+ appenderRefs.get().add(appender.getName());
+ }
+ break;
+ case REWRITE_POLICY_TAG: {
+ RewritePolicy policy = config.parseRewritePolicy(currentElement);
+ if (policy != null) {
+ rewritePolicyHolder.set(policy);
+ }
+ break;
+ }
+ case FILTER_TAG: {
+ filter.set(config.parseFilters(currentElement));
+ break;
+ }
+ case PARAM_TAG: {
+ if (currentElement.getAttribute(NAME_ATTR).equalsIgnoreCase(THRESHOLD_PARAM)) {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+ } else {
+ level.set(value);
+ }
+ }
+ break;
+ }
+ }
+ });
+ return createAppender(name, level.get(), appenderRefs.get().toArray(new String[0]), 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) {
+ String appenderRef = getProperty(APPENDER_REF_TAG);
+ Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ String policyPrefix = appenderPrefix + ".rewritePolicy";
+ String className = getProperty(policyPrefix);
+ RewritePolicy policy = configuration.getBuilderManager().parseRewritePolicy(className, policyPrefix,
+ props, configuration);
+ String level = getProperty(THRESHOLD_PARAM);
+ if (appenderRef == null) {
+ LOGGER.warn("No appender references configured for AsyncAppender {}", name);
+ return null;
+ }
+ Appender appender = configuration.parseAppender(props, appenderRef);
+ if (appender == null) {
+ LOGGER.warn("Cannot locate Appender {}", appenderRef);
+ return null;
+ }
+ return createAppender(name, level, new String[] {appenderRef}, policy, filter, configuration);
+ }
+
+ private <T extends Log4j1Configuration> Appender createAppender(String name, String level,
+ String[] appenderRefs, RewritePolicy policy, Filter filter, T configuration) {
+ org.apache.logging.log4j.Level logLevel = OptionConverter.convertLevel(level,
+ org.apache.logging.log4j.Level.TRACE);
+ AppenderRef[] refs = new AppenderRef[appenderRefs.length];
+ int index = 0;
+ for (String appenderRef : appenderRefs) {
+ refs[index++] = AppenderRef.createAppenderRef(appenderRef, logLevel, null);
+ }
+ 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 new AppenderWrapper(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
index ccc8099..bac9425 100644
--- 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
@@ -158,7 +158,7 @@
@Override
- public Appender parseAppender(final String name, final String layoutPrefix,
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
Layout layout = configuration.parseLayout(layoutPrefix, name, props);
Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
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 0000000..c281ea9
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/appender/SyslogAppenderBuilder.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 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.bridge.LayoutWrapper;
+import org.apache.log4j.builders.AbstractBuilder;
+import org.apache.log4j.builders.Holder;
+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.SyslogAppender;
+import org.apache.logging.log4j.core.layout.SyslogLayout;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.logging.log4j.plugins.Plugin;
+import org.apache.logging.log4j.status.StatusLogger;
+import org.w3c.dom.Element;
+
+import java.util.Properties;
+
+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.NAME_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.PARAM_TAG;
+import static org.apache.log4j.xml.XmlConfiguration.VALUE_ATTR;
+import static org.apache.log4j.xml.XmlConfiguration.forEachElement;
+
+/**
+ * Build a File Appender
+ */
+@Plugin(name = "org.apache.log4j.net.SyslogAppender", category = CATEGORY)
+public class SyslogAppenderBuilder extends AbstractBuilder implements AppenderBuilder {
+
+ private static final Logger LOGGER = StatusLogger.getLogger();
+ private static final String FACILITY_PARAM = "Facility";
+ private static final String SYSLOG_HOST_PARAM = "SyslogHost";
+ private static int SYSLOG_PORT = 512;
+
+ public SyslogAppenderBuilder() {
+ }
+
+ public SyslogAppenderBuilder(String prefix, Properties props) {
+ super(prefix, props);
+ }
+
+ @Override
+ public Appender parseAppender(Element appenderElement, XmlConfiguration config) {
+ String name = appenderElement.getAttribute(NAME_ATTR);
+ Holder<Layout> layout = new Holder<>();
+ Holder<Filter> filter = new Holder<>();
+ Holder<String> facility = new Holder<>();
+ Holder<String> level = new Holder<>();
+ Holder<String> host = new Holder<>();
+ forEachElement(appenderElement.getChildNodes(), (currentElement) -> {
+ switch (currentElement.getTagName()) {
+ case LAYOUT_TAG:
+ layout.set(config.parseLayout(currentElement));
+ break;
+ case FILTER_TAG:
+ filter.set(config.parseFilters(currentElement));
+ break;
+ case PARAM_TAG: {
+ switch (currentElement.getAttribute(NAME_ATTR)) {
+ case SYSLOG_HOST_PARAM: {
+ host.set(currentElement.getAttribute(VALUE_ATTR));
+ break;
+ }
+ case FACILITY_PARAM:
+ facility.set(currentElement.getAttribute(VALUE_ATTR));
+ break;
+ case THRESHOLD_PARAM: {
+ String value = currentElement.getAttribute(VALUE_ATTR);
+ if (value == null) {
+ LOGGER.warn("No value supplied for Threshold parameter, ignoring.");
+ } else {
+ level.set(value);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ });
+
+ return createAppender(name, config, layout.get(), facility.get(), filter.get(), host.get(), level.get());
+ }
+
+
+ @Override
+ public Appender parseAppender(final String name, final String appenderPrefix, final String layoutPrefix,
+ final String filterPrefix, final Properties props, final PropertiesConfiguration configuration) {
+ Filter filter = configuration.parseAppenderFilters(props, filterPrefix, name);
+ Layout layout = configuration.parseLayout(layoutPrefix, name, props);
+ String level = getProperty(THRESHOLD_PARAM);
+ String facility = getProperty(FACILITY_PARAM, "LOCAL0");
+ String syslogHost = getProperty(SYSLOG_HOST_PARAM, "localhost:514");
+
+ return createAppender(name, configuration, layout, facility, filter, syslogHost, level);
+ }
+
+ private Appender createAppender(final String name, final Log4j1Configuration configuration, Layout layout,
+ String facility, final Filter filter, final String syslogHost, final String level) {
+ Holder<String> host = new Holder<>();
+ Holder<Integer> port = new Holder<>();
+ resolveSyslogHost(syslogHost, host, port);
+ org.apache.logging.log4j.core.Layout appenderLayout;
+ if (layout instanceof LayoutWrapper) {
+ appenderLayout = ((LayoutWrapper) layout).getLayout();
+ } else if (layout != null) {
+ appenderLayout = new LayoutAdapter(layout);
+ } else {
+ appenderLayout = SyslogLayout.newBuilder()
+ .setFacility(Facility.toFacility(facility))
+ .setConfiguration(configuration)
+ .build();
+ }
+
+ org.apache.logging.log4j.core.Filter fileFilter = buildFilters(level, filter);
+ return new AppenderWrapper(SyslogAppender.newBuilder()
+ .setName(name)
+ .setConfiguration(configuration)
+ .setLayout(appenderLayout)
+ .setFilter(fileFilter)
+ .setPort(port.get())
+ .setProtocol(Protocol.TCP)
+ .setHost(host.get())
+ .build());
+ }
+
+ private void resolveSyslogHost(String syslogHost, Holder<String> host, Holder<Integer> port) {
+ int urlPort = -1;
+
+ //
+ // If not an unbracketed IPv6 address then
+ // parse as a URL
+ //
+ String[] parts = syslogHost.split(":");
+ if (parts.length == 1) {
+ host.set(parts[0]);
+ port.set(SYSLOG_PORT);
+ } else if (parts.length == 2) {
+ host.set(parts[0]);
+ port.set(Integer.parseInt(parts[1]));
+ } else {
+ LOGGER.warn("Invalid syslogHost setting: {}. Using default", syslogHost);
+ host.set("localhost");
+ port.set(SYSLOG_PORT);
+ }
+ }
+}
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 0000000..5ca196a
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/builders/rewrite/RewritePolicyBuilder.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.config.PropertiesConfiguration;
+import org.apache.log4j.rewrite.RewritePolicy;
+import org.apache.log4j.xml.XmlConfiguration;
+import org.w3c.dom.Element;
+
+/**
+ * Define a RewritePolicy Builder.
+ */
+public interface RewritePolicyBuilder {
+
+ RewritePolicy parseRewritePolicy(Element element, XmlConfiguration config);
+
+ RewritePolicy parseRewritePolicy(PropertiesConfiguration config);
+}
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
index 021afa3..9236006 100644
--- 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
@@ -16,6 +16,7 @@
*/
package org.apache.log4j.config;
+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;
@@ -35,12 +36,19 @@
public static final String NULL = "null";
+ protected final BuilderManager manager;
+
public Log4j1Configuration(final LoggerContext loggerContext, final ConfigurationSource source,
int monitorIntervalSeconds) {
super(loggerContext, source);
+ manager = new BuilderManager();
initializeWatchers(this, source, monitorIntervalSeconds);
}
+ public BuilderManager getBuilderManager() {
+ return manager;
+ }
+
/**
* Initialize the configuration.
*/
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
index c3e995d..2f7ac58 100644
--- 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
@@ -66,8 +66,6 @@
private final Map<String, Appender> registry;
- private final BuilderManager manager;
-
/**
* No argument constructor.
*/
@@ -75,7 +73,6 @@
int monitorIntervalSeconds) {
super(loggerContext, source, monitorIntervalSeconds);
registry = new HashMap<>();
- manager = new BuilderManager();
}
public void doConfigure() {
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
index fe9530d..e9e35f9 100644
--- 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
@@ -18,6 +18,7 @@
import org.apache.log4j.Appender;
import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
import java.util.Collections;
import java.util.Enumeration;
@@ -67,4 +68,20 @@
public void removeAppender(String name) {
appenders.remove(name);
}
+
+ /**
+ * Call the <code>doAppend</code> method on all attached appenders.
+ */
+ public int appendLoopOnAppenders(LoggingEvent event) {
+ for (Appender appender : appenders.values()) {
+ appender.doAppend(event);
+ }
+ return appenders.size();
+ }
+
+ public void close() {
+ for (Appender appender : appenders.values()) {
+ appender.close();
+ }
+ }
}
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
index e09e26b..5f910fa 100644
--- 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
@@ -20,6 +20,7 @@
import org.apache.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.spi.StandardLevel;
import org.apache.logging.log4j.util.LoaderUtil;
import java.io.InterruptedIOException;
@@ -358,6 +359,30 @@
return org.apache.logging.log4j.Level.ALL;
}
+ public static Level convertLevel(org.apache.logging.log4j.Level level) {
+ if (level == null) {
+ return Level.ERROR;
+ }
+ switch (level.getStandardLevel()) {
+ case FATAL:
+ return Level.FATAL;
+ case WARN:
+ return Level.WARN;
+ case INFO:
+ return Level.INFO;
+ case DEBUG:
+ return Level.DEBUG;
+ case TRACE:
+ return Level.TRACE;
+ case ALL:
+ return Level.ALL;
+ case OFF:
+ return Level.OFF;
+ default:
+ return Level.ERROR;
+ }
+ }
+
/**
* Find the value corresponding to <code>key</code> in
* <code>props</code>. Then perform variable substitution on the
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 0000000..4f53583
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/MapRewritePolicy.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.rewrite;
+
+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;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 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.
+ * <p>
+ * 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}
+ */
+ public LoggingEvent rewrite(final LoggingEvent source) {
+ Object msg = source.getMessage();
+ if (msg instanceof MapMessage || msg instanceof Map) {
+ Map<String, String> props = source.getProperties() != null ? new HashMap<>(source.getProperties())
+ : new HashMap<>();
+ @SuppressWarnings("unchecked")
+ Map<String, Object> 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;
+ Object newMsg = eventProps.get("message");
+ if (newMsg != null) {
+ newMessage = new SimpleMessage(newMsg.toString());
+ for (Map.Entry<String, Object> 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 {
+ LocationInfo info = source.getLocationInformation();
+ StackTraceElement element = new StackTraceElement(info.getClassName(), info.getMethodName(),
+ info.getFileName(), Integer.parseInt(info.getLineNumber()));
+ Thread thread = getThread(source.getThreadName());
+ long threadId = thread != null ? thread.getId() : 0;
+ 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)
+ .setThrownProxy(null)
+ .build();
+ }
+ return new LogEventAdapter(event);
+ } else {
+ return source;
+ }
+
+ }
+
+ private Thread getThread(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 0000000..b822a99
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/rewrite/PropertyRewritePolicy.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.log4j.rewrite;
+
+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;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * This policy rewrites events by adding
+ * a user-specified list of properties to the event.
+ * Existing properties are not modified.
+ * <p>
+ * 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<String, String> properties = Collections.EMPTY_MAP;
+
+ public PropertyRewritePolicy() {
+ }
+
+ /**
+ * Set a string representing the property name/value pairs.
+ * <p>
+ * Form: propname1=propvalue1,propname2=propvalue2
+ *
+ * @param props
+ */
+ public void setProperties(String props) {
+ Map hashTable = new HashMap();
+ StringTokenizer pairs = new StringTokenizer(props, ",");
+ while (pairs.hasMoreTokens()) {
+ StringTokenizer entry = new StringTokenizer(pairs.nextToken(), "=");
+ hashTable.put(entry.nextElement().toString().trim(), entry.nextElement().toString().trim());
+ }
+ synchronized (this) {
+ properties = hashTable;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public LoggingEvent rewrite(final LoggingEvent source) {
+ if (!properties.isEmpty()) {
+ Map<String, String> rewriteProps = source.getProperties() != null ? new HashMap<>(source.getProperties())
+ : new HashMap<>();
+ for (Map.Entry<String, String> 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 {
+ LocationInfo info = source.getLocationInformation();
+ StackTraceElement element = new StackTraceElement(info.getClassName(), info.getMethodName(),
+ info.getFileName(), Integer.parseInt(info.getLineNumber()));
+ Thread thread = getThread(source.getThreadName());
+ long threadId = thread != null ? thread.getId() : 0;
+ 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)
+ .setThrownProxy(null)
+ .build();
+ }
+ return new LogEventAdapter(event);
+ }
+ return source;
+ }
+
+ private Thread getThread(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 0000000..9570218
--- /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/spi/LoggingEvent.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/LoggingEvent.java
index e9f57de..d7755a2 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
@@ -20,6 +20,9 @@
import org.apache.log4j.Level;
import org.apache.log4j.bridge.LogEventAdapter;
+import java.util.Map;
+import java.util.Set;
+
/**
* 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.
@@ -49,6 +52,14 @@
return null;
}
+ public String getFQNOfLoggerClass() {
+ return null;
+ }
+
+ public final long getTimeStamp() {
+ return 0;
+ }
+
/**
* Gets the logger of the event.
* Use should be restricted to cloning events.
@@ -128,4 +139,24 @@
return null;
}
+ public void setProperty(final String propName,
+ final String propValue) {
+
+ }
+
+ public String getProperty(final String key) {
+ return null;
+ }
+
+ public Set getPropertyKeySet() {
+ return null;
+ }
+
+ public Map getProperties() {
+ return null;
+ }
+
+ public Object removeProperty(String propName) {
+ return null;
+ }
}
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
index c96c26d..a37f140 100644
--- 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
@@ -21,10 +21,10 @@
import org.apache.log4j.Level;
import org.apache.log4j.bridge.AppenderAdapter;
import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.log4j.builders.BuilderManager;
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;
@@ -99,8 +99,6 @@
*/
protected static final String DEFAULT_PREFIX = "log4j";
- private final BuilderManager manager;
-
// key: appenderName, value: appender
private Map<String, Appender> appenderMap;
@@ -110,7 +108,6 @@
int monitorIntervalSeconds) {
super(loggerContext, source, monitorIntervalSeconds);
appenderMap = new HashMap<>();
- manager = new BuilderManager();
}
public void addAppenderIfAbsent(Appender appender) {
@@ -415,6 +412,45 @@
return null;
}
+
+ public RewritePolicy parseRewritePolicy(Element rewritePolicyElement) {
+
+ 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 {
+ RewritePolicy policy = LoaderUtil.newInstanceOf(className);
+ 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) {
+ 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.
*/
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 0000000..840971a
--- /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 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.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test RewriteAppender
+ */
+public class MapRewriteAppenderTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-mapRewrite.xml");
+ }
+
+ @After
+ public void after() {
+ ThreadContext.clearMap();
+ }
+
+ @Test
+ public void testRewrite() throws Exception {
+ Logger logger = LogManager.getLogger("test");
+ Map<String, String> map = new HashMap<>();
+ map.put("message", "This is a test");
+ map.put("hello", "world");
+ logger.debug(map);
+ LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = context.getConfiguration();
+ Map<String, Appender> appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+ if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull("No Event Appender", eventAppender);
+ List<LoggingEvent> events = eventAppender.getEvents();
+ assertTrue("No events", events != null && events.size() > 0);
+ assertNotNull("No properties in the event", events.get(0).getProperties());
+ assertTrue("Key was not inserted", events.get(0).getProperties().containsKey("hello"));
+ assertEquals("Key value is incorrect", "world", events.get(0).getProperties().get("hello"));
+ }
+}
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 0000000..d7aca5c
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/RewriteAppenderTest.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.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.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test RewriteAppender
+ */
+public class RewriteAppenderTest {
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty(ConfigurationFactory.LOG4J1_CONFIGURATION_FILE_PROPERTY, "target/test-classes/log4j1-rewrite.xml");
+ }
+
+ @After
+ public void after() {
+ ThreadContext.clearMap();
+ }
+
+ @Test
+ public void testRewrite() throws Exception {
+ Logger logger = LogManager.getLogger("test");
+ ThreadContext.put("key1", "This is a test");
+ ThreadContext.put("hello", "world");
+ logger.debug("Say hello");
+ LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+ Configuration configuration = context.getConfiguration();
+ Map<String, Appender> appenders = configuration.getAppenders();
+ ListAppender eventAppender = null;
+ for (Map.Entry<String, Appender> entry : appenders.entrySet()) {
+ if (entry.getKey().equals("events")) {
+ eventAppender = (ListAppender) ((AppenderAdapter.Adapter) entry.getValue()).getAppender();
+ }
+ }
+ assertNotNull("No Event Appender", eventAppender);
+ List<LoggingEvent> events = eventAppender.getEvents();
+ assertTrue("No events", events != null && events.size() > 0);
+ assertNotNull("No properties in the event", events.get(0).getProperties());
+ assertTrue("Key was not inserted", events.get(0).getProperties().containsKey("key2"));
+ assertEquals("Key value is incorrect", "Log4j", events.get(0).getProperties().get("key2"));
+ }
+}
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 0000000..7ccb463
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/config/SyslogAppenderTest.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.config;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.net.mock.MockSyslogServer;
+import org.apache.logging.log4j.core.net.mock.MockSyslogServerFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+
+
+/**
+ * Class Description goes here.
+ */
+public class SyslogAppenderTest {
+
+ private static final int PORTNUM = 9999;
+ private MockSyslogServer syslogServer;
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty("log4j.configuration", "target/test-classes/log4j1-syslog.xml");
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void teardown() {
+ if (syslogServer != null) {
+ syslogServer.shutdown();
+ }
+ }
+
+ @Test
+ public void sendMessage() throws Exception {
+ initTCPTestEnvironment(null);
+ Logger logger = LogManager.getLogger(SyslogAppenderTest.class);
+ logger.info("This is a test");
+ List<String> messages = null;
+ for (int i = 0; i < 5; ++i) {
+ Thread.sleep(250);
+ messages = syslogServer.getMessageList();
+ if (messages != null && messages.size() > 0) {
+ break;
+ }
+ }
+ assertNotNull("No messages received", messages);
+ assertEquals("Sent message not detected", 1, messages.size());
+ }
+
+
+ protected void initTCPTestEnvironment(final String messageFormat) throws IOException {
+ syslogServer = MockSyslogServerFactory.createTCPSyslogServer(1, PORTNUM);
+ syslogServer.start();
+ }
+}
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 0000000..19973f4
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-maprewrite.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-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 "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!--<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">-->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="events" class="org.apache.log4j.ListAppender">
+ </appender>
+
+ <appender name="rewrite" class="org.apache.log4j.rewrite.RewriteAppender">
+ <appender-ref ref="events"/>
+ <rewritePolicy class="org.apache.log4j.rewrite.MapRewritePolicy">
+ </rewritePolicy>
+ </appender>
+
+ <root>
+ <priority value ="trace" />
+ <appender-ref ref="rewrite" />
+ </root>
+
+</log4j:configuration>
\ No newline at end of file
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 0000000..25eb1b5
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-rewrite.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-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 "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!--<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">-->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="events" class="org.apache.log4j.ListAppender">
+ </appender>
+
+ <appender name="rewrite" class="org.apache.log4j.rewrite.RewriteAppender">
+ <appender-ref ref="events"/>
+ <rewritePolicy class="org.apache.log4j.rewrite.PropertyRewritePolicy">
+ <param name="properties" value="key2=Log4j"/>
+ </rewritePolicy>
+ </appender>
+
+ <root>
+ <priority value ="trace" />
+ <appender-ref ref="rewrite" />
+ </root>
+
+</log4j:configuration>
\ No newline at end of file
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 0000000..4fd96a8
--- /dev/null
+++ b/log4j-1.2-api/src/test/resources/log4j1-syslog.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-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 "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="syslog" class="org.apache.log4j.net.SyslogAppender">
+ <param name="SyslogHost" value="localhost:9999"/>
+ <param name="Facility" value="USER"/>
+ <param name="FacilityPrinting" value="true"/>
+ <param name="Threshold" value="DEBUG"/>
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
+ </layout>
+ </appender>
+
+ <root>
+ <priority value ="trace" />
+ <appender-ref ref="syslog" />
+ </root>
+
+</log4j:configuration>
\ No newline at end of file
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7871c47..81dae7d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -157,6 +157,9 @@
</action>
</release>
<release version="2.13.0" date="2019-MM-DD" description="GA Release 2.13.0">
+ <action issue="LOG4J2-63" dev="rgoers" type="add">
+ Add experimental support for Log4j 1 configuration files.
+ </action>
<action issue="LOG4J2-2712" dev="rgoers" type="fix">
The rolling file appenders would fail to compress the file after rollover if the file name matched the
file pattern.
diff --git a/src/site/markdown/manual/compatibility.md b/src/site/markdown/manual/compatibility.md
index df0d728..69a1b30 100644
--- a/src/site/markdown/manual/compatibility.md
+++ b/src/site/markdown/manual/compatibility.md
@@ -28,17 +28,67 @@
## Configuration Compatibility
Log4j 2 provides experimental support for Log4j 1 configuration files. Configuration of the Appenders, Layouts
-and Filters that were provided in the Log4j 1 distribution will be redirected to their Log4j 2 counterparts.
-This means that although the while the behavior of these components will be similar they may not be exactly the
-same. For example, the XML generated by the XMLLayout may not exactly match the XML generated by the Log4j 1
-XMLLayout.
+and Filters that were provided in the Log4j 1 distribution will be redirected to their Log4j 2 counterparts -
+with the exception of the implemented Rewrite Policies. This means that although the while the behavior of these
+components will be similar they may not be exactly the same. For example, the XML generated by the XMLLayout may
+not exactly match the XML generated by the Log4j 1XMLLayout.
In addition, Log4j 2 supports custom Log4j 1 Appenders, Filters, and Layouts with some constraints. Since the
-original Log4j 1 components are not present in Log4j 2 custom components that extend them will fail.
+original Log4j 1 components may not be present in Log4j 2, custom components that extend them will fail.
As support for Log4j 1 is an experimental feature one of the following steps must be taken to enable it:
1. Set the system property "log4j1.compatibility" to a value of "true". Log4j 2 will then add log4j.properties,
log4j-test.properties, log4j.xml and log4j-test.xml to the configuration files it searches for on the class path.
1. Set the Log4j 1 system property "log4j.configuration" to the location of the log4j 1 configuration file. The
-files must have a file extension of either ".properties" or ".xml".
\ No newline at end of file
+files must have a file extension of either ".properties" or ".xml".
+
+## Supported Components
+### Appenders
+
+* AsyncAppender
+* ConsoleAppender
+* DailyRollingFileAppender
+* FileAppender
+* NullAppender
+* RewriteAppender (limited)
+* RollingFileAppender
+* SyslogAppender
+
+## Filters
+
+* DenyAllFilter
+* LevelMatchFilter
+* LevelRangeFilter
+* StringMatchFilter
+
+## Layouts
+
+* HtmlLayout
+* PatternLayout
+* SimpleLayout
+* TTCCLayout
+* XmlLayout
+
+## Rewrite Policies
+
+* MapRewritePolicy
+* PropertyRewritePolicy
+
+## Unsupported or Unimplemented Components
+### Appenders
+
+* JDBCAppender (cannot be mapped to Log4j 2's JdbcAppender)
+* JMSAppender
+* SMTPAppender
+* SocketAppender (Requires the use of the SerializedLayout which is a security risk)
+* SocketHubAppender (Requires the use of the SerializedLayout which is a securiy risk)
+* TelnetAppender (Security risk)
+
+## Rewrite Policies
+
+* ReflectionRewritePolicy
+* Custom rewrite policies since LoggingEvent is currently a no-op.
+
+### Renderers
+Log4j 2 currently will ignore renderers.
\ No newline at end of file