View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, 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  
21  import ch.qos.logback.core.encoder.Encoder;
22  import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
23  import ch.qos.logback.core.spi.DeferredProcessingAware;
24  import ch.qos.logback.core.status.ErrorStatus;
25  
26  /**
27   * OutputStreamAppender appends events to a {@link OutputStream}. This class
28   * provides basic services that other appenders build upon.
29   * 
30   * For more information about this appender, please refer to the online manual
31   * at http://logback.qos.ch/manual/appenders.html#OutputStreamAppender
32   * 
33   * @author Ceki Gülcü
34   */
35  public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {
36  
37    
38    /**
39     * It is the encoder which is ultimately responsible for writing the event to
40     * an {@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 Object lock = new Object();
48  
49    /**
50     * This is the {@link OutputStream outputStream} where output will be written.
51     */
52    private OutputStream outputStream;
53  
54    /**
55     * The underlying output stream used by this appender.
56     * 
57     * @return
58     */
59    public OutputStream getOutputStream() {
60      return outputStream;
61    }
62  
63    /**
64     * Checks that requires parameters are set and if everything is in order,
65     * activates this appender.
66     */
67    public void start() {
68      int errors = 0;
69      if (this.encoder == null) {
70        addStatus(new ErrorStatus("No encoder set for the appender named \""
71            + name + "\".", this));
72        errors++;
73      }
74  
75      if (this.outputStream == null) {
76        addStatus(new ErrorStatus(
77            "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
107    * closed.
108    * 
109    * <p>
110    * Stopped appenders cannot be reused.
111    */
112   public void stop() {
113     synchronized (lock) {
114       closeOutputStream();
115       super.stop();
116     }
117   }
118 
119   /**
120    * Close the underlying {@link OutputStream}.
121    */
122   protected void closeOutputStream() {
123     if (this.outputStream != null) {
124       try {
125         // before closing we have to output out layout's footer
126         encoderClose();
127         this.outputStream.close();
128         this.outputStream = null;
129       } catch (IOException e) {
130         addStatus(new ErrorStatus(
131             "Could not close output stream for OutputStreamAppender.", this, e));
132       }
133     }
134   }
135 
136   void encoderInit() {
137     if (encoder != null && this.outputStream != null) {
138       try {
139         encoder.init(outputStream);
140       } catch (IOException ioe) {
141         this.started = false;
142         addStatus(new ErrorStatus(
143             "Failed to initialize encoder for appender named [" + name + "].",
144             this, ioe));
145       }
146     }
147   }
148 
149   void encoderClose() {
150     if (encoder != null && this.outputStream != null) {
151       try {
152         encoder.close();
153       } catch (IOException ioe) {
154         this.started = false;
155         addStatus(new ErrorStatus("Failed to write footer for appender named ["
156             + name + "].", this, ioe));
157       }
158     }
159   }
160 
161   /**
162    * <p>
163    * Sets the @link OutputStream} where the log output will go. The specified
164    * <code>OutputStream</code> must be opened by the user and be writable. The
165    * <code>OutputStream</code> will be closed when the appender instance is
166    * closed.
167    * 
168    * @param outputStream
169    *          An already opened OutputStream.
170    */
171   public void setOutputStream(OutputStream outputStream) {
172     synchronized (lock) {
173       // close any previously opened output stream
174       closeOutputStream();
175 
176       this.outputStream = outputStream;
177       if (encoder == null) {
178         addWarn("Encoder has not been set. Cannot invoke its init method.");
179         return;
180       }
181 
182       encoderInit();
183     }
184   }
185 
186   protected void writeOut(E event) throws IOException {
187     this.encoder.doEncode(event);
188   }
189 
190   /**
191    * Actual writing occurs here.
192    * <p>
193    * Most subclasses of <code>WriterAppender</code> will need to override this
194    * method.
195    * 
196    * @since 0.9.0
197    */
198   protected void subAppend(E event) {
199     if (!isStarted()) {
200       return;
201     }
202     try {
203       // this step avoids LBCLASSIC-139
204       if (event instanceof DeferredProcessingAware) {
205         ((DeferredProcessingAware) event).prepareForDeferredProcessing();
206       }
207       // the synchronization prevents the OutputStream from being closed while we
208       // are writing. It also prevents multiple threads from entering the same
209       // converter. Converters assume that they are in a synchronized block.
210       synchronized (lock) {
211         writeOut(event);
212       }
213     } catch (IOException ioe) {
214       // as soon as an exception occurs, move to non-started state
215       // and add a single ErrorStatus to the SM.
216       this.started = false;
217       addStatus(new ErrorStatus("IO failure in appender", this, ioe));
218     }
219   }
220 
221   public Encoder<E> getEncoder() {
222     return encoder;
223   }
224 
225   public void setEncoder(Encoder<E> encoder) {
226     this.encoder = encoder;
227   }
228   
229 }