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