1
2
3
4
5
6
7
8
9
10
11
12
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.rolling.LengthCounter;
25 import ch.qos.logback.core.spi.DeferredProcessingAware;
26 import ch.qos.logback.core.status.ErrorStatus;
27
28
29
30
31
32
33
34
35
36
37 public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {
38
39
40
41
42
43 protected Encoder<E> encoder;
44
45
46
47
48 protected final ReentrantLock streamWriteLock = new ReentrantLock(false);
49
50
51
52
53 private OutputStream outputStream;
54
55 boolean immediateFlush = true;
56
57
58
59
60
61
62 public OutputStream getOutputStream() {
63 return outputStream;
64 }
65
66
67
68
69
70 public void start() {
71 int errors = 0;
72 if (this.encoder == null) {
73 addStatus(new ErrorStatus("No encoder set for the appender named \"" + name + "\".", this));
74 errors++;
75 }
76
77 if (this.outputStream == null) {
78 addStatus(new ErrorStatus("No output stream set for the appender named \"" + name + "\".", this));
79 errors++;
80 }
81
82 if (encoder == null) {
83 addWarn("Encoder has not been set. Cannot invoke its init method.");
84 errors++;
85 }
86
87
88
89 if (errors == 0) {
90 super.start();
91 encoderInit();
92 }
93 }
94
95 public void setLayout(Layout<E> layout) {
96 addWarn("This appender no longer admits a layout as a sub-component, set an encoder instead.");
97 addWarn("To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.");
98 addWarn("See also " + CODES_URL + "#layoutInsteadOfEncoder for details");
99 LayoutWrappingEncoder<E> lwe = new LayoutWrappingEncoder<E>();
100 lwe.setLayout(layout);
101 lwe.setContext(context);
102 this.encoder = lwe;
103 }
104
105 @Override
106 protected void append(E eventObject) {
107 if (!isStarted()) {
108 return;
109 }
110
111 subAppend(eventObject);
112 }
113
114
115
116
117
118
119
120 public void stop() {
121 if(!isStarted())
122 return;
123
124 streamWriteLock.lock();
125 try {
126 closeOutputStream();
127 super.stop();
128 } finally {
129 streamWriteLock.unlock();
130 }
131 }
132
133
134
135
136 protected void closeOutputStream() {
137 if (this.outputStream != null) {
138 try {
139
140 encoderClose();
141 this.outputStream.close();
142 this.outputStream = null;
143 } catch (IOException e) {
144 addStatus(new ErrorStatus("Could not close output stream for OutputStreamAppender.", this, e));
145 }
146 }
147 }
148
149 void encoderClose() {
150 if (encoder != null && this.outputStream != null) {
151 try {
152 byte[] footer = encoder.footerBytes();
153 writeBytes(footer);
154 } catch (IOException ioe) {
155 this.started = false;
156 addStatus(new ErrorStatus("Failed to write footer for appender named [" + name + "].", this, ioe));
157 }
158 }
159 }
160
161
162
163
164
165
166
167
168
169
170 public void setOutputStream(OutputStream outputStream) {
171 streamWriteLock.lock();
172 try {
173
174 closeOutputStream();
175 this.outputStream = outputStream;
176
177
178
179
180
181 if(isStarted()) {
182 encoderInit();
183 }
184 } finally {
185 streamWriteLock.unlock();
186 }
187 }
188
189 void encoderInit() {
190 if (encoder != null && this.outputStream != null) {
191 try {
192 byte[] header = encoder.headerBytes();
193 writeBytes(header);
194 } catch (IOException ioe) {
195 this.started = false;
196 addStatus(
197 new ErrorStatus("Failed to initialize encoder for appender named [" + name + "].", this, ioe));
198 }
199 }
200 }
201
202 protected void writeOut(E event) throws IOException {
203 byte[] byteArray = this.encoder.encode(event);
204 writeBytes(byteArray);
205 }
206
207 private void writeBytes(byte[] byteArray) throws IOException {
208 if (byteArray == null || byteArray.length == 0)
209 return;
210
211 streamWriteLock.lock();
212
213 try {
214
215
216 if(isStarted()) {
217 writeByteArrayToOutputStreamWithPossibleFlush(byteArray);
218 updateByteCount(byteArray);
219 }
220 } finally {
221 streamWriteLock.unlock();
222 }
223 }
224
225 protected void updateByteCount(byte[] byteArray) {
226 }
227
228
229
230
231
232
233 protected final void writeByteArrayToOutputStreamWithPossibleFlush(byte[] byteArray) throws IOException {
234 this.outputStream.write(byteArray);
235 if (immediateFlush) {
236 this.outputStream.flush();
237 }
238 }
239
240
241
242
243
244
245
246
247
248 protected void subAppend(E event) {
249 if (!isStarted()) {
250 return;
251 }
252 try {
253
254 if (event instanceof DeferredProcessingAware) {
255 ((DeferredProcessingAware) event).prepareForDeferredProcessing();
256 }
257 writeOut(event);
258
259 } catch (IOException ioe) {
260
261
262 this.started = false;
263 addStatus(new ErrorStatus("IO failure in appender", this, ioe));
264 }
265 }
266
267 public Encoder<E> getEncoder() {
268 return encoder;
269 }
270
271 public void setEncoder(Encoder<E> encoder) {
272 this.encoder = encoder;
273 }
274
275 public boolean isImmediateFlush() {
276 return immediateFlush;
277 }
278
279 public void setImmediateFlush(boolean immediateFlush) {
280 this.immediateFlush = immediateFlush;
281 }
282
283 }