View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core;
15  
16  import static ch.qos.logback.core.CoreConstants.CODES_URL;
17  
18  import java.io.IOException;
19  import java.io.OutputStream;
20  import java.util.concurrent.locks.ReentrantLock;
21  
22  import ch.qos.logback.core.encoder.Encoder;
23  import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
24  import ch.qos.logback.core.spi.DeferredProcessingAware;
25  import ch.qos.logback.core.status.ErrorStatus;
26  
27  /**
28   * OutputStreamAppender appends events to a {@link OutputStream}. This class
29   * provides basic services that other appenders build upon.
30   * 
31   * For more information about this appender, please refer to the online manual
32   * at http://logback.qos.ch/manual/appenders.html#OutputStreamAppender
33   * 
34   * @author Ceki Gülcü
35   */
36  public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {
37  
38      /**
39       * It is the encoder which is ultimately responsible for writing the event to an
40       * {@link OutputStream}.
41       */
42      protected Encoder<E> encoder;
43  
44      /**
45       * All synchronization in this class is done via the lock object.
46       */
47      protected final ReentrantLock lock = new ReentrantLock(false);
48  
49      /**
50       * This is the {@link OutputStream outputStream} where output will be written.
51       */
52      private OutputStream outputStream;
53  
54      boolean immediateFlush = true;
55  
56      /**
57       * The underlying output stream used by this appender.
58       * 
59       * @return
60       */
61      public OutputStream getOutputStream() {
62          return outputStream;
63      }
64  
65      /**
66       * Checks that requires parameters are set and if everything is in order,
67       * activates this appender.
68       */
69      public void start() {
70          int errors = 0;
71          if (this.encoder == null) {
72              addStatus(new ErrorStatus("No encoder set for the appender named \"" + name + "\".", this));
73              errors++;
74          }
75  
76          if (this.outputStream == null) {
77              addStatus(new ErrorStatus("No output stream set for the appender named \"" + name + "\".", this));
78              errors++;
79          }
80          // only error free appenders should be activated
81          if (errors == 0) {
82              super.start();
83          }
84      }
85  
86      public void setLayout(Layout<E> layout) {
87          addWarn("This appender no longer admits a layout as a sub-component, set an encoder instead.");
88          addWarn("To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.");
89          addWarn("See also " + CODES_URL + "#layoutInsteadOfEncoder for details");
90          LayoutWrappingEncoder<E> lwe = new LayoutWrappingEncoder<E>();
91          lwe.setLayout(layout);
92          lwe.setContext(context);
93          this.encoder = lwe;
94      }
95  
96      @Override
97      protected void append(E eventObject) {
98          if (!isStarted()) {
99              return;
100         }
101 
102         subAppend(eventObject);
103     }
104 
105     /**
106      * Stop this appender instance. The underlying stream or writer is also closed.
107      * 
108      * <p>
109      * Stopped appenders cannot be reused.
110      */
111     public void stop() {
112         if(!isStarted())
113             return;
114 
115         lock.lock();
116         try {
117             closeOutputStream();
118             super.stop();
119         } finally {
120             lock.unlock();
121         }
122     }
123 
124     /**
125      * Close the underlying {@link OutputStream}.
126      */
127     protected void closeOutputStream() {
128         if (this.outputStream != null) {
129             try {
130                 // before closing we have to output out layout's footer
131                 encoderClose();
132                 this.outputStream.close();
133                 this.outputStream = null;
134             } catch (IOException e) {
135                 addStatus(new ErrorStatus("Could not close output stream for OutputStreamAppender.", this, e));
136             }
137         }
138     }
139 
140     void encoderClose() {
141         if (encoder != null && this.outputStream != null) {
142             try {
143                 byte[] footer = encoder.footerBytes();
144                 writeBytes(footer);
145             } catch (IOException ioe) {
146                 this.started = false;
147                 addStatus(new ErrorStatus("Failed to write footer for appender named [" + name + "].", this, ioe));
148             }
149         }
150     }
151 
152     /**
153      * <p>
154      * Sets the @link OutputStream} where the log output will go. The specified
155      * <code>OutputStream</code> must be opened by the user and be writable. The
156      * <code>OutputStream</code> will be closed when the appender instance is
157      * closed.
158      * 
159      * @param outputStream An already opened OutputStream.
160      */
161     public void setOutputStream(OutputStream outputStream) {
162         lock.lock();
163         try {
164             // close any previously opened output stream
165             closeOutputStream();
166             this.outputStream = outputStream;
167             if (encoder == null) {
168                 addWarn("Encoder has not been set. Cannot invoke its init method.");
169                 return;
170             }
171 
172             encoderInit();
173         } finally {
174             lock.unlock();
175         }
176     }
177 
178     void encoderInit() {
179         if (encoder != null && this.outputStream != null) {
180             try {
181                 byte[] header = encoder.headerBytes();
182                 writeBytes(header);
183             } catch (IOException ioe) {
184                 this.started = false;
185                 addStatus(
186                         new ErrorStatus("Failed to initialize encoder for appender named [" + name + "].", this, ioe));
187             }
188         }
189     }
190 
191     protected void writeOut(E event) throws IOException {
192         byte[] byteArray = this.encoder.encode(event);
193         writeBytes(byteArray);
194     }
195 
196     private void writeBytes(byte[] byteArray) throws IOException {
197         if (byteArray == null || byteArray.length == 0)
198             return;
199 
200         lock.lock();
201         try {
202             this.outputStream.write(byteArray);
203             if (immediateFlush) {
204                 this.outputStream.flush();
205             }
206         } finally {
207             lock.unlock();
208         }
209     }
210 
211     /**
212      * Actual writing occurs here.
213      * <p>
214      * Most subclasses of <code>WriterAppender</code> will need to override this
215      * method.
216      * 
217      * @since 0.9.0
218      */
219     protected void subAppend(E event) {
220         if (!isStarted()) {
221             return;
222         }
223         try {
224             // this step avoids LBCLASSIC-139
225             if (event instanceof DeferredProcessingAware) {
226                 ((DeferredProcessingAware) event).prepareForDeferredProcessing();
227             }
228             writeOut(event);
229 
230         } catch (IOException ioe) {
231             // as soon as an exception occurs, move to non-started state
232             // and add a single ErrorStatus to the SM.
233             this.started = false;
234             addStatus(new ErrorStatus("IO failure in appender", this, ioe));
235         }
236     }
237 
238     public Encoder<E> getEncoder() {
239         return encoder;
240     }
241 
242     public void setEncoder(Encoder<E> encoder) {
243         this.encoder = encoder;
244     }
245 
246     public boolean isImmediateFlush() {
247         return immediateFlush;
248     }
249 
250     public void setImmediateFlush(boolean immediateFlush) {
251         this.immediateFlush = immediateFlush;
252     }
253 
254 }