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  package org.apache.logging.log4j.core.appender;
    18
    19  import java.io.IOException;
    20  import java.io.OutputStream;
    21
    22  import org.apache.logging.log4j.core.Layout;
    23
    24  /**
    25   * Manages an OutputStream so that it can be shared by multiple Appenders and will
    26   * allow appenders to reconfigure without requiring a new stream.
    27   */
    28  public class OutputStreamManager extends AbstractManager {
    29
    30      private volatile OutputStream os;
    31      protected final Layout<?> layout;
    32
    33      protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout) {
    34          super(streamName);
    35          this.os = os;
    36          this.layout = layout;
    37          if (layout != null) {
    38              final byte[] header = layout.getHeader();
    39              if (header != null) {
    40                  try {
    41                      this.os.write(header, 0, header.length);
    42                  } catch (final IOException ioe) {
    43                      LOGGER.error("Unable to write header", ioe);
    44                  }
    45              }
    46          }
    47      }
    48
    49      /**
    50       * Creates a Manager.
    51       *
    52       * @param name The name of the stream to manage.
    53       * @param data The data to pass to the Manager.
    54       * @param factory The factory to use to create the Manager.
    55       * @param <T> The type of the OutputStreamManager.
    56       * @return An OutputStreamManager.
    57       */
    58      public static <T> OutputStreamManager getManager(final String name, final T data,
    59                                                   final ManagerFactory<? extends OutputStreamManager, T> factory) {
    60          return AbstractManager.getManager(name, factory, data);
    61      }
    62
    63      /**
    64       * Default hook to write footer during close.
    65       */
    66      @Override
    67      public void releaseSub() {
    68          writeFooter();
    69          close();
    70      }
    71
    72      /**
    73       * Writes the footer.
    74       */
    75      protected void writeFooter() {
    76          if (layout == null) {
    77              return;
    78          }
    79          final byte[] footer = layout.getFooter();
    80          if (footer != null) {
    81              write(footer);
    82          }
    83      }
    84
    85      /**
    86       * Returns the status of the stream.
    87       * @return true if the stream is open, false if it is not.
    88       */
    89      public boolean isOpen() {
    90          return getCount() > 0;
    91      }
    92
    93      protected OutputStream getOutputStream() {
    94          return os;
    95      }
    96
    97      protected void setOutputStream(final OutputStream os) {
    98          final byte[] header = layout.getHeader();
    99          if (header != null) {
    100             try {
    101                 os.write(header, 0, header.length);
    102                 this.os = os; // only update field if os.write() succeeded
    103             } catch (final IOException ioe) {
    104                 LOGGER.error("Unable to write header", ioe);
    105             }
    106         } else {
    107             this.os = os;
    108         }
    109     }
    110
    111     /**
    112      * Some output streams synchronize writes while others do not. Synchronizing here insures that
    113      * log events won't be intertwined.
    114      * @param bytes The serialized Log event.
    115      * @param offset The offset into the byte array.
    116      * @param length The number of bytes to write.
    117      * @throws AppenderLoggingException if an error occurs.
    118      */
    119     protected synchronized void write(final byte[] bytes, final int offset, final int length)  {
    120         //System.out.println("write " + count);
    121         try {
    122             os.write(bytes, offset, length);
    123         } catch (final IOException ex) {
    124             final String msg = "Error writing to stream " + getName();
    125             throw new AppenderLoggingException(msg, ex);
    126         }
    127     }
    128
    129     /**
    130      * Some output streams synchronize writes while others do not.
    131      * @param bytes The serialized Log event.
    132      * @throws AppenderLoggingException if an error occurs.
    133      */
    134     protected void write(final byte[] bytes)  {
    135         write(bytes, 0, bytes.length);
    136     }
    137
    138     protected synchronized void close() {
    139         final OutputStream stream = os; // access volatile field only once per method
    140         if (stream == System.out || stream == System.err) {
    141             return;
    142         }
    143         try {
    144             stream.close();
    145         } catch (final IOException ex) {
    146             LOGGER.error("Unable to close stream " + getName() + ". " + ex);
    147         }
    148     }
    149
    150     /**
    151      * Flushes any buffers.
    152      */
    153     public synchronized void flush() {
    154         try {
    155             os.flush();
    156         } catch (final IOException ex) {
    157             final String msg = "Error flushing stream " + getName();
    158             throw new AppenderLoggingException(msg, ex);
    159         }
    160     }
    161 }