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