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.spi.DeferredProcessingAware;
25 import ch.qos.logback.core.status.ErrorStatus;
26
27
28
29
30
31
32
33
34
35
36 public class OutputStreamAppender<E> extends UnsynchronizedAppenderBase<E> {
37
38
39
40
41
42 protected Encoder<E> encoder;
43
44
45
46
47 protected final ReentrantLock streamWriteLock = new ReentrantLock(false);
48
49
50
51
52 private OutputStream outputStream;
53
54 boolean immediateFlush = true;
55
56
57
58
59
60
61 public OutputStream getOutputStream() {
62 return outputStream;
63 }
64
65
66
67
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
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
107
108
109
110
111 public void stop() {
112 if(!isStarted())
113 return;
114
115 streamWriteLock.lock();
116 try {
117 closeOutputStream();
118 super.stop();
119 } finally {
120 streamWriteLock.unlock();
121 }
122 }
123
124
125
126
127 protected void closeOutputStream() {
128 if (this.outputStream != null) {
129 try {
130
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
154
155
156
157
158
159
160
161 public void setOutputStream(OutputStream outputStream) {
162 streamWriteLock.lock();
163 try {
164
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 streamWriteLock.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 streamWriteLock.lock();
201 try {
202 writeByteArrayToOutputStreamWithPossibleFlush(byteArray);
203 } finally {
204 streamWriteLock.unlock();
205 }
206 }
207
208
209
210
211
212
213 protected final void writeByteArrayToOutputStreamWithPossibleFlush(byte[] byteArray) throws IOException {
214 this.outputStream.write(byteArray);
215 if (immediateFlush) {
216 this.outputStream.flush();
217 }
218 }
219
220
221
222
223
224
225
226
227
228 protected void subAppend(E event) {
229 if (!isStarted()) {
230 return;
231 }
232 try {
233
234 if (event instanceof DeferredProcessingAware) {
235 ((DeferredProcessingAware) event).prepareForDeferredProcessing();
236 }
237 writeOut(event);
238
239 } catch (IOException ioe) {
240
241
242 this.started = false;
243 addStatus(new ErrorStatus("IO failure in appender", this, ioe));
244 }
245 }
246
247 public Encoder<E> getEncoder() {
248 return encoder;
249 }
250
251 public void setEncoder(Encoder<E> encoder) {
252 this.encoder = encoder;
253 }
254
255 public boolean isImmediateFlush() {
256 return immediateFlush;
257 }
258
259 public void setImmediateFlush(boolean immediateFlush) {
260 this.immediateFlush = immediateFlush;
261 }
262
263 }