All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.oliviercailloux.javagrade.utils.StdOutErrLoggerImpl Maven / Gradle / Ivy

The newest version!
package io.github.oliviercailloux.javagrade.utils;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

class StdOutErrLoggerImpl implements StdOutErrLogger {

  @SuppressWarnings("unused")
  private static final Logger LOGGER = LoggerFactory.getLogger(StdOutErrLoggerImpl.class);

  /**
   * Slightly improved from
   * https://svn.apache.org/viewvc/logging/log4j/trunk/contribs/JimMoore/LoggingOutputStream.java?view=co,
   * found thanks to https://stackoverflow.com/a/11187462 (I didn’t check the writing algorithm).
   */
  private static class LoggingOutputStream extends OutputStream {

    private static final String LINE_SEPARATOR = System.getProperty("line.separator");
    private static final int DEFAULT_BUFFER_LENGTH = 2048;
    private Logger delegate;
    private Level level;
    private byte[] buffer;

    /**
     * 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.
     */
    private int count;

    /**
     * Remembers the size of the buffer for speed.
     */
    private int bufferLength;

    private boolean hasBeenClosed = false;

    private LoggingOutputStream(Logger delegate, Level level) {
      this.delegate = checkNotNull(delegate);
      this.level = checkNotNull(level);
      count = 0;
      bufferLength = DEFAULT_BUFFER_LENGTH;
      buffer = new byte[DEFAULT_BUFFER_LENGTH];
      hasBeenClosed = false;
    }

    @Override
    public void write(int b) throws IOException {
      checkState(!hasBeenClosed);

      /* don't log nulls */
      if (b == 0) {
        return;
      }

      /* would this be writing past the buffer? */
      if (count == bufferLength) {
        /* grow the buffer */
        final int newBufLength = bufferLength + DEFAULT_BUFFER_LENGTH;
        final byte[] newBuf = new byte[newBufLength];

        System.arraycopy(buffer, 0, newBuf, 0, bufferLength);

        buffer = newBuf;
        bufferLength = newBufLength;
      }

      buffer[count] = (byte) b;
      count++;
    }

    @Override
    public void flush() {
      checkState(!hasBeenClosed);

      if (count == 0) {
        return;
      }

      /* don't print out blank lines; flushing from PrintStream puts out these */
      if (count == LINE_SEPARATOR.length()) {
        if (((char) buffer[0]) == LINE_SEPARATOR.charAt(0)
            && ((count == 1) || /* <- Unix & Mac, -> Windows */
                ((count == 2) && ((char) buffer[1]) == LINE_SEPARATOR.charAt(1)))) {
          reset();
          return;
        }
      }

      final byte[] theBytes = new byte[count];

      System.arraycopy(buffer, 0, theBytes, 0, count);

      final String content = new String(theBytes);
      switch (level) {
        case ERROR:
          delegate.error(content);
          break;
        case WARN:
          delegate.warn(content);
          break;
        case INFO:
          delegate.info(content);
          break;
        case DEBUG:
          delegate.debug(content);
          break;
        case TRACE:
          delegate.trace(content);
          break;
        default:
          throw new VerifyException("Unknown level");
      }

      reset();
    }

    private void reset() {
      // not resetting the buffer -- assuming that if it grew that it
      // will likely grow similarly again
      count = 0;
    }

    @Override
    public void close() {
      if (!hasBeenClosed) {
        flush();
        hasBeenClosed = true;
      }
    }
  }

  private ImmutableList loggingOutputStreamsToClose;
  private ImmutableList printStreamsToClose;
  private PrintStream outOrig;

  private PrintStream errOrig;
  private boolean closed;

  StdOutErrLoggerImpl() {
    loggingOutputStreamsToClose = null;
    printStreamsToClose = null;
    outOrig = null;
    errOrig = null;
    closed = false;
  }

  @SuppressWarnings("resource")
  void start() {
    checkState(loggingOutputStreamsToClose == null);

    final StdOutErrLoggerImpl.LoggingOutputStream out =
        new LoggingOutputStream(LOGGER, Level.DEBUG);
    final StdOutErrLoggerImpl.LoggingOutputStream err =
        new LoggingOutputStream(LOGGER, Level.DEBUG);
    loggingOutputStreamsToClose = ImmutableList.of(out, err);
    final PrintStream outPs = new PrintStream(out);
    final PrintStream errPs = new PrintStream(err);
    printStreamsToClose = ImmutableList.of(outPs, errPs);

    outOrig = System.out;
    errOrig = System.err;

    System.setOut(outPs);
    System.setErr(errPs);
  }

  @Override
  public void close() {
    if (!closed) {
      System.setOut(outOrig);
      System.setErr(errOrig);
      closed = true;
    }

    for (PrintStream toClose : printStreamsToClose) {
      toClose.close();
    }
    for (StdOutErrLoggerImpl.LoggingOutputStream toClose : loggingOutputStreamsToClose) {
      toClose.close();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy