Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerPrintWriterTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerPrintWriterTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerPrintWriterTest.java (revision 0) @@ -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.logging.log4j.streams; + +import static org.junit.Assert.assertEquals; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.TestLogger; +import org.junit.Test; + +public class LoggerPrintWriterTest extends LoggerWriterTest { + private PrintWriter print; + + public LoggerPrintWriterTest(final Level level, final String logMessage) { + super(level, logMessage); + } + + @Override + protected Writer createWriter(StringWriter wrapped2, TestLogger logger, Level level) { + this.print = new LoggerPrintWriter(wrapped, logger, level); + return this.print; + } + + @Test + public void testPrint_boolean() throws Exception { + print.print(true); + assertMessages(); + print.println(); + assertMessages("true"); + assertEquals("true" + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_char() throws Exception { + for (char c : logMessage.toCharArray()) { + print.print(c); + assertMessages(); + } + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_int() throws Exception { + print.print(12); + assertMessages(); + print.println(); + assertMessages("12"); + assertEquals("12" + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_long() throws Exception { + print.print(12L); + assertMessages(); + print.println(); + assertMessages("12"); + assertEquals("12" + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_CharacterArray() throws Exception { + print.print(logMessage.toCharArray()); + assertMessages(); + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_String() throws Exception { + print.print(logMessage); + assertMessages(); + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_Object() throws Exception { + print.print((Object) logMessage); + assertMessages(); + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrintf() throws Exception { + print.printf("<<<%s>>>", logMessage); + assertMessages(); + print.println(); + assertMessages("<<<" + logMessage + ">>>"); + assertEquals("<<<" + logMessage + ">>>" + NEWLINE, wrapped.toString()); + } + + @Test + public void testFormat() throws Exception { + print.format("[%s]", logMessage).println(); + assertMessages('[' + logMessage + ']'); + assertEquals('[' + logMessage + ']' + NEWLINE, wrapped.toString()); + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerPrintStreamTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerPrintStreamTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerPrintStreamTest.java (revision 0) @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.streams; + +import static org.junit.Assert.assertEquals; + +import java.io.OutputStream; + +import org.apache.logging.log4j.Level; +import org.junit.Test; + +public class LoggerPrintStreamTest extends LoggerOutputStreamTest { + private LoggerPrintStream print; + + public LoggerPrintStreamTest(final Level level, final String logMessage) { + super(level, logMessage); + } + + @Override + protected OutputStream createOutputStream() { + this.print = new LoggerPrintStream(wrapped, logger, level); + return this.print; + } + + @Test + public void testPrint_boolean() throws Exception { + print.print(true); + assertMessages(); + print.println(); + assertMessages("true"); + assertEquals("true" + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_char() throws Exception { + for (char c : logMessage.toCharArray()) { + print.print(c); + assertMessages(); + } + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_int() throws Exception { + print.print(12); + assertMessages(); + print.println(); + assertMessages("12"); + assertEquals("12" + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_long() throws Exception { + print.print(12L); + assertMessages(); + print.println(); + assertMessages("12"); + assertEquals("12" + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_CharacterArray() throws Exception { + print.print(logMessage.toCharArray()); + assertMessages(); + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_String() throws Exception { + print.print(logMessage); + assertMessages(); + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrint_Object() throws Exception { + print.print((Object) logMessage); + assertMessages(); + print.println(); + assertMessages(logMessage); + assertEquals(logMessage + NEWLINE, wrapped.toString()); + } + + @Test + public void testPrintf() throws Exception { + print.printf("<<<%s>>>", logMessage); + assertMessages(); + print.println(); + assertMessages("<<<" + logMessage + ">>>"); + assertEquals("<<<" + logMessage + ">>>" + NEWLINE, wrapped.toString()); + } + + @Test + public void testFormat() throws Exception { + print.format("[%s]", logMessage).println(); + assertMessages('[' + logMessage + ']'); + assertEquals('[' + logMessage + ']' + NEWLINE, wrapped.toString()); + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerInputStreamTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerInputStreamTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerInputStreamTest.java (revision 0) @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import static org.junit.Assert.assertEquals; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.logging.log4j.Level; +import org.junit.Test; + +public class LoggerInputStreamTest extends StreamTesting { + protected ByteArrayInputStream wrapped; + protected ByteArrayOutputStream read; + protected InputStream in; + + public LoggerInputStreamTest(final Level level, final String logMessage) { + super(level, logMessage); + wrapped = new ByteArrayInputStream((logMessage + "\r\n" + logMessage).getBytes()); + read = new ByteArrayOutputStream(); + in = createInputStream(); + } + + protected InputStream createInputStream() { + return new LoggerInputStream(wrapped, logger, level); + } + + @Test + public void testRead_int() throws Exception { + for (int i = 0; i < logMessage.length(); i++) { + read.write(in.read()); + } + if (!(in instanceof BufferedInputStream)) { + assertMessages(); + } + assertEquals("carriage return", '\r', in.read()); + if (!(in instanceof BufferedInputStream)) { + assertMessages(); + } + assertEquals("newline", '\n', in.read()); + assertMessages(logMessage); + } + + @Test + public void testRead_ByteArray() throws Exception { + final byte[] bytes = new byte[logMessage.length()]; + assertEquals("len", bytes.length, in.read(bytes)); + if (!(in instanceof BufferedInputStream)) { + assertMessages(); + } + in.read(bytes); + assertMessages(logMessage); + } + + @Test + public void testRead_ByteArray_Offset_Length() throws Exception { + final byte[] bytes = new byte[logMessage.length() * 2]; + assertEquals("len", logMessage.length(), in.read(bytes, 0, logMessage.length())); + if (!(in instanceof BufferedInputStream)) { + assertMessages(); + } + in.read(bytes); + assertMessages(logMessage); + } + + @Test + public void testRead_IgnoresWindowsNewline() throws IOException { + final byte[] bytes = new byte[1024]; + int len = in.read(bytes); + read.write(bytes, 0, len); + assertMessages(logMessage); + assertEquals(logMessage + "\r\n" + logMessage, read.toString()); + in.close(); + assertMessages(logMessage, logMessage); + } + + @Test + public void testRead_MultipleLines() throws IOException { + wrapped = new ByteArrayInputStream((logMessage + "\n" + logMessage + '\n').getBytes()); + in = new LoggerInputStream(wrapped, logger, level); + + final byte[] bytes = new byte[1024]; + int len = in.read(bytes); + read.write(bytes, 0, len); + assertMessages(logMessage, logMessage); + assertEquals(logMessage + '\n' + logMessage + '\n', read.toString()); + } + + @Test + public void testClose_NoRemainingData() throws IOException { + wrapped = new ByteArrayInputStream((logMessage + '\n').getBytes()); + in = new LoggerInputStream(wrapped, logger, level); + + final byte[] bytes = new byte[1024]; + in.read(bytes); + assertMessages(logMessage); + in.close(); + assertMessages(logMessage); + } + + @Test + public void testClose_HasRemainingData() throws IOException { + final byte[] bytes = new byte[1024]; + in.read(bytes); + assertMessages(logMessage); + in.close(); + assertMessages(logMessage, logMessage); + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerBufferedReaderTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerBufferedReaderTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerBufferedReaderTest.java (revision 0) @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import static org.junit.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.Reader; + +import org.apache.logging.log4j.Level; +import org.junit.Test; + +public class LoggerBufferedReaderTest extends LoggerReaderTest { + private BufferedReader reader; + + public LoggerBufferedReaderTest(final Level level, final String logMessage) { + super(level, logMessage); + } + + @Override + protected Reader createReader() { + this.reader = new LoggerBufferedReader(wrapped, logger, level); + return this.reader; + } + + @Test + public void testReadLine() throws Exception { + assertEquals("first line", logMessage, reader.readLine()); + assertMessages(logMessage); + assertEquals("second line", logMessage, reader.readLine()); + assertMessages(logMessage, logMessage); + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerBufferedInputStreamTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerBufferedInputStreamTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerBufferedInputStreamTest.java (revision 0) @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import java.io.InputStream; + +import org.apache.logging.log4j.Level; + +public class LoggerBufferedInputStreamTest extends LoggerInputStreamTest { + + public LoggerBufferedInputStreamTest(final Level level, final String logMessage) { + super(level, logMessage); + } + + @Override + protected InputStream createInputStream() { + return new LoggerBufferedInputStream(wrapped, logger, level); + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerOutputStreamTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerOutputStreamTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerOutputStreamTest.java (revision 0) @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.logging.log4j.Level; +import org.easymock.EasyMock; +import org.junit.Test; + +public class LoggerOutputStreamTest extends StreamTesting { + protected ByteArrayOutputStream wrapped; + protected OutputStream out; + + public LoggerOutputStreamTest(final Level level, final String logMessage) { + super(level, logMessage); + wrapped = new ByteArrayOutputStream(); + out = createOutputStream(); + } + + protected OutputStream createOutputStream() { + return new LoggerOutputStream(wrapped, logger, level); + } + + @Test + public void testWrite_Int() throws Exception { + for (byte b : logMessage.getBytes()) { + out.write(b); + assertMessages(); + } + out.write('\n'); + assertMessages(logMessage); + assertEquals(logMessage + '\n', wrapped.toString()); + } + + @Test + public void testWrite_ByteArray() throws Exception { + final byte[] bytes = logMessage.getBytes(); + out.write(bytes); + assertMessages(); + out.write('\n'); + assertMessages(logMessage); + assertEquals(logMessage + '\n', wrapped.toString()); + } + + @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); + out.write(bytes, middle, length); + assertMessages(); + out.write('\n'); + assertMessages(right); + assertEquals(logMessage.substring(middle, bytes.length) + '\n', wrapped.toString()); + } + + @Test + public void testWrite_IgnoresWindowsNewline() throws IOException { + out.write(logMessage.getBytes()); + out.write("\r\n".getBytes()); + out.write(logMessage.getBytes()); + out.close(); + assertMessages(logMessage, logMessage); + assertEquals(logMessage + "\r\n" + logMessage, wrapped.toString()); + } + + @Test + public void testWrite_MultipleLines() throws IOException { + out.write((logMessage + '\n' + logMessage + '\n').getBytes()); + assertMessages(logMessage, logMessage); + assertEquals(logMessage + '\n' + logMessage + '\n', wrapped.toString()); + } + + @Test + public void testFlush() throws IOException { + final OutputStream out = EasyMock.createMock("out", OutputStream.class); + out.flush(); // expect the flush to come through to the mocked OutputStream + out.close(); + replay(out); + + final LoggerOutputStream los = new LoggerOutputStream(out, logger, level); + los.flush(); + los.close(); + verify(out); + } + + @Test + public void testClose_NoRemainingData() throws IOException { + out.close(); + assertMessages(); + assertEquals("", wrapped.toString()); + } + + @Test + public void testClose_HasRemainingData() throws IOException { + out.write(logMessage.getBytes()); + assertMessages(); + out.close(); + assertMessages(logMessage); + assertEquals(logMessage, wrapped.toString()); + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/StreamTesting.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/StreamTesting.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/StreamTesting.java (revision 0) @@ -0,0 +1,56 @@ +package org.apache.logging.log4j.streams; + +import static org.hamcrest.core.StringStartsWith.startsWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.TestLogger; +import org.junit.Ignore; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +// this is a base class for stream tests and shouldn't run on its own +@Ignore +@RunWith(Parameterized.class) +public class StreamTesting { + protected final static String NEWLINE = System.getProperty("line.separator"); + + protected Level level; + protected String logMessage; + protected TestLogger logger; + + @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 StreamTesting(final Level level, final String logMessage) { + this.logger = (TestLogger) LogManager.getLogger(); + this.logger.getEntries().clear(); + this.level = level; + this.logMessage = logMessage; + } + + protected void assertMessages(final String... messages) { + List results = logger.getEntries(); + assertEquals("Unexpected number of results.", messages.length, results.size()); + for (int i = 0; i < messages.length; i++) { + final String start = ' ' + level.name() + ' ' + messages[i]; + assertThat(results.get(i), startsWith(start)); + } + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerWriterTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerWriterTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerWriterTest.java (revision 0) @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.streams; + +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.TestLogger; +import org.easymock.EasyMock; +import org.junit.Test; + +public class LoggerWriterTest extends StreamTesting { + protected StringWriter wrapped; + protected Writer writer; + + public LoggerWriterTest(final Level level, final String logMessage) { + super(level, logMessage); + wrapped = new StringWriter(); + writer = createWriter(wrapped, logger, level); + } + + protected Writer createWriter(StringWriter wrapped2, TestLogger logger, Level level) { + return new LoggerWriter(wrapped, logger, level); + } + + @Test + public void testWrite_CharArray() throws Exception { + final char[] chars = logMessage.toCharArray(); + writer.write(chars); + assertMessages(); + writer.write('\n'); + assertMessages(logMessage); + assertEquals(logMessage + '\n', wrapped.toString()); + } + + @Test + public void testWrite_CharArray_Offset_Length() throws Exception { + final char[] chars = logMessage.toCharArray(); + int middle = chars.length / 2; + int length = chars.length - middle; + final String right = new String(chars, middle, length); + writer.write(chars, middle, length); + assertMessages(); + writer.write('\n'); + assertMessages(right); + assertEquals(logMessage.substring(middle, logMessage.length()) + '\n', wrapped.toString()); + } + + @Test + public void testWrite_Character() throws Exception { + for (char c : logMessage.toCharArray()) { + writer.write(c); + assertMessages(); + } + writer.write('\n'); + assertMessages(logMessage); + assertEquals(logMessage + '\n', wrapped.toString()); + } + + @Test + public void testWrite_IgnoresWindowsNewline() throws IOException { + writer.write(logMessage + "\r\n"); + writer.write(logMessage); + writer.close(); + assertMessages(logMessage, logMessage); + assertEquals(logMessage + "\r\n" + logMessage, wrapped.toString()); + } + + @Test + public void testWrite_MultipleLines() throws IOException { + writer.write(logMessage + '\n' + logMessage + '\n'); + assertMessages(logMessage, logMessage); + assertEquals(logMessage + '\n' + logMessage + '\n', wrapped.toString()); + } + + @Test + public void testFlush() throws IOException { + final OutputStream out = EasyMock.createMock(OutputStream.class); + out.flush(); // expect the flush to come through to the mocked OutputStream + out.close(); + replay(out); + + final LoggerOutputStream los = new LoggerOutputStream(out, logger, level); + los.flush(); + los.close(); + verify(out); + } + + @Test + public void testClose_NoRemainingData() throws IOException { + writer.close(); + assertMessages(); + assertEquals("", wrapped.toString()); + } + + @Test + public void testClose_HasRemainingData() throws IOException { + writer.write(logMessage); + assertMessages(); + writer.close(); + assertMessages(logMessage); + assertEquals(logMessage, wrapped.toString()); + } +} Index: log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerReaderTest.java =================================================================== --- log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerReaderTest.java (revision 0) +++ log4j-api/src/test/java/org/apache/logging/log4j/streams/LoggerReaderTest.java (revision 0) @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import static org.junit.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.CharBuffer; + +import org.apache.logging.log4j.Level; +import org.junit.Test; + +public class LoggerReaderTest extends StreamTesting { + protected StringReader wrapped; + protected StringWriter read; + protected Reader reader; + + public LoggerReaderTest(final Level level, final String logMessage) { + super(level, logMessage); + wrapped = new StringReader(logMessage + "\r\n" + logMessage); + read = new StringWriter(); + reader = createReader(); + } + + protected Reader createReader() { + return new LoggerReader(wrapped, logger, level); + } + + @Test + public void testRead_int() throws Exception { + for (int i = 0; i < logMessage.length(); i++) { + read.write(reader.read()); + } + if (!(reader instanceof BufferedReader)) { + assertMessages(); + } + assertEquals("carriage return", '\r', reader.read()); + if (!(reader instanceof BufferedReader)) { + assertMessages(); + } + assertEquals("newline", '\n', reader.read()); + assertMessages(logMessage); + } + + @Test + public void testRead_CharArray() throws Exception { + final char[] chars = new char[logMessage.length()]; + assertEquals("len", logMessage.length(), reader.read(chars)); + if (!(reader instanceof BufferedReader)) { + assertMessages(); + } + reader.read(chars); + assertMessages(logMessage); + } + + @Test + public void testRead_CharArray_Offset_Length() throws Exception { + final char[] chars = new char[1024]; + assertEquals("len", logMessage.length(), reader.read(chars, 0, logMessage.length())); + if (!(reader instanceof BufferedReader)) { + assertMessages(); + } + reader.read(chars); + reader.close(); + assertMessages(logMessage, logMessage); + } + + @Test + public void testRead_CharBuffer() throws Exception { + final CharBuffer chars = CharBuffer.allocate(1024); + assertEquals("len", logMessage.length() * 2 + 2, reader.read(chars)); + reader.close(); + assertMessages(logMessage, logMessage); + } + + @Test + public void testRead_IgnoresWindowsNewline() throws IOException { + final char[] chars = new char[1024]; + int len = reader.read(chars); + read.write(chars, 0, len); + if (!(reader instanceof BufferedReader)) { + assertMessages(logMessage); + } + assertEquals(logMessage + "\r\n" + logMessage, read.toString()); + reader.close(); + assertMessages(logMessage, logMessage); + } + + @Test + public void testRead_MultipleLines() throws IOException { + wrapped = new StringReader(logMessage + "\n" + logMessage + '\n'); + reader = createReader(); + + final char[] chars = new char[1024]; + int len = reader.read(chars); + read.write(chars, 0, len); + assertMessages(logMessage, logMessage); + assertEquals(logMessage + '\n' + logMessage + '\n', read.toString()); + } + + @Test + public void testClose_NoRemainingData() throws IOException { + wrapped = new StringReader(logMessage + '\n'); + reader = createReader(); + + final char[] chars = new char[1024]; + reader.read(chars); + assertMessages(logMessage); + reader.close(); + assertMessages(logMessage); + } + + @Test + public void testClose_HasRemainingData() throws IOException { + final char[] chars = new char[1024]; + reader.read(chars); + if (!(reader instanceof BufferedReader)) { + assertMessages(logMessage); + } + reader.close(); + assertMessages(logMessage, logMessage); + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/CharStreamLogger.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/CharStreamLogger.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/CharStreamLogger.java (revision 0) @@ -0,0 +1,93 @@ +package org.apache.logging.log4j.streams; + +import java.nio.CharBuffer; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +public class CharStreamLogger { + private final LoggerProvider logger; + private final Level level; + private final Marker marker; + private final StringBuilder msg = new StringBuilder(); + private boolean closed = false; + + public CharStreamLogger(final LoggerProvider logger, final Level level, final Marker marker) { + this.logger = logger; + this.level = level; + this.marker = marker; + } + + public void put(final String fqcn, final int c) { + if (c >= 0) { + synchronized (msg) { + if (closed) { + return; + } + switch (c) { + case '\n': + log(fqcn); + break; + case '\r': + break; + default: + msg.append((char) c); + } + } + } else { + logEnd(fqcn); + } + } + + public void put(final String fqcn, final char[] cbuf, final int off, final int len) { + put(fqcn, CharBuffer.wrap(cbuf), off, len); + } + + public void put(final String fqcn, final CharSequence str, final int off, final int len) { + if (len >= 0) { + synchronized (msg) { + if (closed) { + return; + } + int start = off; + final int end = off + len; + for (int pos = off; pos < end; pos++) { + char c = str.charAt(pos); + switch (c) { + case '\r': + case '\n': + msg.append(str, start, pos); + start = pos + 1; + if (c == '\n') { + log(fqcn); + } + break; + } + } + msg.append(str, start, end); + } + } else { + logEnd(fqcn); + } + } + + public void close(final String fqcn) { + synchronized (msg) { + closed = true; + logEnd(fqcn); + } + } + + private void logEnd(final String fqcn) { + if (msg.length() > 0) { + log(fqcn); + } + } + + private void log(final String fqcn) { + logger.log(fqcn, level, marker, msg.toString()); // convert to string now so async loggers + // work + msg.setLength(0); + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerPrintWriter.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerPrintWriter.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerPrintWriter.java (revision 0) @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import java.io.PrintWriter; +import java.io.Writer; +import java.util.Locale; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +/** + * Print Writer 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.PrintWriter} methods in + * spirit, but doesn't require output to any external writer. + */ +public class LoggerPrintWriter extends PrintWriter { + private static final String FQCN = LoggerPrintWriter.class.getName(); + + public LoggerPrintWriter(final Logger logger, final Level level) { + this(null, false, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintWriter(final Logger logger, final Level level, final Marker marker) { + this(null, false, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintWriter(final Writer writer, final Logger logger, final Level level) { + this(writer, false, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintWriter(final Writer writer, final Logger logger, final Level level, final Marker marker) { + this(writer, false, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintWriter(final Writer writer, final boolean autoFlush, final Logger logger, final Level level) { + this(writer, autoFlush, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintWriter(final Writer writer, final boolean autoFlush, final Logger logger, final Level level, final Marker marker) { + this(writer, autoFlush, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintWriter(final Writer writer, boolean autoFlush, final LoggerProvider logger, final String fqcn, + final Level level, final Marker marker) { + super(new LoggerWriter(writer, logger, fqcn, level, marker), autoFlush); + } + + @Override + public void write(int c) { + super.write(c); + } + + @Override + public void write(char[] buf) { + super.write(buf); + } + + @Override + public void write(char[] buf, int off, int len) { + super.write(buf, off, len); + } + + @Override + public void write(String s) { + super.write(s); + } + + @Override + public void write(String s, int off, int len) { + super.write(s, off, len); + } + + @Override + public void flush() { + super.flush(); + } + + @Override + public void close() { + super.close(); + } + + @Override + public void print(boolean b) { + super.print(b); + } + + @Override + public void print(char c) { + super.print(c); + } + + @Override + public void print(int i) { + super.print(i); + } + + @Override + public void print(long l) { + super.print(l); + } + + @Override + public void print(float f) { + super.print(f); + } + + @Override + public void print(double d) { + super.print(d); + } + + @Override + public void print(char[] s) { + super.print(s); + } + + @Override + public void print(String s) { + super.print(s); + } + + @Override + public void print(Object obj) { + super.print(obj); + } + + @Override + public void println() { + super.println(); + } + + @Override + public void println(boolean x) { + super.println(x); + } + + @Override + public void println(char x) { + super.println(x); + } + + @Override + public void println(int x) { + super.println(x); + } + + @Override + public void println(long x) { + super.println(x); + } + + @Override + public void println(float x) { + super.println(x); + } + + @Override + public void println(double x) { + super.println(x); + } + + @Override + public void println(char[] x) { + super.println(x); + } + + @Override + public void println(String x) { + super.println(x); + } + + @Override + public void println(Object x) { + super.println(x); + } + + @Override + public LoggerPrintWriter printf(String format, Object... args) { + super.printf(format, args); + return this; + } + + @Override + public LoggerPrintWriter printf(Locale l, String format, Object... args) { + super.printf(l, format, args); + return this; + } + + @Override + public LoggerPrintWriter append(char c) { + super.append(c); + return this; + } + + @Override + public LoggerPrintWriter append(CharSequence csq) { + super.append(csq); + return this; + } + + @Override + public LoggerPrintWriter append(CharSequence csq, int start, int end) { + super.append(csq, start, end); + return this; + } + + @Override + public LoggerPrintWriter format(String format, Object... args) { + super.format(format, args); + return this; + } + + @Override + public LoggerPrintWriter format(Locale l, String format, Object... args) { + super.format(l, format, args); + return this; + } + + @Override + public boolean checkError() { + return super.checkError(); + } + + @Override + public String toString() { + return LoggerPrintWriter.class.getSimpleName() + "{stream=" + out + '}'; + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerPrintStream.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerPrintStream.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerPrintStream.java (revision 0) @@ -0,0 +1,272 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.streams; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.Locale; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +/** + * 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 require 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 LoggerPrintStream extends PrintStream { + private static final String FQCN = LoggerPrintStream.class.getName(); + + public LoggerPrintStream(final Logger logger, final Level level) { + this(null, false, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintStream(final Charset charset, final Logger logger, final Level level) throws UnsupportedEncodingException { + this(null, false, charset, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintStream(final Logger logger, final Level level, final Marker marker) { + this(null, false, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintStream(final Charset charset, final Logger logger, final Level level, final Marker marker) throws UnsupportedEncodingException { + this(null, false, charset, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintStream(final OutputStream out, final Logger logger, final Level level) { + this(out, false, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintStream(final OutputStream out, final Charset charset, final Logger logger, final Level level) throws UnsupportedEncodingException { + this(out, false, charset, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintStream(final OutputStream out, final Logger logger, final Level level, final Marker marker) { + this(out, false, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintStream(final OutputStream out, final Charset charset, final Logger logger, final Level level, final Marker marker) + throws UnsupportedEncodingException { + this(out, false, charset, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintStream(final OutputStream out, final boolean autoFlush, final Logger logger, final Level level) { + this(out, autoFlush, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintStream(final OutputStream out, final boolean autoFlush, final Charset charset, final Logger logger, final Level level) + throws UnsupportedEncodingException { + this(out, autoFlush, charset, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerPrintStream(final OutputStream out, final boolean autoFlush, final Logger logger, final Level level, final Marker marker) { + this(out, autoFlush, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintStream(final OutputStream out, final boolean autoFlush, final Charset charset, final Logger logger, final Level level, final Marker marker) + throws UnsupportedEncodingException { + this(out, autoFlush, charset, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerPrintStream(final OutputStream out, boolean autoFlush, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + super(new LoggerOutputStream(out, Charset.defaultCharset(), logger, fqcn, level, marker), autoFlush); + } + + public LoggerPrintStream(final OutputStream out, boolean autoFlush, final Charset charset, final LoggerProvider logger, final String fqcn, + final Level level, final Marker marker) throws UnsupportedEncodingException { + super(new LoggerOutputStream(out, charset, logger, fqcn, level, marker), autoFlush, charset.name()); + } + + @Override + public void write(int b) { + super.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + super.write(b); + } + + @Override + public void write(byte[] b, int off, int len) { + super.write(b, off, len); + } + + @Override + public void flush() { + super.flush(); + } + + @Override + public void close() { + super.close(); + } + + @Override + public void print(boolean b) { + super.print(b); + } + + @Override + public void print(char c) { + super.print(c); + } + + @Override + public void print(int i) { + super.print(i); + } + + @Override + public void print(long l) { + super.print(l); + } + + @Override + public void print(float f) { + super.print(f); + } + + @Override + public void print(double d) { + super.print(d); + } + + @Override + public void print(char[] s) { + super.print(s); + } + + @Override + public void print(String s) { + super.print(s); + } + + @Override + public void print(Object obj) { + super.print(obj); + } + + @Override + public void println() { + super.println(); + } + + @Override + public void println(boolean x) { + super.println(x); + } + + @Override + public void println(char x) { + super.println(x); + } + + @Override + public void println(int x) { + super.println(x); + } + + @Override + public void println(long x) { + super.println(x); + } + + @Override + public void println(float x) { + super.println(x); + } + + @Override + public void println(double x) { + super.println(x); + } + + @Override + public void println(char[] x) { + super.println(x); + } + + @Override + public void println(String x) { + super.println(x); + } + + @Override + public void println(Object x) { + super.println(x); + } + + @Override + public LoggerPrintStream printf(String format, Object... args) { + super.printf(format, args); + return this; + } + + @Override + public LoggerPrintStream printf(Locale l, String format, Object... args) { + super.printf(l, format, args); + return this; + } + + @Override + public LoggerPrintStream append(char c) { + super.append(c); + return this; + } + + @Override + public LoggerPrintStream append(CharSequence csq) { + super.append(csq); + return this; + } + + @Override + public LoggerPrintStream append(CharSequence csq, int start, int end) { + super.append(csq, start, end); + return this; + } + + @Override + public LoggerPrintStream format(String format, Object... args) { + super.format(format, args); + return this; + } + + @Override + public LoggerPrintStream format(Locale l, String format, Object... args) { + super.format(l, format, args); + return this; + } + + @Override + public boolean checkError() { + return super.checkError(); + } + + @Override + public String toString() { + return LoggerPrintStream.class.getSimpleName() + "{stream=" + out + '}'; + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerBufferedReader.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerBufferedReader.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerBufferedReader.java (revision 0) @@ -0,0 +1,69 @@ +package org.apache.logging.log4j.streams; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.CharBuffer; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +public class LoggerBufferedReader extends BufferedReader { + private final static String FQCN = LoggerBufferedReader.class.getName(); + + public LoggerBufferedReader(final Reader reader, final Logger logger, final Level level) { + this(reader, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerBufferedReader(final Reader reader, final Logger logger, final Level level, final Marker marker) { + this(reader, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerBufferedReader(final Reader reader, final int sz, final Logger logger, final Level level) { + this(reader, sz, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerBufferedReader(final Reader reader, final int sz, final Logger logger, final Level level, final Marker marker) { + this(reader, sz, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerBufferedReader(final Reader reader, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + super(new LoggerReader(reader, logger, FQCN, level, marker)); + } + + public LoggerBufferedReader(final Reader reader, final int sz, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + super(new LoggerReader(reader, logger, FQCN, level, marker), sz); + } + + @Override + public void close() throws IOException { + super.close(); + } + + @Override + public int read() throws IOException { + return super.read(); + } + + @Override + public int read(char[] cbuf) throws IOException { + return super.read(cbuf); + } + + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + return super.read(cbuf, off, len); + } + + @Override + public int read(CharBuffer target) throws IOException { + return super.read(target); + } + + @Override + public String readLine() throws IOException { + return super.readLine(); + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/ByteStreamLogger.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/ByteStreamLogger.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/ByteStreamLogger.java (revision 0) @@ -0,0 +1,130 @@ +package org.apache.logging.log4j.streams; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +public class ByteStreamLogger { + private static final int BUFFER_SIZE = 1024; + + private final LoggerProvider logger; + private final Level level; + private final Marker marker; + private final ByteBufferInputStream in; + private final InputStreamReader reader; + private final char[] msgBuf = new char[BUFFER_SIZE]; + private final StringBuilder msg = new StringBuilder(); + private boolean closed; + final ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); + + public ByteStreamLogger(final LoggerProvider logger, final Level level, final Marker marker, final Charset charset) { + this.logger = logger; + this.level = level; + this.marker = marker; + this.in = new ByteBufferInputStream(); + this.reader = new InputStreamReader(in, charset); + } + + public void put(final String fqcn, final int b) throws IOException { + if (b >= 0) { + synchronized (msg) { + buf.put((byte) (b & 0xFF)); + extractMessages(fqcn); + } + } else { + logEnd(fqcn); + } + } + + public void put(final String fqcn, final byte[] b, int off, int len) throws IOException { + if (len >= 0) { + synchronized (msg) { + while (len > buf.remaining()) { + final int remaining = buf.remaining(); + buf.put(b, off, remaining); + len -= remaining; + off += remaining; + extractMessages(fqcn); + } + buf.put(b, off, len); + extractMessages(fqcn); + } + } else { + logEnd(fqcn); + } + } + + public void close(String fqcn) { + synchronized (msg) { + closed = true; + logEnd(fqcn); + } + } + + private void extractMessages(final String fqcn) throws IOException { + if (closed) { + return; + } + int read = reader.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; + log(fqcn); + break; + } + } + msg.append(msgBuf, off, read - off); + read = reader.read(msgBuf); + } + } + + private void logEnd(final String fqcn) { + if (msg.length() > 0) { + log(fqcn); + } + } + + private void log(final String fqcn) { + // convert to string now so async loggers work + logger.log(fqcn, level, marker, msg.toString()); + msg.setLength(0); + } + + private class ByteBufferInputStream extends InputStream { + + public int read() throws IOException { + buf.flip(); + int result = -1; + if (buf.limit() > 0) { + result = buf.get() & 0xFF; + } + buf.compact(); + return result; + } + + public int read(byte[] bytes, int off, int len) throws IOException { + buf.flip(); + int result = -1; + if (buf.limit() > 0) { + result = Math.min(len, buf.limit()); + buf.get(bytes, off, result); + } + buf.compact(); + return result; + } + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerOutputStream.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerOutputStream.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerOutputStream.java (revision 0) @@ -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.logging.log4j.streams; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +/** + * 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.OutputStream} methods in + * spirit, but doesn't require 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 String FQCN = LoggerOutputStream.class.getName(); + + private final OutputStream out; + private final ByteStreamLogger logger; + private final String fqcn; + + public LoggerOutputStream(final Logger logger, final Level level) { + this(null, Charset.defaultCharset(), (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerOutputStream(final Charset charset, final Logger logger, final Level level) { + this(null, charset, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerOutputStream(final Logger logger, final Level level, final Marker marker) { + this(null, Charset.defaultCharset(), (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerOutputStream(final Charset charset, final Logger logger, final Level level, final Marker marker) { + this(null, charset, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerOutputStream(final OutputStream out, final Logger logger, final Level level) { + this(out, Charset.defaultCharset(), (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerOutputStream(final OutputStream out, final Charset charset, final Logger logger, final Level level) { + this(out, charset, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerOutputStream(final OutputStream out, final Logger logger, final Level level, final Marker marker) { + this(out, Charset.defaultCharset(), (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerOutputStream(final OutputStream out, final Charset charset, final Logger logger, final Level level, final Marker marker) { + this(out, charset, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerOutputStream(final OutputStream out, final Charset charset, final LoggerProvider logger, final String fqcn, final Level level, + final Marker marker) { + this.out = out; + this.logger = new ByteStreamLogger(logger, level, marker, charset); + this.fqcn = fqcn; + } + + @Override + public void write(final int b) throws IOException { + if (out != null) { + out.write(b); + } + logger.put(fqcn, (byte) (b & 0xFF)); + } + + @Override + public void write(byte[] b) throws IOException { + if (out != null) { + out.write(b); + } + logger.put(fqcn, b, 0, b.length); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (out != null) { + out.write(b, off, len); + } + logger.put(fqcn, b, off, len); + } + + @Override + public void flush() throws IOException { + if (out != null) { + out.flush(); + } + } + + @Override + public void close() throws IOException { + out.close(); + logger.close(fqcn); + } + + @Override + public String toString() { + return LoggerOutputStream.class.getSimpleName() + "{stream=" + out + '}'; + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerWriter.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerWriter.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerWriter.java (revision 0) @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +/** + * Writer 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.Writer} methods in spirit, but + * doesn't require output to any external writer. + */ +public class LoggerWriter extends Writer { + private final static String FQCN = LoggerWriter.class.getName(); + + private final Writer writer; + private final CharStreamLogger logger; + private final String fqcn; + + public LoggerWriter(final Logger logger, final Level level) { + this(null, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerWriter(final Logger logger, final Level level, final Marker marker) { + this(null, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerWriter(final Writer writer, final Logger logger, final Level level) { + this(writer, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerWriter(final Writer writer, final Logger logger, final Level level, final Marker marker) { + this(writer, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerWriter(final Writer writer, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + this.writer = writer; + this.logger = new CharStreamLogger(logger, level, marker); + this.fqcn = fqcn; + } + + @Override + public void write(final int c) throws IOException { + if (writer != null) { + writer.write(c); + } + logger.put(fqcn, (char) c); + } + + @Override + public void write(final char[] cbuf) throws IOException { + if (writer != null) { + writer.write(cbuf); + } + logger.put(fqcn, cbuf, 0, cbuf.length); + } + + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + if (writer != null) { + writer.write(cbuf, off, len); + } + logger.put(fqcn, cbuf, off, len); + } + + @Override + public void write(final String str) throws IOException { + if (writer != null) { + writer.write(str); + } + logger.put(fqcn, str, 0, str.length()); + } + + @Override + public void write(final String str, final int off, final int len) throws IOException { + if (writer != null) { + writer.write(str, off, len); + } + logger.put(fqcn, str, off, len); + } + + @Override + public void flush() throws IOException { + if (writer != null) { + writer.flush(); + } + } + + @Override + public void close() throws IOException { + if (writer != null) { + writer.close(); + } + logger.close(fqcn); + } + + @Override + public String toString() { + return LoggerWriter.class.getSimpleName() + "{stream=" + writer + '}'; + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerReader.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerReader.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerReader.java (revision 0) @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.streams; + +import java.io.FilterReader; +import java.io.IOException; +import java.io.Reader; +import java.nio.CharBuffer; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +/** + * Writer 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.Writer} methods in spirit, but + * doesn't require output to any external writer. + */ +public class LoggerReader extends FilterReader { + private final static String FQCN = LoggerReader.class.getName(); + + private final CharStreamLogger logger; + private final String fqcn; + + public LoggerReader(final Reader reader, final Logger logger, final Level level) { + this(reader, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerReader(final Reader reader, final Logger logger, final Level level, final Marker marker) { + this(reader, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerReader(final Reader reader, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + super(reader); + this.logger = new CharStreamLogger(logger, level, marker); + this.fqcn = fqcn; + } + + @Override + public int read() throws IOException { + final int c = super.read(); + logger.put(fqcn, c); + return c; + } + + @Override + public int read(final char[] cbuf) throws IOException { + return read(cbuf, 0, cbuf.length); + } + + @Override + public int read(final char[] cbuf, final int off, final int len) throws IOException { + final int charsRead = super.read(cbuf, off, len); + logger.put(fqcn, cbuf, off, charsRead); + return charsRead; + } + + @Override + public int read(CharBuffer target) throws IOException { + final int charsRead = super.read(target); + if (charsRead > 0) { + logger.put(fqcn, target, 0, charsRead); + } + return charsRead; + } + + @Override + public void close() throws IOException { + super.close(); + logger.close(fqcn); + } + + @Override + public String toString() { + return LoggerReader.class.getSimpleName() + "{stream=" + in + '}'; + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerBufferedInputStream.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerBufferedInputStream.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerBufferedInputStream.java (revision 0) @@ -0,0 +1,88 @@ +package org.apache.logging.log4j.streams; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +public class LoggerBufferedInputStream extends BufferedInputStream { + private final static String FQCN = LoggerBufferedInputStream.class.getName(); + + public LoggerBufferedInputStream(final InputStream in, final Logger logger, final Level level) { + this(in, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerBufferedInputStream(final InputStream in, final Charset charset, final Logger logger, final Level level) { + this(in, charset, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerBufferedInputStream(final InputStream in, final Logger logger, final Level level, final Marker marker) { + this(in, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerBufferedInputStream(final InputStream in, final Charset charset, final Logger logger, final Level level, final Marker marker) { + this(in, charset, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerBufferedInputStream(final InputStream in, final int sz, final Logger logger, final Level level) { + this(in, sz, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerBufferedInputStream(final InputStream in, final Charset charset, final int sz, final Logger logger, final Level level) { + this(in, charset, sz, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerBufferedInputStream(final InputStream in, final int sz, final Logger logger, final Level level, final Marker marker) { + this(in, sz, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerBufferedInputStream(final InputStream in, final Charset charset, final int sz, final Logger logger, final Level level, final Marker marker) { + this(in, charset, sz, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerBufferedInputStream(final InputStream in, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + this(in, Charset.defaultCharset(), logger, fqcn, level, marker); + } + + public LoggerBufferedInputStream(final InputStream in, final Charset charset, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + super(new LoggerInputStream(in, charset, logger, fqcn, level, marker)); + } + + public LoggerBufferedInputStream(final InputStream in, final int sz, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + this(in, Charset.defaultCharset(), sz, logger, fqcn, level, marker); + } + + public LoggerBufferedInputStream(final InputStream in, final Charset charset, final int sz, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + super(new LoggerInputStream(in, charset, logger, fqcn, level, marker), sz); + } + + @Override + public void close() throws IOException { + super.close(); + } + + @Override + public synchronized int read() throws IOException { + return super.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return super.read(b); + } + + @Override + public synchronized int read(byte[] b, int off, int len) throws IOException { + return super.read(b, off, len); + } + + @Override + public String toString() { + return LoggerBufferedInputStream.class.getSimpleName() + "{stream=" + in + '}'; + } +} Index: log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerInputStream.java =================================================================== --- log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerInputStream.java (revision 0) +++ log4j-api/src/main/java/org/apache/logging/log4j/streams/LoggerInputStream.java (revision 0) @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.streams; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.spi.LoggerProvider; + +/** + * Input stream that logs each line read to a pre-defined level. Can also be configured with a + * Marker. + */ +public class LoggerInputStream extends FilterInputStream { + private static final String FQCN = LoggerInputStream.class.getName(); + + private final String fqcn; + private final ByteStreamLogger logger; + + public LoggerInputStream(final InputStream in, final Logger logger, final Level level) { + this(in, Charset.defaultCharset(), (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerInputStream(final InputStream in, final Charset charset, final Logger logger, final Level level) { + this(in, charset, (LoggerProvider) logger, FQCN, level, null); + } + + public LoggerInputStream(final InputStream in, final Logger logger, final Level level, final Marker marker) { + this(in, Charset.defaultCharset(), (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerInputStream(final InputStream in, final Charset charset, final Logger logger, final Level level, final Marker marker) { + this(in, charset, (LoggerProvider) logger, FQCN, level, marker); + } + + public LoggerInputStream(final InputStream in, final Charset charset, final LoggerProvider logger, final String fqcn, final Level level, final Marker marker) { + super(in); + this.logger = new ByteStreamLogger(logger, level, marker, charset); + this.fqcn = fqcn; + } + + @Override + public int read() throws IOException { + final int b = super.read(); + logger.put(fqcn, b); + return b; + } + + @Override + public int read(final byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + int bytesRead = super.read(b, off, len); + logger.put(fqcn, b, off, bytesRead); + return bytesRead; + } + + @Override + public void close() throws IOException { + logger.close(fqcn); + super.close(); + } + + @Override + public String toString() { + return LoggerInputStream.class.getSimpleName() + "{stream=" + in + '}'; + } +} Index: log4j-api/pom.xml =================================================================== --- log4j-api/pom.xml (revision 1577410) +++ log4j-api/pom.xml (working copy) @@ -60,6 +60,11 @@ 3.2.1 test + + org.easymock + easymock + test +