1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender;
18
19 import java.io.IOException;
20 import java.io.OutputStream;
21 import java.io.Serializable;
22 import java.nio.Buffer;
23 import java.nio.ByteBuffer;
24 import java.util.Objects;
25 import java.util.concurrent.TimeUnit;
26
27 import org.apache.logging.log4j.core.Layout;
28 import org.apache.logging.log4j.core.LoggerContext;
29 import org.apache.logging.log4j.core.layout.ByteBufferDestination;
30 import org.apache.logging.log4j.core.layout.ByteBufferDestinationHelper;
31 import org.apache.logging.log4j.core.util.Constants;
32
33
34
35
36
37 public class OutputStreamManager extends AbstractManager implements ByteBufferDestination {
38 protected final Layout<?> layout;
39 protected ByteBuffer byteBuffer;
40 private volatile OutputStream outputStream;
41 private boolean skipFooter;
42
43 protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
44 final boolean writeHeader) {
45 this(os, streamName, layout, writeHeader, Constants.ENCODER_BYTE_BUFFER_SIZE);
46 }
47
48 protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
49 final boolean writeHeader, final int bufferSize) {
50 this(os, streamName, layout, writeHeader, ByteBuffer.wrap(new byte[bufferSize]));
51 }
52
53
54
55
56
57 @Deprecated
58 protected OutputStreamManager(final OutputStream os, final String streamName, final Layout<?> layout,
59 final boolean writeHeader, final ByteBuffer byteBuffer) {
60 super(null, streamName);
61 this.outputStream = os;
62 this.layout = layout;
63 if (writeHeader && layout != null) {
64 final byte[] header = layout.getHeader();
65 if (header != null) {
66 try {
67 getOutputStream().write(header, 0, header.length);
68 } catch (final IOException e) {
69 logError("Unable to write header", e);
70 }
71 }
72 }
73 this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
74 }
75
76
77
78
79 protected OutputStreamManager(final LoggerContext loggerContext, final OutputStream os, final String streamName,
80 final boolean createOnDemand, final Layout<? extends Serializable> layout, final boolean writeHeader,
81 final ByteBuffer byteBuffer) {
82 super(loggerContext, streamName);
83 if (createOnDemand && os != null) {
84 LOGGER.error(
85 "Invalid OutputStreamManager configuration for '{}': You cannot both set the OutputStream and request on-demand.",
86 streamName);
87 }
88 this.layout = layout;
89 this.byteBuffer = Objects.requireNonNull(byteBuffer, "byteBuffer");
90 this.outputStream = os;
91 if (writeHeader && layout != null) {
92 final byte[] header = layout.getHeader();
93 if (header != null) {
94 try {
95 getOutputStream().write(header, 0, header.length);
96 } catch (final IOException e) {
97 logError("Unable to write header for " + streamName, e);
98 }
99 }
100 }
101 }
102
103
104
105
106
107
108
109
110
111
112 public static <T> OutputStreamManager getManager(final String name, final T data,
113 final ManagerFactory<? extends OutputStreamManager, T> factory) {
114 return AbstractManager.getManager(name, factory, data);
115 }
116
117 @SuppressWarnings("unused")
118 protected OutputStream createOutputStream() throws IOException {
119 throw new IllegalStateException(getClass().getCanonicalName() + " must implement createOutputStream()");
120 }
121
122
123
124
125
126 public void skipFooter(final boolean skipFooter) {
127 this.skipFooter = skipFooter;
128 }
129
130
131
132
133 @Override
134 public boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
135 writeFooter();
136 return closeOutputStream();
137 }
138
139
140
141
142 protected void writeFooter() {
143 if (layout == null || skipFooter) {
144 return;
145 }
146 final byte[] footer = layout.getFooter();
147 if (footer != null) {
148 write(footer);
149 }
150 }
151
152
153
154
155
156 public boolean isOpen() {
157 return getCount() > 0;
158 }
159
160 public boolean hasOutputStream() {
161 return outputStream != null;
162 }
163
164 protected OutputStream getOutputStream() throws IOException {
165 if (outputStream == null) {
166 outputStream = createOutputStream();
167 }
168 return outputStream;
169 }
170
171 protected void setOutputStream(final OutputStream os) {
172 final byte[] header = layout.getHeader();
173 if (header != null) {
174 try {
175 os.write(header, 0, header.length);
176 this.outputStream = os;
177 } catch (final IOException ioe) {
178 logError("Unable to write header", ioe);
179 }
180 } else {
181 this.outputStream = os;
182 }
183 }
184
185
186
187
188
189
190 protected void write(final byte[] bytes) {
191 write(bytes, 0, bytes.length, false);
192 }
193
194
195
196
197
198
199
200 protected void write(final byte[] bytes, final boolean immediateFlush) {
201 write(bytes, 0, bytes.length, immediateFlush);
202 }
203
204 @Override
205 public void writeBytes(final byte[] data, final int offset, final int length) {
206 write(data, offset, length, false);
207 }
208
209
210
211
212
213
214
215
216
217 protected void write(final byte[] bytes, final int offset, final int length) {
218 writeBytes(bytes, offset, length);
219 }
220
221
222
223
224
225
226
227
228
229
230 protected synchronized void write(final byte[] bytes, final int offset, final int length, final boolean immediateFlush) {
231 if (immediateFlush && byteBuffer.position() == 0) {
232 writeToDestination(bytes, offset, length);
233 flushDestination();
234 return;
235 }
236 if (length >= byteBuffer.capacity()) {
237
238 flush();
239 writeToDestination(bytes, offset, length);
240 } else {
241 if (length > byteBuffer.remaining()) {
242 flush();
243 }
244 byteBuffer.put(bytes, offset, length);
245 }
246 if (immediateFlush) {
247 flush();
248 }
249 }
250
251
252
253
254
255
256
257
258
259 protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) {
260 try {
261 getOutputStream().write(bytes, offset, length);
262 } catch (final IOException ex) {
263 throw new AppenderLoggingException("Error writing to stream " + getName(), ex);
264 }
265 }
266
267
268
269
270
271 protected synchronized void flushDestination() {
272 final OutputStream stream = outputStream;
273 if (stream != null) {
274 try {
275 stream.flush();
276 } catch (final IOException ex) {
277 throw new AppenderLoggingException("Error flushing stream " + getName(), ex);
278 }
279 }
280 }
281
282
283
284
285
286
287
288
289
290 protected synchronized void flushBuffer(final ByteBuffer buf) {
291 ((Buffer) buf).flip();
292 if (buf.remaining() > 0) {
293 writeToDestination(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
294 }
295 buf.clear();
296 }
297
298
299
300
301 public synchronized void flush() {
302 flushBuffer(byteBuffer);
303 flushDestination();
304 }
305
306 protected synchronized boolean closeOutputStream() {
307 flush();
308 final OutputStream stream = outputStream;
309 if (stream == null || stream == System.out || stream == System.err) {
310 return true;
311 }
312 try {
313 stream.close();
314 } catch (final IOException ex) {
315 logError("Unable to close stream", ex);
316 return false;
317 }
318 return true;
319 }
320
321
322
323
324
325
326 @Override
327 public ByteBuffer getByteBuffer() {
328 return byteBuffer;
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348 @Override
349 public ByteBuffer drain(final ByteBuffer buf) {
350 flushBuffer(buf);
351 return buf;
352 }
353
354 @Override
355 public void writeBytes(final ByteBuffer data) {
356 if (data.remaining() == 0) {
357 return;
358 }
359 synchronized (this) {
360 ByteBufferDestinationHelper.writeToUnsynchronized(data, this);
361 }
362 }
363 }