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 }