1
14 package net.logstash.logback.encoder;
15
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.OutputStream;
19 import java.nio.charset.Charset;
20
21 import com.fasterxml.jackson.databind.annotation.JsonValueInstantiator;
22
23 import net.logstash.logback.Logback11Support;
24 import net.logstash.logback.composite.CompositeJsonFormatter;
25 import net.logstash.logback.composite.JsonProviders;
26 import net.logstash.logback.decorate.JsonFactoryDecorator;
27 import net.logstash.logback.decorate.JsonGeneratorDecorator;
28 import ch.qos.logback.core.encoder.Encoder;
29 import ch.qos.logback.core.encoder.EncoderBase;
30 import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
31 import ch.qos.logback.core.pattern.PatternLayoutBase;
32 import ch.qos.logback.core.spi.DeferredProcessingAware;
33
34 public abstract class CompositeJsonEncoder<Event extends DeferredProcessingAware>
35 extends EncoderBase<Event> {
36
37 private static final byte[] EMPTY_BYTES = new byte[0];
38
39
43 private boolean logback11ImmediateFlush = true;
44
48 private OutputStream logback11OutputStream;
49
50
57 private int minBufferSize = 1024;
58
59 private Encoder<Event> prefix;
60 private Encoder<Event> suffix;
61
62 private final CompositeJsonFormatter<Event> formatter;
63
64 private String lineSeparator = System.lineSeparator();
65
66 private byte[] lineSeparatorBytes;
67
68 private Charset charset;
69
70
74 private Logback11Support logback11Support = Logback11Support.INSTANCE;
75
76 public CompositeJsonEncoder() {
77 super();
78 this.formatter = createFormatter();
79 }
80
81 protected abstract CompositeJsonFormatter<Event> createFormatter();
82
83
92 public void init(OutputStream outputStream) throws IOException {
93 logback11Support.verifyLogback11OrBefore();
94 this.logback11OutputStream = outputStream;
95 initWrapped(prefix, outputStream);
96 initWrapped(suffix, outputStream);
97 }
98
99 private void initWrapped(Encoder<Event> wrapped, OutputStream outputStream) throws IOException {
100 if (wrapped != null) {
101 logback11Support.init(wrapped, outputStream);
102 }
103 }
104
105 @Override
106 public byte[] encode(Event event) {
107 logback11Support.verifyLogback12OrAfter();
108
109 byte[] prefixBytes = doEncodeWrappedToBytes(prefix, event);
110 byte[] suffixBytes = doEncodeWrappedToBytes(suffix, event);
111
112 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(
113 minBufferSize
114 + (prefixBytes == null ? 0 : prefixBytes.length)
115 + (suffixBytes == null ? 0 : suffixBytes.length)
116 + lineSeparatorBytes.length);
117 try {
118 if (prefixBytes != null) {
119 outputStream.write(prefixBytes);
120 }
121
122 formatter.writeEventToOutputStream(event, outputStream);
123
124 if (suffixBytes != null) {
125 outputStream.write(suffixBytes);
126 }
127
128 outputStream.write(lineSeparatorBytes);
129
130 return outputStream.toByteArray();
131 } catch (IOException e) {
132 addWarn("Error encountered while encoding log event. "
133 + "Event: " + event, e);
134 return EMPTY_BYTES;
135 } finally {
136 try {
137 outputStream.close();
138 } catch (IOException e) {
139 throw new RuntimeException(e);
140 }
141 }
142 }
143
144
157 @SuppressWarnings("unchecked")
158 public void doEncode(Object event) throws IOException {
159 doEncode((Event) event);
160 }
161
162
172 public void doEncode(Event event) throws IOException {
173 logback11Support.verifyLogback11OrBefore();
174 try {
175 doEncodeWrappedToOutputStream(prefix, event);
176
177 formatter.writeEventToOutputStream(event, logback11OutputStream);
178
179 doEncodeWrappedToOutputStream(suffix, event);
180
181 logback11OutputStream.write(lineSeparatorBytes);
182
183 if (logback11ImmediateFlush) {
184 logback11OutputStream.flush();
185 }
186
187 } catch (IOException e) {
188 addWarn("Error encountered while encoding log event. "
189 + "OutputStream is now in an unknown state, but will continue to be used for future log events."
190 + "Event: " + event, e);
191 }
192 }
193
194 private byte[] doEncodeWrappedToBytes(Encoder<Event> wrapped, Event event) {
195 if (wrapped != null) {
196 return wrapped.encode(event);
197 }
198 return EMPTY_BYTES;
199 }
200
201 private void doEncodeWrappedToOutputStream(Encoder<Event> wrapped, Event event) throws IOException {
202 if (wrapped != null) {
203 logback11Support.doEncode(wrapped, event);
204 }
205 }
206
207 @Override
208 public void start() {
209 super.start();
210 formatter.setContext(getContext());
211 formatter.start();
212 charset = Charset.forName(formatter.getEncoding());
213 lineSeparatorBytes = this.lineSeparator == null
214 ? EMPTY_BYTES
215 : this.lineSeparator.getBytes(charset);
216 startWrapped(prefix);
217 startWrapped(suffix);
218 if (logback11Support.isLogback11OrBefore()) {
219 addWarn("Logback version is prior to 1.2.0. Enabling backwards compatible encoding. Logback 1.2.1 or greater is recommended.");
220 }
221 }
222
223 @SuppressWarnings({ "unchecked", "rawtypes" })
224 private void startWrapped(Encoder<Event> wrapped) {
225 if (wrapped instanceof LayoutWrappingEncoder) {
226
232 LayoutWrappingEncoder<Event> layoutWrappedEncoder = (LayoutWrappingEncoder<Event>) wrapped;
233 layoutWrappedEncoder.setCharset(charset);
234
235 if (layoutWrappedEncoder.getLayout() instanceof PatternLayoutBase) {
236
240 PatternLayoutBase layout = (PatternLayoutBase) layoutWrappedEncoder.getLayout();
241 layout.setPostCompileProcessor(null);
242
247 layout.start();
248 }
249 }
250
251 if (wrapped != null && !wrapped.isStarted()) {
252 wrapped.start();
253 }
254 }
255
256 @Override
257 public void stop() {
258 super.stop();
259 formatter.stop();
260 stopWrapped(prefix);
261 stopWrapped(suffix);
262 }
263
264 private void stopWrapped(Encoder<Event> wrapped) {
265 if (wrapped != null && !wrapped.isStarted()) {
266 wrapped.stop();
267 }
268 }
269
270
280 public void close() throws IOException {
281 logback11Support.verifyLogback11OrBefore();
282 closeWrapped(prefix);
283 closeWrapped(suffix);
284 }
285
286 private void closeWrapped(Encoder<Event> wrapped) throws IOException {
287 if (wrapped != null && !wrapped.isStarted()) {
288 logback11Support.close(wrapped);
289 }
290 }
291
292
295 protected void setLogback11Support(Logback11Support logback11Support) {
296 this.logback11Support = logback11Support;
297 }
298
299 @Override
300 public byte[] headerBytes() {
301 return EMPTY_BYTES;
302 }
303
304 @Override
305 public byte[] footerBytes() {
306 return EMPTY_BYTES;
307 }
308
309 public JsonProviders<Event> getProviders() {
310 return formatter.getProviders();
311 }
312
313 public void setProviders(JsonProviders<Event> jsonProviders) {
314 formatter.setProviders(jsonProviders);
315 }
316
317 public boolean isImmediateFlush() {
318 return logback11ImmediateFlush;
319 }
320
321 public void setImmediateFlush(boolean immediateFlush) {
322 this.logback11ImmediateFlush = immediateFlush;
323 }
324
325 public JsonFactoryDecorator getJsonFactoryDecorator() {
326 return formatter.getJsonFactoryDecorator();
327 }
328
329 public void setJsonFactoryDecorator(JsonFactoryDecorator jsonFactoryDecorator) {
330 formatter.setJsonFactoryDecorator(jsonFactoryDecorator);
331 }
332
333 public JsonGeneratorDecorator getJsonGeneratorDecorator() {
334 return formatter.getJsonGeneratorDecorator();
335 }
336
337 public String getEncoding() {
338 return formatter.getEncoding();
339 }
340
341
345 public void setEncoding(String encodingName) {
346 formatter.setEncoding(encodingName);
347 }
348
349 public void setFindAndRegisterJacksonModules(boolean findAndRegisterJacksonModules) {
350 formatter.setFindAndRegisterJacksonModules(findAndRegisterJacksonModules);
351 }
352
353 public void setJsonGeneratorDecorator(JsonGeneratorDecorator jsonGeneratorDecorator) {
354 formatter.setJsonGeneratorDecorator(jsonGeneratorDecorator);
355 }
356
357 public String getLineSeparator() {
358 return lineSeparator;
359 }
360
361
375 public void setLineSeparator(String lineSeparator) {
376 this.lineSeparator = SeparatorParser.parseSeparator(lineSeparator);
377 }
378
379 public int getMinBufferSize() {
380 return minBufferSize;
381 }
382
389 public void setMinBufferSize(int minBufferSize) {
390 this.minBufferSize = minBufferSize;
391 }
392
393 protected CompositeJsonFormatter<Event> getFormatter() {
394 return formatter;
395 }
396
397 public Encoder<Event> getPrefix() {
398 return prefix;
399 }
400 public void setPrefix(Encoder<Event> prefix) {
401 this.prefix = prefix;
402 }
403
404 public Encoder<Event> getSuffix() {
405 return suffix;
406 }
407 public void setSuffix(Encoder<Event> suffix) {
408 this.suffix = suffix;
409 }
410
411 }
412