View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17
    18  package org.apache.logging.log4j.io;
    19
    20  import java.io.IOException;
    21  import java.io.InputStream;
    22  import java.io.InputStreamReader;
    23  import java.nio.ByteBuffer;
    24  import java.nio.charset.Charset;
    25
    26  import org.apache.logging.log4j.Level;
    27  import org.apache.logging.log4j.Marker;
    28  import org.apache.logging.log4j.spi.ExtendedLogger;
    29
    30  /**
    31   *
    32   * @since 2.1
    33   */
    34  public class ByteStreamLogger {
    35      private class ByteBufferInputStream extends InputStream {
    36
    37          @Override
    38          public int read() throws IOException {
    39              ByteStreamLogger.this.buf.flip();
    40              int result = -1;
    41              if (ByteStreamLogger.this.buf.limit() > 0) {
    42                  result = ByteStreamLogger.this.buf.get() & 0xFF;
    43              }
    44              ByteStreamLogger.this.buf.compact();
    45              return result;
    46          }
    47
    48          @Override
    49          public int read(final byte[] bytes, final int off, final int len) throws IOException {
    50              ByteStreamLogger.this.buf.flip();
    51              int result = -1;
    52              if (ByteStreamLogger.this.buf.limit() > 0) {
    53                  result = Math.min(len, ByteStreamLogger.this.buf.limit());
    54                  ByteStreamLogger.this.buf.get(bytes, off, result);
    55              }
    56              ByteStreamLogger.this.buf.compact();
    57              return result;
    58          }
    59      }
    60
    61      private static final int BUFFER_SIZE = 1024;
    62      private final ExtendedLogger logger;
    63      private final Level level;
    64      private final Marker marker;
    65      private final InputStreamReader reader;
    66      private final char[] msgBuf = new char[BUFFER_SIZE];
    67      private final StringBuilder msg = new StringBuilder();
    68      private boolean closed;
    69
    70      private final ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE);
    71
    72      public ByteStreamLogger(final ExtendedLogger logger, final Level level, final Marker marker, final Charset charset) {
    73          this.logger = logger;
    74          this.level = level == null ? logger.getLevel() : level;
    75          this.marker = marker;
    76          this.reader = new InputStreamReader(new ByteBufferInputStream(),
    77              charset == null ? Charset.defaultCharset() : charset);
    78      }
    79
    80      public void close(final String fqcn) {
    81          synchronized (this.msg) {
    82              this.closed = true;
    83              logEnd(fqcn);
    84          }
    85      }
    86
    87      private void extractMessages(final String fqcn) throws IOException {
    88          if (this.closed) {
    89              return;
    90          }
    91          int read = this.reader.read(this.msgBuf);
    92          while (read > 0) {
    93              int off = 0;
    94              for (int pos = 0; pos < read; pos++) {
    95                  switch (this.msgBuf[pos]) {
    96                  case '\r':
    97                      this.msg.append(this.msgBuf, off, pos - off);
    98                      off = pos + 1;
    99                      break;
    100                 case '\n':
    101                     this.msg.append(this.msgBuf, off, pos - off);
    102                     off = pos + 1;
    103                     log(fqcn);
    104                     break;
    105                 }
    106             }
    107             this.msg.append(this.msgBuf, off, read - off);
    108             read = this.reader.read(this.msgBuf);
    109         }
    110     }
    111
    112     private void log(final String fqcn) {
    113         // convert to string now so async loggers work
    114         this.logger.logIfEnabled(fqcn, this.level, this.marker, this.msg.toString());
    115         this.msg.setLength(0);
    116     }
    117
    118     private void logEnd(final String fqcn) {
    119         if (this.msg.length() > 0) {
    120             log(fqcn);
    121         }
    122     }
    123
    124     public void put(final String fqcn, final byte[] b, final int off, final int len) throws IOException {
    125         int curOff = off;
    126         int curLen = len;
    127         if (curLen >= 0) {
    128             synchronized (this.msg) {
    129                 while (curLen > this.buf.remaining()) {
    130                     final int remaining = this.buf.remaining();
    131                     this.buf.put(b, curOff, remaining);
    132                     curLen -= remaining;
    133                     curOff += remaining;
    134                     extractMessages(fqcn);
    135                 }
    136                 this.buf.put(b, curOff, curLen);
    137                 extractMessages(fqcn);
    138             }
    139         } else {
    140             logEnd(fqcn);
    141         }
    142     }
    143
    144     public void put(final String fqcn, final int b) throws IOException {
    145         if (b >= 0) {
    146             synchronized (this.msg) {
    147                 this.buf.put((byte) (b & 0xFF));
    148                 extractMessages(fqcn);
    149             }
    150         } else {
    151             logEnd(fqcn);
    152         }
    153     }
    154 }