/* * 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.io; 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.ExtendedLogger; /** * * @since 2.1 */ public class ByteStreamLogger { private class ByteBufferInputStream extends InputStream { @Override public int read() throws IOException { ByteStreamLogger.this.buf.flip(); int result = -1; if (ByteStreamLogger.this.buf.limit() > 0) { result = ByteStreamLogger.this.buf.get() & 0xFF; } ByteStreamLogger.this.buf.compact(); return result; } @Override public int read(final byte[] bytes, final int off, final int len) throws IOException { ByteStreamLogger.this.buf.flip(); int result = -1; if (ByteStreamLogger.this.buf.limit() > 0) { result = Math.min(len, ByteStreamLogger.this.buf.limit()); ByteStreamLogger.this.buf.get(bytes, off, result); } ByteStreamLogger.this.buf.compact(); return result; } } private static final int BUFFER_SIZE = 1024; private final ExtendedLogger logger; private final Level level; private final Marker marker; 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 ExtendedLogger logger, final Level level, final Marker marker, final Charset charset) { this.logger = logger; this.level = level == null ? logger.getLevel() : level; this.marker = marker; this.reader = new InputStreamReader(new ByteBufferInputStream(), charset == null ? Charset.defaultCharset() : charset); } public void close(final String fqcn) { synchronized (this.msg) { this.closed = true; logEnd(fqcn); } } private void extractMessages(final String fqcn) throws IOException { if (this.closed) { return; } int read = this.reader.read(this.msgBuf); while (read > 0) { int off = 0; for (int pos = 0; pos < read; pos++) { switch (this.msgBuf[pos]) { case '\r': this.msg.append(this.msgBuf, off, pos - off); off = pos + 1; break; case '\n': this.msg.append(this.msgBuf, off, pos - off); off = pos + 1; log(fqcn); break; } } this.msg.append(this.msgBuf, off, read - off); read = this.reader.read(this.msgBuf); } } private void log(final String fqcn) { // convert to string now so async loggers work this.logger.logIfEnabled(fqcn, this.level, this.marker, this.msg.toString()); this.msg.setLength(0); } private void logEnd(final String fqcn) { if (this.msg.length() > 0) { log(fqcn); } } public void put(final String fqcn, final byte[] b, final int off, final int len) throws IOException { int curOff = off; int curLen = len; if (curLen >= 0) { synchronized (this.msg) { while (curLen > this.buf.remaining()) { final int remaining = this.buf.remaining(); this.buf.put(b, curOff, remaining); curLen -= remaining; curOff += remaining; extractMessages(fqcn); } this.buf.put(b, curOff, curLen); extractMessages(fqcn); } } else { logEnd(fqcn); } } public void put(final String fqcn, final int b) throws IOException { if (b >= 0) { synchronized (this.msg) { this.buf.put((byte) (b & 0xFF)); extractMessages(fqcn); } } else { logEnd(fqcn); } } }