Index: log4j-streams/src/main/java/org/apache/logging/log4j/streams/helpers/ByteStreamLogger.java =================================================================== --- log4j-streams/src/main/java/org/apache/logging/log4j/streams/helpers/ByteStreamLogger.java (revision 0) +++ log4j-streams/src/main/java/org/apache/logging/log4j/streams/helpers/ByteStreamLogger.java (revision 0) @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.helpers; + +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; + private 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(final String fqcn) { + synchronized (msg) { + closed = true; + logEnd(fqcn); +// in.close(); + } + } + + 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.logIfEnabled(fqcn, level, marker, msg.toString()); + msg.setLength(0); + } + + private class ByteBufferInputStream extends InputStream { + + @Override + public int read() throws IOException { + buf.flip(); + int result = -1; + if (buf.limit() > 0) { + result = buf.get() & 0xFF; + } + buf.compact(); + return result; + } + + @Override + public int read(final byte[] bytes, final int off, final 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-streams/src/main/java/org/apache/logging/log4j/streams/helpers/CharStreamLogger.java =================================================================== --- log4j-streams/src/main/java/org/apache/logging/log4j/streams/helpers/CharStreamLogger.java (revision 0) +++ log4j-streams/src/main/java/org/apache/logging/log4j/streams/helpers/CharStreamLogger.java (revision 0) @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.helpers; + +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++) { + final 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.logIfEnabled(fqcn, level, marker, msg.toString()); // convert to string now so async loggers + // work + msg.setLength(0); + } +} Index: log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerStreams.java =================================================================== --- log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerStreams.java (revision 0) +++ log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerStreams.java (revision 0) @@ -0,0 +1,149 @@ +package org.apache.logging.log4j.streams; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.nio.charset.Charset; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; + +public class LoggerStreams { + + public static Builder trace(final Logger logger) { + return new Builder(logger, Level.TRACE, null); + } + + public static Builder debug(final Logger logger) { + return new Builder(logger, Level.DEBUG, null); + } + + public static Builder info(final Logger logger) { + return new Builder(logger, Level.INFO, null); + } + + public static Builder warn(final Logger logger) { + return new Builder(logger, Level.WARN, null); + } + + public static Builder error(final Logger logger) { + return new Builder(logger, Level.ERROR, null); + } + + public static class Builder { + private final Logger logger; + private final Level level; + private final Marker marker; + + Builder(final Logger logger, final Level level, final Marker marker) { + this.logger = logger; + this.level = level; + this.marker = marker; + } + + public Builder marker(final Marker marker) { + return new Builder(logger, level, marker); + } + + public PrintingBuilder printing() { + return new PrintingBuilder(logger, level, marker, false); + } + + public BufferedBuilder buffered() { + return new BufferedBuilder(logger, level, marker, 0); + } + + public LoggerWriter create(final Writer writer) { + return new LoggerWriter(writer, logger, level, marker); + } + } + + public static class PrintingBuilder { + private final Logger logger; + private final Level level; + private final Marker marker; + private final boolean autoFlush; + + PrintingBuilder(final Logger logger, final Level level, final Marker marker, final boolean autoFlush) { + this.logger = logger; + this.level = level; + this.marker = marker; + this.autoFlush = autoFlush; + } + + public PrintingBuilder marker(final Marker marker) { + return new PrintingBuilder(logger, level, marker, autoFlush); + } + + public PrintingBuilder autoFlush() { + return autoFlush(true); + } + + public PrintingBuilder autoFlush(final boolean autoFlush) { + return new PrintingBuilder(logger, level, marker, autoFlush); + } + + public LoggerPrintWriter create(final Writer writer) { + return new LoggerPrintWriter(writer, autoFlush, logger, level, marker); + } + + public LoggerPrintStream create(final OutputStream out) { + return new LoggerPrintStream(out, autoFlush, logger, level, marker); + } + + public LoggerPrintStream create(final OutputStream out, final Charset charset) { + try { + return new LoggerPrintStream(out, autoFlush, charset, logger, level, marker); + } catch (UnsupportedEncodingException e) { + // Should never occur because the constructor must throw this + throw new IllegalArgumentException("Invalid charset", e); + } + } + } + + public static class BufferedBuilder { + private final Logger logger; + private final Level level; + private final Marker marker; + private final int size; + + BufferedBuilder(final Logger logger, final Level level, final Marker marker, final int size) { + this.logger = logger; + this.level = level; + this.marker = marker; + this.size = size; + } + + public BufferedBuilder marker(final Marker marker) { + return new BufferedBuilder(logger, level, marker, size); + } + + public BufferedBuilder size(final int size) { + return new BufferedBuilder(logger, level, marker, size); + } + + public LoggerBufferedReader create(final Reader reader) { + if (size > 0) { + return new LoggerBufferedReader(reader, size, logger, level, marker); + } + return new LoggerBufferedReader(reader, logger, level, marker); + } + + public LoggerBufferedInputStream create(final InputStream in) { + if (size > 0) { + return new LoggerBufferedInputStream(in, size, logger, level, marker); + } + return new LoggerBufferedInputStream(in, logger, level, marker); + } + + public LoggerBufferedInputStream create(final InputStream in, final Charset charset) { + if (size > 0) { + return new LoggerBufferedInputStream(in, charset, size, logger, level, marker); + } + return new LoggerBufferedInputStream(in, charset, logger, level, marker); + } + } +} Index: log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerOutputStream.java =================================================================== --- log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerOutputStream.java (revision 1588227) +++ log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerOutputStream.java (working copy) @@ -25,6 +25,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.spi.LoggerProvider; +import org.apache.logging.log4j.streams.helpers.ByteStreamLogger; /** * Output stream that logs each line written to a pre-defined level. Can also be configured with a Index: log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerReader.java =================================================================== --- log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerReader.java (revision 1588227) +++ log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerReader.java (working copy) @@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.spi.LoggerProvider; +import org.apache.logging.log4j.streams.helpers.CharStreamLogger; /** * Writer that logs each line written to a pre-defined level. Can also be configured with a Marker. Index: log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerWriter.java =================================================================== --- log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerWriter.java (revision 1588227) +++ log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerWriter.java (working copy) @@ -24,6 +24,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.spi.LoggerProvider; +import org.apache.logging.log4j.streams.helpers.CharStreamLogger; /** * Writer that logs each line written to a pre-defined level. Can also be configured with a Marker. Index: log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerInputStream.java =================================================================== --- log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerInputStream.java (revision 1588227) +++ log4j-streams/src/main/java/org/apache/logging/log4j/streams/LoggerInputStream.java (working copy) @@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.spi.LoggerProvider; +import org.apache.logging.log4j.streams.helpers.ByteStreamLogger; /** * Input stream that logs each line read to a pre-defined level. Can also be configured with a Index: log4j-bom/pom.xml =================================================================== --- log4j-bom/pom.xml (revision 1588126) +++ log4j-bom/pom.xml (working copy) @@ -64,6 +64,12 @@ log4j-slf4j-impl ${project.version} + + + org.apache.logging.log4j + log4j-streams + ${project.version} +