1
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
36 public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {
37
38
42 protected Encoder<E> encoder;
43
44
47 protected final ReentrantLock lock = new ReentrantLock(false);
48
49
52 private OutputStream outputStream;
53
54 boolean immediateFlush = true;
55
56
61 public OutputStream getOutputStream() {
62 return outputStream;
63 }
64
65
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
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
112 public void stop() {
113 lock.lock();
114 try {
115 closeOutputStream();
116 super.stop();
117 } finally {
118 lock.unlock();
119 }
120 }
121
122
125 protected void closeOutputStream() {
126 if (this.outputStream != null) {
127 try {
128
129 encoderClose();
130 this.outputStream.close();
131 this.outputStream = null;
132 } catch (IOException e) {
133 addStatus(new ErrorStatus("Could not close output stream for OutputStreamAppender.", this, e));
134 }
135 }
136 }
137
138 void encoderClose() {
139 if (encoder != null && this.outputStream != null) {
140 try {
141 byte[] footer = encoder.footerBytes();
142 writeBytes(footer);
143 } catch (IOException ioe) {
144 this.started = false;
145 addStatus(new ErrorStatus("Failed to write footer for appender named [" + name + "].", this, ioe));
146 }
147 }
148 }
149
150
160 public void setOutputStream(OutputStream outputStream) {
161 lock.lock();
162 try {
163
164 closeOutputStream();
165 this.outputStream = outputStream;
166 if (encoder == null) {
167 addWarn("Encoder has not been set. Cannot invoke its init method.");
168 return;
169 }
170
171 encoderInit();
172 } finally {
173 lock.unlock();
174 }
175 }
176
177 void encoderInit() {
178 if (encoder != null && this.outputStream != null) {
179 try {
180 byte[] header = encoder.headerBytes();
181 writeBytes(header);
182 } catch (IOException ioe) {
183 this.started = false;
184 addStatus(new ErrorStatus("Failed to initialize encoder for appender named [" + name + "].", this, ioe));
185 }
186 }
187 }
188 protected void writeOut(E event) throws IOException {
189 byte[] byteArray = this.encoder.encode(event);
190 writeBytes(byteArray);
191 }
192
193 private void writeBytes(byte[] byteArray) throws IOException {
194 if(byteArray == null || byteArray.length == 0)
195 return;
196
197 lock.lock();
198 try {
199 this.outputStream.write(byteArray);
200 if (immediateFlush) {
201 this.outputStream.flush();
202 }
203 } finally {
204 lock.unlock();
205 }
206 }
207
208
216 protected void subAppend(E event) {
217 if (!isStarted()) {
218 return;
219 }
220 try {
221
222 if (event instanceof DeferredProcessingAware) {
223 ((DeferredProcessingAware) event).prepareForDeferredProcessing();
224 }
225
226
227
228
229
230 byte[] byteArray = this.encoder.encode(event);
231 writeBytes(byteArray);
232
233 } catch (IOException ioe) {
234
235
236 this.started = false;
237 addStatus(new ErrorStatus("IO failure in appender", this, ioe));
238 }
239 }
240
241 public Encoder<E> getEncoder() {
242 return encoder;
243 }
244
245 public void setEncoder(Encoder<E> encoder) {
246 this.encoder = encoder;
247 }
248
249 public boolean isImmediateFlush() {
250 return immediateFlush;
251 }
252
253 public void setImmediateFlush(boolean immediateFlush) {
254 this.immediateFlush = immediateFlush;
255 }
256
257 }
258