【问题标题】:Redirect System.out and System.err to slf4j将 System.out 和 System.err 重定向到 slf4j
【发布时间】:2012-06-25 10:21:22
【问题描述】:

我需要将 System.out/err.println 输出重定向到 slf4j。

我知道这不是正确记录日志的方法,但是有一个外部库可以记录到 System.out

【问题讨论】:

标签: java logging log4j slf4j


【解决方案1】:

您可以使用sysout-over-slf4j。

sysout-over-slf4j 模块允许用户将所有对 System.out 和 System.err 的调用重定向到 SLF4J 定义的记录器,该记录器具有 System.out.println(或类似)的完全限定类的名称在可配置的级别上进行了调用。

如果您不使用 Maven,请download jar 并将其添加到您的类路径中。

或者,将其添加为 Maven 依赖项:

<dependency>
    <groupId>uk.org.lidalia</groupId>
    <artifactId>sysout-over-slf4j</artifactId>
    <version>1.0.2</version>
</dependency>

然后在应用程序启动时,call:

SysOutOverSLF4J.sendSystemOutAndErrToSLF4J();

【讨论】:

  • 看起来 sysout-ver-slf4j 依赖于 slf4j 1.6.0 我尝试将它添加到我们的类路径中,但在使用 slf4j 1.7.7 时出现编译时错误
  • 运行良好(slf4j 1.7.13)
  • 被接受为新的回答者!最好有一个维护的库,而不是来自某人的一段代码。但我需要提一下,我还没有测试过这个库!
  • 此库无法在 OSGI 环境中运行,因为缺少 MANIFEST.MF(例如)Bundle-SymbolicName
【解决方案2】:

我通过将LoggingOutputStream 用于 log4j 解决了这个问题,并为 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 <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;

    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
     * <code>close</code> 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 <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
     */
    @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 <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.
     */
    @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;
    }
}
}

现在可以通过在应用程序开始时调用 SysStreamsLogger.bindSystemStreams() 来重定向 Sytem.out/err。

日志输出如下所示(来自 OpenEJB 启动)

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/

【讨论】:

  • 使用了这个,但添加了一个 System.getProperty() 来获取一个 vm -DREDIRECT_SYSLOGS=true 以便 Eclipse 仍然按预期将错误记录到其控制台。我只在生产环境中重定向日志。
  • 感谢分享。我设法将错误流重定向到日志。
猜你喜欢
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
  • 2011-06-26
相关资源
最近更新 更多