Index: src/site/xdoc/manual/layouts.xml.vm =================================================================== --- src/site/xdoc/manual/layouts.xml.vm (revision 1574084) +++ src/site/xdoc/manual/layouts.xml.vm (working copy) @@ -393,6 +393,41 @@ + enc{pattern}
+ encode{pattern} + + +

+ Encodes special characters such as '\n' and HTML characters to help prevent log forging + and some XSS attacks that could occur when displaying logs in a web browser. Anytime + user provided data is logged, this can provide a safeguard. +

+

+ A typical usage would encode the message +

%enc{%m}
+ but user input could come from other locations as well, such as the MDC +
%enc{%mdc{key}}
+

+

The replaced characters are: + + + + + + + + + + + + + +
CharacterReplacement
'\r', '\n'Removed from the pattern
&, <, >, ", ', /Replaced with the corresponding HTML entity
+

+ + + + ex|exception|throwable
  {["none"
  |"full"
Index: log4j-api/src/test/java/org/apache/logging/log4j/spi/LoggerOutputStreamTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/spi/LoggerOutputStreamTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/spi/LoggerOutputStreamTest.java (revision 0) @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.spi; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.TestLogger; +import org.apache.logging.log4j.spi.LoggerOutputStream; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import static org.hamcrest.core.StringStartsWith.startsWith; +import static org.junit.Assert.*; + +@RunWith(Parameterized.class) +public class LoggerOutputStreamTest { + private List results; + private LoggerOutputStream stream; + private Level level; + private String logMessage; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[][]{ + { Level.DEBUG, "debug log string test" }, + { Level.INFO, "info log string test" }, + { Level.WARN, "DANGER ZONE" }, + { Level.ERROR, "MAYDAY! MAYDAY!" }, + { Level.FATAL, "ABANDON SHIP!" } + } + ); + } + + public LoggerOutputStreamTest(final Level level, final String logMessage) { + this.level = level; + this.logMessage = logMessage; + } + + @Before + public void setUp() throws Exception { + TestLogger logger = (TestLogger) LogManager.getLogger(); + results = logger.getEntries(); + assertEmpty(); + stream = new LoggerOutputStream(logger, level); + assertEmpty(); + } + + @After + public void tearDown() throws Exception { + results.clear(); + } + + private void assertEmpty() { + assertTrue("There should be no results yet.", results.isEmpty()); + } + + private void assertNumResults(int numResults) { + assertEquals("Unexpected number of results.", numResults, results.size()); + } + + private void assertMessageStartsWith(final String message) { + assertNumResults(1); + final String start = ' ' + level.name() + ' ' + message; + assertThat(results.get(0), startsWith(start)); + } + + @Test + public void testWrite_Int() throws Exception { + for (byte b : logMessage.getBytes()) { + stream.write(b); + assertEmpty(); + } + stream.write('\n'); + assertMessageStartsWith(logMessage); + } + + @Test + public void testWrite_ByteArray() throws Exception { + final byte[] bytes = logMessage.getBytes(); + stream.write(bytes); + assertEmpty(); + stream.write('\n'); + assertMessageStartsWith(logMessage); + } + + @Test + public void testWrite_ByteArray_Offset_Length() throws Exception { + final byte[] bytes = logMessage.getBytes(); + int middle = bytes.length/2; + int length = bytes.length - middle; + final String right = new String(bytes, middle, length); + stream.write(bytes, middle, length); + assertEmpty(); + stream.write('\n'); + assertMessageStartsWith(right); + } + + @Test + public void testPrint_Boolean() throws Exception { + stream.print(true); + assertEmpty(); + stream.println(); + assertMessageStartsWith("true"); + } + + @Test + public void testPrint_Character() throws Exception { + for (char c : logMessage.toCharArray()) { + stream.print(c); + assertEmpty(); + } + stream.println(); + assertMessageStartsWith(logMessage); + } + + @Test + public void testPrint_Integer() throws Exception { + int n = logMessage.codePointAt(0); + stream.print(n); + assertEmpty(); + stream.println(); + assertMessageStartsWith(String.valueOf(n)); + } + + @Test + public void testPrint_CharacterArray() throws Exception { + stream.print(logMessage.toCharArray()); + assertEmpty(); + stream.println(); + assertMessageStartsWith(logMessage); + } + + @Test + public void testPrint_String() throws Exception { + stream.print(logMessage); + assertEmpty(); + stream.println(); + assertMessageStartsWith(logMessage); + } + + @Test + public void testPrint_Object() throws Exception { + final Object o = logMessage; + stream.print(o); + assertEmpty(); + stream.println(); + assertMessageStartsWith(logMessage); + } + + @Test + public void testPrintf() throws Exception { + stream.printf("<<<%s>>>", logMessage); + assertEmpty(); + stream.println(); + assertMessageStartsWith("<<<" + logMessage); + } + + @Test + public void testFormat() throws Exception { + stream.format("[%s]", logMessage).println(); + assertMessageStartsWith("[" + logMessage); + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java (revision 1574084) +++ log4j-api/src/main/java/org/apache/logging/log4j/spi/AbstractLogger.java (working copy) @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.spi; -import java.io.PrintWriter; import java.io.Serializable; import org.apache.logging.log4j.Level; @@ -32,7 +31,7 @@ /** * Base implementation of a Logger. It is highly recommended that any Logger implementation extend this class. */ -public abstract class AbstractLogger implements Logger, Serializable { +public abstract class AbstractLogger implements Logger, ExtensibleLogger, Serializable { private static final long serialVersionUID = 2L; @@ -851,29 +850,6 @@ } /** - * Gets a print stream that logs lines to this logger. - * - * @param level the logging level - * @return print stream that logs printed lines to this logger. - */ - @Override - public PrintWriter printWriter(final Level level) { - return new PrintWriter(new LoggerWriter(this, null, level), true); - } - - /** - * Gets a marked print stream that logs lines to this logger. - * - * @param marker the marker data specific to this log statement - * @param level the logging level - * @return print stream that logs printed lines to this logger. - */ - @Override - public PrintWriter printWriter(Marker marker, Level level) { - return new PrintWriter(new LoggerWriter(this, marker, level), true); - } - - /** * Logs a message with the specific Marker at the INFO level. * * @param marker the marker data specific to this log statement @@ -1475,7 +1451,7 @@ * @param data The Message. * @param t A Throwable or null. */ - public abstract void log(Marker marker, String fqcn, Level level, Message data, Throwable t); + protected abstract void log(Marker marker, String fqcn, Level level, Message data, Throwable t); /** * Logs a formatted message using the specified format string and arguments. @@ -1965,4 +1941,78 @@ } } + @Override + public LoggerExtension extend(Class extendingClass) { + return new Extension(extendingClass.getName()); + } + + private final class Extension implements LoggerExtension { + + private final String fqcn; + + Extension(String fqcn) { + this.fqcn = fqcn; + } + + @Override + public boolean isEnabled(Level level, Marker marker, Message data, Throwable t) { + return AbstractLogger.this.isEnabled(level, marker, data, t); + } + + @Override + public boolean isEnabled(Level level, Marker marker, Object data, Throwable t) { + return AbstractLogger.this.isEnabled(level, marker, data, t); + } + + @Override + public boolean isEnabled(Level level, Marker marker, String data) { + return AbstractLogger.this.isEnabled(level, marker, data); + } + + @Override + public boolean isEnabled(Level level, Marker marker, String data, Object... p1) { + return AbstractLogger.this.isEnabled(level, marker, data, p1); + } + + @Override + public boolean isEnabled(Level level, Marker marker, String data, Throwable t) { + return AbstractLogger.this.isEnabled(level, marker, data, t); + } + + @Override + public void log(Level level, Marker marker, Message data, Throwable t) { + if (AbstractLogger.this.isEnabled(level, marker, data, t)) { + AbstractLogger.this.log(marker, fqcn, level, data, t); + } + } + + @Override + public void log(Level level, Marker marker, Object data, Throwable t) { + if (AbstractLogger.this.isEnabled(level, marker, data, t)) { + AbstractLogger.this.log(marker, fqcn, level, getMessageFactory().newMessage(data), t); + } + } + + @Override + public void log(Level level, Marker marker, String data) { + if (AbstractLogger.this.isEnabled(level, marker, data)) { + AbstractLogger.this.log(marker, fqcn, level, getMessageFactory().newMessage(data), null); + } + } + + @Override + public void log(Level level, Marker marker, String data, Object... p1) { + if (AbstractLogger.this.isEnabled(level, marker, data)) { + AbstractLogger.this.log(marker, fqcn, level, getMessageFactory().newMessage(data, p1), null); + } + } + + @Override + public void log(Level level, Marker marker, String data, Throwable t) { + if (AbstractLogger.this.isEnabled(level, marker, data)) { + AbstractLogger.this.log(marker, fqcn, level, getMessageFactory().newMessage(data), null); + } + } + + } } Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerExtension.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerExtension.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerExtension.java (revision 0) @@ -0,0 +1,28 @@ +package org.apache.logging.log4j.spi; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.message.Message; + +public interface LoggerExtension { + + boolean isEnabled(Level level, Marker marker, Message data, Throwable t); + + boolean isEnabled(Level level, Marker marker, Object data, Throwable t); + + boolean isEnabled(Level level, Marker marker, String data); + + boolean isEnabled(Level level, Marker marker, String data, Object... p1); + + boolean isEnabled(Level level, Marker marker, String data, Throwable t); + + void log(Level level, Marker marker, Message data, Throwable t); + + void log(Level level, Marker marker, Object data, Throwable t); + + void log(Level level, Marker marker, String data); + + void log(Level level, Marker marker, String data, Object... p1); + + void log(Level level, Marker marker, String data, Throwable t); +} Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/ExtensibleLogger.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/spi/ExtensibleLogger.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/spi/ExtensibleLogger.java (revision 0) @@ -0,0 +1,6 @@ +package org.apache.logging.log4j.spi; + + +public interface ExtensibleLogger { + public LoggerExtension extend(Class extensionClass); +} Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerWriter.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerWriter.java (revision 1574084) +++ log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerWriter.java (working copy) @@ -18,61 +18,117 @@ import java.io.IOException; import java.io.Writer; +import java.nio.CharBuffer; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.message.Message; public class LoggerWriter extends Writer { - private static final String FQCN = LoggerWriter.class.getName(); - private final AbstractLogger logger; + private final Writer writer; + private final LoggerExtension logger; private final Level level; private final Marker marker; - private final StringBuilder buf = new StringBuilder(); + private final StringBuilder msg = new StringBuilder(); + private boolean closed; - public LoggerWriter(AbstractLogger logger, Marker marker, Level level) { + public LoggerWriter(Writer writer, LoggerExtension logger, Level level, Marker marker) { + this.writer = writer; this.logger = logger; - this.marker = marker; this.level = level; + this.marker = marker; } @Override - public void close() throws IOException { - // don't log a blank message if the last character was a newline - if (buf.length() > 0) { - log(); + public void write(int c) throws IOException { + extractMessages(new char[] { (char) c }, 0, 1); + if (writer != null) { + writer.write(c); } } @Override - public void flush() throws IOException { - // flushing automatically happens when a newline is encountered + public void write(char[] cbuf) throws IOException { + extractMessages(cbuf, 0, cbuf.length); + if (writer != null) { + writer.write(cbuf); + } } @Override public void write(char[] cbuf, int off, int len) throws IOException { - int currOff = off; - synchronized (buf) { + extractMessages(cbuf, off, len); + if (writer != null) { + writer.write(cbuf, off, len); + } + } + + @Override + public void write(String str) throws IOException { + extractMessages(str, 0, str.length()); + if (writer != null) { + writer.write(str); + } + } + + @Override + public void write(String str, int off, int len) throws IOException { + extractMessages(str, off, len); + if (writer != null) { + writer.write(str, off, len); + } + } + + @Override + public void flush() throws IOException { + if (writer != null) { + writer.flush(); + } + } + + @Override + public void close() throws IOException { + synchronized (msg) { + closed = true; + // don't log a blank message if the last character was a newline + if (msg.length() > 0) { + logMessage(); + } + } + if (writer != null) { + writer.close(); + } + } + + private void extractMessages(char[] cbuf, int off, int len) { + extractMessages(CharBuffer.wrap(cbuf), off, len); + } + + private void extractMessages(CharSequence str, int off, int len) { + synchronized (msg) { + if (closed) { + return; + } for (int pos = off; pos < off + len; pos++) { - switch (cbuf[pos]) { + switch (str.charAt(pos)) { case '\r': - buf.append(cbuf, currOff, pos - currOff); - currOff = pos + 1; + msg.append(str, off, pos - off); + len -= (pos - off); + off = pos + 1; break; case '\n': - buf.append(cbuf, currOff, pos - currOff); - currOff = pos + 1; - log(); + msg.append(str, off, pos - off); + len -= (pos - off); + off = pos + 1; + logMessage(); break; } } - buf.append(cbuf, currOff, len - (currOff - off)); + msg.append(str, off, len); } } - private void log() { - final Message message = logger.getMessageFactory().newMessage(buf.toString()); - buf.setLength(0); - logger.log(marker, FQCN, level, message, null); + private void logMessage() { + logger.log(level, marker, msg.toString()); + msg.setLength(0); } } Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java (revision 1574084) +++ log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerContext.java (working copy) @@ -53,4 +53,6 @@ */ boolean hasLogger(String name); + LoggerExtension extendLogger(String name, Class extensionClass); + } Index: log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerOutputStream.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerOutputStream.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/spi/LoggerOutputStream.java (revision 0) @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.spi; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; + +/** + * Output stream that logs each line written to a pre-defined level. Can also be configured with a + * Marker. This class provides an interface that follows the {@link java.io.PrintStream} methods in + * spirit, but doesn't output to any external stream. This class should not be used as a + * stream for an underlying logger unless it's being used as a bridge. Otherwise, infinite loops may + * occur! + */ +public class LoggerOutputStream extends OutputStream { + private static final int BUFFER_SIZE = 1024; + + private final OutputStream out; + private final AbstractLogger logger; + private final Level level; + private final Marker marker; + private final ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); + private final char[] msgBuf = new char[BUFFER_SIZE]; + private final InputStreamReader msgReader; + private final StringBuilder msg = new StringBuilder(); + private boolean closed; + + public LoggerOutputStream(OutputStream out, Charset charset, AbstractLogger logger, Level level, Marker marker) { + this.out = out; + this.logger = logger; + this.level = level; + this.marker = marker; + this.msgReader = new InputStreamReader(new ByteBufferInputStream(), charset); + } + + @Override + public void write(int b) throws IOException { + synchronized (msg) { + buf.put((byte) (b & 0xFF)); + extractMessages(); + } + if (out != null) { + out.write(b); + } + } + + @Override + public void write(byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + synchronized (msg) { + while (len > buf.remaining()) { + final int remaining = buf.remaining(); + buf.put(b, off, remaining); + len -= remaining; + off += remaining; + extractMessages(); + } + buf.put(b, off, len); + extractMessages(); + } + if (out != null) { + out.write(b, off, len); + } + } + + @Override + public void flush() throws IOException { + if (out != null) { + out.flush(); + } + } + + @Override + public void close() throws IOException { + synchronized (msg) { + closed = true; + // don't log a blank message if the last character was a newline + if (msg.length() > 0) { + logMessage(); + } + } + if (out != null) { + out.close(); + } + } + + private void extractMessages() throws IOException { + if (closed) { + return; + } + int read = msgReader.read(msgBuf); + while (read >= 0) { + int off = 0; + for (int pos = 0; pos < read; pos++) { + switch (msgBuf[pos]) { + case '\r': + msg.append(msgBuf, off, pos - off); + off = pos + 1; + break; + case '\n': + msg.append(msgBuf, off, pos - off); + off = pos + 1; + logMessage(); + break; + } + } + msg.append(msgBuf, off, read - off); + read = msgReader.read(msgBuf); + } + } + + private void logMessage() { + logger.log(level, marker, msg.toString()); + } + + private class ByteBufferInputStream extends InputStream { + + public int read() throws IOException { + if (!buf.hasRemaining()) { + return -1; + } + return buf.get() & 0xFF; + } + + public int read(byte[] bytes, int off, int len) throws IOException { + if (!buf.hasRemaining()) { + return -1; + } + + len = Math.min(len, buf.remaining()); + buf.get(bytes, off, len); + return len; + } + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java (revision 1574084) +++ log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java (working copy) @@ -27,7 +27,9 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.MessageFactory; import org.apache.logging.log4j.spi.AbstractLogger; +import org.apache.logging.log4j.spi.ExtensibleLogger; import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.spi.LoggerExtension; import org.apache.logging.log4j.util.PropertiesUtil; /** @@ -49,8 +51,8 @@ /** Include the instance name in the log message? */ private final boolean showLogName; /** - * Include the short name ( last component ) of the logger in the log message. Defaults to true - otherwise we'll be - * lost in a flood of messages without knowing who sends them. + * Include the short name ( last component ) of the logger in the log message. Defaults to true + * - otherwise we'll be lost in a flood of messages without knowing who sends them. */ private final boolean showShortName; /** Include the current time in the log message */ @@ -76,8 +78,7 @@ final String lvl = props.getStringProperty(SYSTEM_PREFIX + "level"); defaultLevel = Level.toLevel(lvl, Level.ERROR); - dateTimeFormat = showDateTime ? props.getStringProperty(SimpleLoggerContext.SYSTEM_PREFIX + "dateTimeFormat", - DEFAULT_DATE_TIME_FORMAT) : null; + dateTimeFormat = showDateTime ? props.getStringProperty(SimpleLoggerContext.SYSTEM_PREFIX + "dateTimeFormat", DEFAULT_DATE_TIME_FORMAT) : null; final String fileName = props.getStringProperty(SYSTEM_PREFIX + "logFile", "system.err"); PrintStream ps; @@ -109,8 +110,8 @@ return logger; } - loggers.putIfAbsent(name, new SimpleLogger(name, defaultLevel, showLogName, showShortName, showDateTime, - showContextMap, dateTimeFormat, messageFactory, props, stream)); + loggers.putIfAbsent(name, new SimpleLogger(name, defaultLevel, showLogName, showShortName, showDateTime, showContextMap, dateTimeFormat, + messageFactory, props, stream)); return loggers.get(name); } @@ -123,4 +124,9 @@ public Object getExternalContext() { return null; } + + @Override + public LoggerExtension extendLogger(final String name, final Class extensionClass) { + return ((ExtensibleLogger) getLogger(name)).extend(extensionClass); + } } Index: log4j-api/src/main/java/org/apache/logging/log4j/Logger.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/Logger.java (revision 1574084) +++ log4j-api/src/main/java/org/apache/logging/log4j/Logger.java (working copy) @@ -16,8 +16,6 @@ */ package org.apache.logging.log4j; -import java.io.PrintWriter; - import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MessageFactory; @@ -465,23 +463,6 @@ String getName(); /** - * Gets a print stream that logs lines to this logger. - * - * @param level the logging level - * @return print stream that logs printed lines to this logger. - */ - PrintWriter printWriter(Level level); - - /** - * Gets a marked print stream that logs lines to this logger. - * - * @param marker the marker data specific to this log statement - * @param level the logging level - * @return print stream that logs printed lines to this logger. - */ - PrintWriter printWriter(Marker marker, Level level); - - /** * Logs a message with the specific Marker at the {@link Level#INFO INFO} level. * * @param marker the marker data specific to this log statement Index: log4j-jcl/src/main/java/org/apache/logging/log4j/jcl/Log4jLog.java =================================================================== --- log4j-jcl/src/main/java/org/apache/logging/log4j/jcl/Log4jLog.java (revision 1574084) +++ log4j-jcl/src/main/java/org/apache/logging/log4j/jcl/Log4jLog.java (working copy) @@ -17,17 +17,111 @@ package org.apache.logging.log4j.jcl; import org.apache.commons.logging.Log; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.AbstractLoggerWrapper; +import org.apache.logging.log4j.spi.LoggerExtension; /** * */ -public class Log4jLog extends AbstractLoggerWrapper implements Log { +public class Log4jLog implements Log { private static final long serialVersionUID = 1L; - public Log4jLog(final AbstractLogger logger, final String name) { - super(logger, name, null); + private final LoggerExtension logger; + + public Log4jLog(final LoggerExtension logger) { + this.logger = logger; + } + + @Override + public boolean isDebugEnabled() { + return logger.isEnabled(Level.DEBUG, null, null); + } + + @Override + public boolean isErrorEnabled() { + return logger.isEnabled(Level.ERROR, null, null); + } + + @Override + public boolean isFatalEnabled() { + return logger.isEnabled(Level.FATAL, null, null); + } + + @Override + public boolean isInfoEnabled() { + return logger.isEnabled(Level.INFO, null, null); + } + + @Override + public boolean isTraceEnabled() { + return logger.isEnabled(Level.TRACE, null, null); + } + + @Override + public boolean isWarnEnabled() { + return logger.isEnabled(Level.WARN, null, null); + } + + @Override + public void trace(Object message) { + logger.log(Level.TRACE, null, message, null); + } + + @Override + public void trace(Object message, Throwable t) { + logger.log(Level.TRACE, null, message, t); + } + + @Override + public void debug(Object message) { + logger.log(Level.DEBUG, null, message, null); + } + + @Override + public void debug(Object message, Throwable t) { + logger.log(Level.DEBUG, null, message, t); + } + + @Override + public void info(Object message) { + logger.log(Level.INFO, null, message, null); + } + + @Override + public void info(Object message, Throwable t) { + logger.log(Level.INFO, null, message, t); + } + + @Override + public void warn(Object message) { + logger.log(Level.WARN, null, message, null); + } + + @Override + public void warn(Object message, Throwable t) { + logger.log(Level.WARN, null, message, t); + } + + @Override + public void error(Object message) { + logger.log(Level.ERROR, null, message, null); + } + + @Override + public void error(Object message, Throwable t) { + logger.log(Level.ERROR, null, message, t); + } + + @Override + public void fatal(Object message) { + logger.log(Level.FATAL, null, message, null); + } + + @Override + public void fatal(Object message, Throwable t) { + logger.log(Level.FATAL, null, message, t); } } Index: log4j-jcl/src/main/java/org/apache/logging/log4j/jcl/LogFactoryImpl.java =================================================================== --- log4j-jcl/src/main/java/org/apache/logging/log4j/jcl/LogFactoryImpl.java (revision 1574084) +++ log4j-jcl/src/main/java/org/apache/logging/log4j/jcl/LogFactoryImpl.java (working copy) @@ -26,15 +26,16 @@ import org.apache.commons.logging.LogFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.spi.AbstractLogger; +import org.apache.logging.log4j.spi.ExtensibleLogger; import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.spi.LoggerExtension; /** * */ public class LogFactoryImpl extends LogFactory { - private final Map> contextMap = - new WeakHashMap>(); + private final Map> contextMap = new WeakHashMap>(); private final ConcurrentMap attributes = new ConcurrentHashMap(); @@ -44,13 +45,9 @@ if (loggers.containsKey(name)) { return loggers.get(name); } - final org.apache.logging.log4j.Logger logger = PrivateManager.getLogger(name); - if (logger instanceof AbstractLogger) { - loggers.putIfAbsent(name, new Log4jLog((AbstractLogger) logger, name)); - return loggers.get(name); - } - throw new LogConfigurationException( - "Commons Logging Adapter requires base logging system to extend Log4j AbstractLogger"); + LoggerExtension logger = PrivateManager.getContext().extendLogger(name, Log4jLog.class); + loggers.putIfAbsent(name, new Log4jLog(logger)); + return loggers.get(name); } private ConcurrentMap getLoggersMap() { @@ -81,8 +78,8 @@ } /** - * This method is supposed to clear all loggers. In this implementation it will clear all the logger - * wrappers but the loggers managed by the underlying logger context will not be. + * This method is supposed to clear all loggers. In this implementation it will clear all the + * logger wrappers but the loggers managed by the underlying logger context will not be. */ @Override public void release() { Index: log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java =================================================================== --- log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java (revision 1574084) +++ log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java (working copy) @@ -113,27 +113,27 @@ } @Override - public boolean isEnabled(final Level level, final Marker marker, final String msg) { + protected boolean isEnabled(final Level level, final Marker marker, final String msg) { return config.filter(level, marker, msg); } @Override - public boolean isEnabled(final Level level, final Marker marker, final String msg, final Throwable t) { + protected boolean isEnabled(final Level level, final Marker marker, final String msg, final Throwable t) { return config.filter(level, marker, msg, t); } @Override - public boolean isEnabled(final Level level, final Marker marker, final String msg, final Object... p1) { + protected boolean isEnabled(final Level level, final Marker marker, final String msg, final Object... p1) { return config.filter(level, marker, msg, p1); } @Override - public boolean isEnabled(final Level level, final Marker marker, final Object msg, final Throwable t) { + protected boolean isEnabled(final Level level, final Marker marker, final Object msg, final Throwable t) { return config.filter(level, marker, msg, t); } @Override - public boolean isEnabled(final Level level, final Marker marker, final Message msg, final Throwable t) { + protected boolean isEnabled(final Level level, final Marker marker, final Message msg, final Throwable t) { return config.filter(level, marker, msg, t); } Index: log4j-slf4j-impl/src/main/java/org/slf4j/helpers/Log4jLoggerFactory.java =================================================================== --- log4j-slf4j-impl/src/main/java/org/slf4j/helpers/Log4jLoggerFactory.java (revision 1574084) +++ log4j-slf4j-impl/src/main/java/org/slf4j/helpers/Log4jLoggerFactory.java (working copy) @@ -24,6 +24,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.spi.LoggerExtension; import org.apache.logging.slf4j.SLF4JLoggingException; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; @@ -50,12 +51,9 @@ return loggers.get(name); } final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name; - final org.apache.logging.log4j.Logger logger = context.getLogger(key); - if (logger instanceof AbstractLogger) { - loggers.putIfAbsent(name, new SLF4JLogger((AbstractLogger) logger, name)); - return loggers.get(name); - } - throw new SLF4JLoggingException("SLF4J Adapter requires base logging system to extend Log4j AbstractLogger"); + final LoggerExtension logger = context.extendLogger(name, Log4jLoggerFactory.class); + loggers.putIfAbsent(name, new SLF4JLogger(logger, name)); + return loggers.get(name); } private ConcurrentMap getLoggersMap(final LoggerContext context) { Index: log4j-slf4j-impl/src/main/java/org/slf4j/impl/SLF4JLogger.java =================================================================== --- log4j-slf4j-impl/src/main/java/org/slf4j/impl/SLF4JLogger.java (revision 1574084) +++ log4j-slf4j-impl/src/main/java/org/slf4j/impl/SLF4JLogger.java (working copy) @@ -23,6 +23,7 @@ import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.AbstractLoggerWrapper; +import org.apache.logging.log4j.spi.LoggerExtension; import org.slf4j.Marker; import org.slf4j.MarkerFactory; import org.slf4j.helpers.EventDataConverter; @@ -39,15 +40,14 @@ public class SLF4JLogger implements LocationAwareLogger, Serializable { private static final long serialVersionUID = 7869000638091304316L; - private static final String FQCN = SLF4JLogger.class.getName(); private static final Marker EVENT_MARKER = MarkerFactory.getMarker("EVENT"); private final boolean eventLogger; - private transient AbstractLoggerWrapper logger; + private transient LoggerExtension logger; private final String name; private transient EventDataConverter converter; - public SLF4JLogger(final AbstractLogger logger, final String name) { - this.logger = new AbstractLoggerWrapper(logger, name, null); + public SLF4JLogger(final LoggerExtension logger, final String name) { + this.logger = logger; this.eventLogger = "EventLogger".equals(name); this.name = name; this.converter = createConverter(); @@ -55,88 +55,62 @@ @Override public void trace(final String format) { - if (logger.isTraceEnabled()) { - logger.log(null, FQCN, Level.TRACE, new SimpleMessage(format), null); - } + logger.log(Level.TRACE, null, format); } @Override public void trace(final String format, final Object o) { - if (logger.isTraceEnabled()) { - logger.log(null, FQCN, Level.TRACE, new ParameterizedMessage(format, o), null); - } + logger.log(Level.TRACE, null, format, o); } @Override public void trace(final String format, final Object arg1, final Object arg2) { - if (logger.isTraceEnabled()) { - final ParameterizedMessage msg = new ParameterizedMessage(format, arg1, arg2); - logger.log(null, FQCN, Level.TRACE, msg, msg.getThrowable()); - } + logger.log(Level.TRACE, null, format, arg1, arg2); } @Override public void trace(final String format, final Object... args) { - if (logger.isTraceEnabled()) { - final ParameterizedMessage msg = new ParameterizedMessage(format, args); - logger.log(null, FQCN, Level.TRACE, msg, msg.getThrowable()); - } + logger.log(Level.TRACE, null, format, args); } @Override public void trace(final String format, final Throwable t) { - if (logger.isTraceEnabled()) { - logger.log(null, FQCN, Level.TRACE, new SimpleMessage(format), t); - } + logger.log(Level.TRACE, null, format, t); } @Override public boolean isTraceEnabled() { - return logger.isTraceEnabled(); + return logger.isEnabled(Level.TRACE, null, null); } @Override public boolean isTraceEnabled(final Marker marker) { - return logger.isTraceEnabled((org.apache.logging.log4j.Marker) marker); + return logger.isEnabled(Level.TRACE, (org.apache.logging.log4j.Marker) marker, null); } @Override public void trace(final Marker marker, final String s) { - if (isTraceEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.TRACE, new SimpleMessage(s), null); - } + logger.log(Level.TRACE, (org.apache.logging.log4j.Marker) marker, s); } @Override public void trace(final Marker marker, final String s, final Object o) { - if (isTraceEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.TRACE, - new ParameterizedMessage(s, o), null); - } + logger.log(Level.TRACE, (org.apache.logging.log4j.Marker) marker, s, o); } @Override public void trace(final Marker marker, final String s, final Object o, final Object o1) { - if (isTraceEnabled(marker)) { - final ParameterizedMessage msg = new ParameterizedMessage(s, o, o1); - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.TRACE, msg, msg.getThrowable()); - } + logger.log(Level.TRACE, (org.apache.logging.log4j.Marker) marker, s, o, o1); } @Override public void trace(final Marker marker, final String s, final Object... objects) { - if (isTraceEnabled(marker)) { - final ParameterizedMessage msg = new ParameterizedMessage(s, objects); - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.TRACE, msg, msg.getThrowable()); - } + logger.log(Level.TRACE, (org.apache.logging.log4j.Marker) marker, s, objects); } @Override public void trace(final Marker marker, final String s, final Throwable throwable) { - if (isTraceEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.TRACE, - new SimpleMessage(s), throwable); - } + logger.log(Level.TRACE, (org.apache.logging.log4j.Marker) marker, s, throwable); } @Override @@ -196,8 +170,7 @@ @Override public void debug(final Marker marker, final String s, final Object o) { if (isDebugEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.DEBUG, - new ParameterizedMessage(s, o), null); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.DEBUG, new ParameterizedMessage(s, o), null); } } @@ -220,8 +193,7 @@ @Override public void debug(final Marker marker, final String s, final Throwable throwable) { if (isDebugEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.DEBUG, - new SimpleMessage(s), throwable); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.DEBUG, new SimpleMessage(s), throwable); } } @@ -282,8 +254,7 @@ @Override public void info(final Marker marker, final String s, final Object o) { if (isInfoEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.INFO, - new ParameterizedMessage(s, o), null); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.INFO, new ParameterizedMessage(s, o), null); } } @@ -306,8 +277,7 @@ @Override public void info(final Marker marker, final String s, final Throwable throwable) { if (isInfoEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.INFO, - new SimpleMessage(s), throwable); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.INFO, new SimpleMessage(s), throwable); } } @@ -368,8 +338,7 @@ @Override public void warn(final Marker marker, final String s, final Object o) { if (isWarnEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.WARN, - new ParameterizedMessage(s, o), null); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.WARN, new ParameterizedMessage(s, o), null); } } @@ -392,8 +361,7 @@ @Override public void warn(final Marker marker, final String s, final Throwable throwable) { if (isWarnEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.WARN, - new SimpleMessage(s), throwable); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.WARN, new SimpleMessage(s), throwable); } } @@ -454,8 +422,7 @@ @Override public void error(final Marker marker, final String s, final Object o) { if (isErrorEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.ERROR, - new ParameterizedMessage(s, o), null); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.ERROR, new ParameterizedMessage(s, o), null); } } @@ -478,22 +445,19 @@ @Override public void error(final Marker marker, final String s, final Throwable throwable) { if (isErrorEnabled(marker)) { - logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.ERROR, - new SimpleMessage(s), throwable); + logger.log((org.apache.logging.log4j.Marker) marker, FQCN, Level.ERROR, new SimpleMessage(s), throwable); } } - @Override - public void log(final Marker marker, final String fqcn, final int i, final String s1, final Object[] objects, - Throwable throwable) { + public void log(final Marker marker, final String fqcn, final int i, final String s1, final Object[] objects, Throwable throwable) { if (!logger.isEnabled(getLevel(i), (org.apache.logging.log4j.Marker) marker, s1)) { return; } Message msg; if (eventLogger && marker != null && marker.contains(EVENT_MARKER) && converter != null) { msg = converter.convertEvent(s1, objects, throwable); - } else if (objects == null) { + } else if (objects == null) { msg = new SimpleMessage(s1); } else { msg = new ParameterizedMessage(s1, objects, throwable); @@ -510,23 +474,21 @@ } /** - * Always treat de-serialization as a full-blown constructor, by - * validating the final state of the de-serialized object. + * Always treat de-serialization as a full-blown constructor, by validating the final state of + * the de-serialized object. */ private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException { - //always perform the default de-serialization first + // always perform the default de-serialization first aInputStream.defaultReadObject(); logger = new AbstractLoggerWrapper((AbstractLogger) LogManager.getLogger(name), name, null); converter = createConverter(); } /** - * This is the default implementation of writeObject. - * Customise if necessary. + * This is the default implementation of writeObject. Customise if necessary. */ - private void writeObject(ObjectOutputStream aOutputStream - ) throws IOException { - //perform the default serialization for all non-transient, non-static fields + private void writeObject(ObjectOutputStream aOutputStream) throws IOException { + // perform the default serialization for all non-transient, non-static fields aOutputStream.defaultWriteObject(); } @@ -541,16 +503,16 @@ private Level getLevel(final int i) { switch (i) { - case TRACE_INT : - return Level.TRACE; - case DEBUG_INT : - return Level.DEBUG; - case INFO_INT : - return Level.INFO; - case WARN_INT : - return Level.WARN; - case ERROR_INT : - return Level.ERROR; + case TRACE_INT: + return Level.TRACE; + case DEBUG_INT: + return Level.DEBUG; + case INFO_INT: + return Level.INFO; + case WARN_INT: + return Level.WARN; + case ERROR_INT: + return Level.ERROR; } return Level.ERROR; }