Redirect System.out and System.err to slf4j

后端 未结 2 1727
無奈伤痛
無奈伤痛 2020-11-28 09:27

I needed to redirect System.out/err.println outputs to slf4j.

I know that this is not the way to do logging properly but there is an external librar

2条回答
  •  隐瞒了意图╮
    2020-11-28 09:42

    I solved the problem by using the LoggingOutputStream for log4j and modified it a little bit for slf4j.

    import java.io.IOException;
    import java.io.PrintStream;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class SysStreamsLogger {
    private static Logger sysOutLogger = LoggerFactory.getLogger("SYSOUT");
    private static Logger sysErrLogger = LoggerFactory.getLogger("SYSERR");
    
    public static final PrintStream sysout = System.out;
    public static final PrintStream syserr = System.err;
    
    protected static final String LINE_SEPERATOR = System.getProperty("line.separator");
    
    public static void bindSystemStreams() {
        // Enable autoflush
        System.setOut(new PrintStream(new LoggingOutputStream(sysOutLogger, false), true));
        System.setErr(new PrintStream(new LoggingOutputStream(sysErrLogger, true), true));
    }
    
    public static void unbindSystemStreams() {
        System.setOut(sysout);
        System.setErr(syserr);
    }
    
    private static class LoggingOutputStream extends java.io.OutputStream {
    
        protected Logger log;
        protected boolean isError;
    
        /**
         * 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 0 through buf.length; elements
         * buf[0] through buf[count-1] 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;
    
        private LoggingOutputStream() {
            // illegal
        }
    
        /**
         * Creates the LoggingOutputStream to flush to the given Category.
         * 
         * @param log
         *            the Logger to write to
         * 
         * @param isError
         *            the if true write to error, else info
         * 
         * @exception IllegalArgumentException
         *                if cat == null or priority == null
         */
        public LoggingOutputStream(Logger log, boolean isError) throws IllegalArgumentException {
            if (log == null) {
                throw new IllegalArgumentException("log == null");
            }
    
            this.isError = isError;
            this.log = log;
            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
         * close is that it closes the output stream. A closed
         * stream cannot perform output operations and cannot be reopened.
         */
        @Override
        public void close() {
            flush();
            hasBeenClosed = true;
        }
    
        /**
         * Writes the specified byte to this output stream. The general contract
         * for write is that one byte is written to the output
         * stream. The byte to be written is the eight low-order bits of the
         * argument b. The 24 high-order bits of b are
         * ignored.
         * 
         * @param b
         *            the byte to write
         */
        @Override
        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 flush 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.
         */
        @Override
        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);
    
            if (isError) {
                log.error(new String(theBytes));
            } else {
                log.info(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;
        }
    }
    }
    

    Now it's possible to redirect Sytem.out/err by calling SysStreamsLogger.bindSystemStreams() at the begining of your application.

    Logging Output looks like this (from OpenEJB startup)

    2012-06-27 13:44:12,792  INFO [main:] SYSOUT:181 - Apache OpenEJB 3.1.4    build: 20101112-03:32
    2012-06-27 13:44:12,793  INFO [main:] SYSOUT:181 - http://openejb.apache.org/
    

提交回复
热议问题