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