| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| import java.io.*; |
| import org.apache.log4j.*; |
| |
| |
| /** |
| * An OutputStream that flushes out to a Category.<p> |
| * |
| * Note that no data is written out to the Category until the stream is |
| * flushed or closed.<p> |
| * |
| * Example:<pre> |
| * // make sure everything sent to System.err is logged |
| * System.setErr(new PrintStream(new LoggingOutputStream(Category.getRoot(), Priority.WARN), true)); |
| * |
| * // make sure everything sent to System.out is also logged |
| * System.setOut(new PrintStream(new LoggingOutputStream(Category.getRoot(), Priority.INFO), true)); |
| * </pre> |
| * |
| * @author <a href="mailto://Jim.Moore@rocketmail.com">Jim Moore</a> |
| * @see Category |
| */ |
| public class LoggingOutputStream extends OutputStream { |
| protected static final String LINE_SEPERATOR = System.getProperty("line.separator"); |
| |
| |
| /** |
| * Used to maintain the contract of {@link #close()}. |
| */ |
| protected boolean hasBeenClosed = false; |
| |
| /** |
| * The internal buffer where data is stored. |
| */ |
| protected byte[] buf; |
| |
| /** |
| * The number of valid bytes in the buffer. This value is always |
| * in the range <tt>0</tt> through <tt>buf.length</tt>; elements |
| * <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid |
| * byte data. |
| */ |
| protected int count; |
| |
| |
| /** |
| * Remembers the size of the buffer for speed. |
| */ |
| private int bufLength; |
| |
| /** |
| * The default number of bytes in the buffer. =2048 |
| */ |
| public static final int DEFAULT_BUFFER_LENGTH = 2048; |
| |
| |
| /** |
| * The category to write to. |
| */ |
| protected Category category; |
| |
| /** |
| * The priority to use when writing to the Category. |
| */ |
| protected Priority priority; |
| |
| |
| private LoggingOutputStream() { |
| // illegal |
| } |
| |
| |
| /** |
| * Creates the LoggingOutputStream to flush to the given Category. |
| * |
| * @param cat the Category to write to |
| * |
| * @param priority the Priority to use when writing to the Category |
| * |
| * @exception IllegalArgumentException |
| * if cat == null or priority == null |
| */ |
| public LoggingOutputStream(Category cat, Priority priority) |
| throws IllegalArgumentException { |
| if (cat == null) { |
| throw new IllegalArgumentException("cat == null"); |
| } |
| if (priority == null) { |
| throw new IllegalArgumentException("priority == null"); |
| } |
| |
| this.priority = priority; |
| category = cat; |
| bufLength = DEFAULT_BUFFER_LENGTH; |
| buf = new byte[DEFAULT_BUFFER_LENGTH]; |
| count = 0; |
| } |
| |
| |
| /** |
| * Closes this output stream and releases any system resources |
| * associated with this stream. The general contract of <code>close</code> |
| * is that it closes the output stream. A closed stream cannot perform |
| * output operations and cannot be reopened. |
| */ |
| public void close() { |
| flush(); |
| hasBeenClosed = true; |
| } |
| |
| |
| /** |
| * Writes the specified byte to this output stream. The general |
| * contract for <code>write</code> is that one byte is written |
| * to the output stream. The byte to be written is the eight |
| * low-order bits of the argument <code>b</code>. The 24 |
| * high-order bits of <code>b</code> are ignored. |
| * |
| * @param b the <code>byte</code> to write |
| * |
| * @exception IOException |
| * if an I/O error occurs. In particular, |
| * an <code>IOException</code> may be thrown if the |
| * output stream has been closed. |
| */ |
| public void write(final int b) throws IOException { |
| if (hasBeenClosed) { |
| throw new IOException("The stream has been closed."); |
| } |
| |
| // don't log nulls |
| if (b == 0) { |
| return; |
| } |
| |
| // would this be writing past the buffer? |
| if (count == bufLength) { |
| // grow the buffer |
| final int newBufLength = bufLength+DEFAULT_BUFFER_LENGTH; |
| final byte[] newBuf = new byte[newBufLength]; |
| |
| System.arraycopy(buf, 0, newBuf, 0, bufLength); |
| |
| buf = newBuf; |
| bufLength = newBufLength; |
| } |
| |
| buf[count] = (byte)b; |
| count++; |
| } |
| |
| |
| /** |
| * Flushes this output stream and forces any buffered output bytes |
| * to be written out. The general contract of <code>flush</code> is |
| * that calling it is an indication that, if any bytes previously |
| * written have been buffered by the implementation of the output |
| * stream, such bytes should immediately be written to their |
| * intended destination. |
| */ |
| public void flush() { |
| if (count == 0) { |
| return; |
| } |
| |
| // don't print out blank lines; flushing from PrintStream puts out these |
| if (count == LINE_SEPERATOR.length()) { |
| if ( ((char)buf[0]) == LINE_SEPERATOR.charAt(0) && |
| ( ( count == 1 ) || // <- Unix & Mac, -> Windows |
| ( (count == 2) && ((char)buf[1]) == LINE_SEPERATOR.charAt(1) ) ) ) { |
| reset(); |
| return; |
| } |
| } |
| |
| final byte[] theBytes = new byte[count]; |
| |
| System.arraycopy(buf, 0, theBytes, 0, count); |
| |
| category.log(priority, new String(theBytes)); |
| |
| reset(); |
| } |
| |
| |
| private void reset() { |
| // not resetting the buffer -- assuming that if it grew that it |
| // will likely grow similarly again |
| count = 0; |
| } |
| |
| } |
| |