1
14 package net.logstash.logback.composite;
15
16 import java.io.IOException;
17 import java.io.OutputStream;
18 import java.io.Writer;
19 import java.lang.ref.SoftReference;
20 import java.util.ServiceConfigurationError;
21
22 import net.logstash.logback.decorate.JsonFactoryDecorator;
23 import net.logstash.logback.decorate.JsonGeneratorDecorator;
24 import net.logstash.logback.decorate.NullJsonFactoryDecorator;
25 import net.logstash.logback.decorate.NullJsonGeneratorDecorator;
26 import ch.qos.logback.access.spi.IAccessEvent;
27 import ch.qos.logback.classic.spi.ILoggingEvent;
28 import ch.qos.logback.core.spi.ContextAware;
29 import ch.qos.logback.core.spi.ContextAwareBase;
30 import ch.qos.logback.core.spi.DeferredProcessingAware;
31 import ch.qos.logback.core.spi.LifeCycle;
32
33 import com.fasterxml.jackson.core.JsonEncoding;
34 import com.fasterxml.jackson.core.JsonFactory;
35 import com.fasterxml.jackson.core.JsonGenerator;
36 import com.fasterxml.jackson.core.io.SegmentedStringWriter;
37 import com.fasterxml.jackson.core.util.BufferRecycler;
38 import com.fasterxml.jackson.core.util.ByteArrayBuilder;
39 import com.fasterxml.jackson.databind.ObjectMapper;
40 import com.fasterxml.jackson.databind.SerializationFeature;
41
42
52 public abstract class CompositeJsonFormatter<Event extends DeferredProcessingAware>
53 extends ContextAwareBase implements LifeCycle {
54
55
60 private final ThreadLocal<SoftReference<BufferRecycler>> recycler = new ThreadLocal<SoftReference<BufferRecycler>>() {
61 protected SoftReference<BufferRecycler> initialValue() {
62 final BufferRecycler bufferRecycler = new BufferRecycler();
63 return new SoftReference<BufferRecycler>(bufferRecycler);
64 }
65 };
66
67
70 private JsonFactory jsonFactory;
71
72
76 private JsonFactoryDecorator jsonFactoryDecorator = new NullJsonFactoryDecorator();
77
78
82 private JsonGeneratorDecorator jsonGeneratorDecorator = new NullJsonGeneratorDecorator();
83
84
87 private JsonProviders<Event> jsonProviders = new JsonProviders<Event>();
88
89 private JsonEncoding encoding = JsonEncoding.UTF8;
90
91 private boolean findAndRegisterJacksonModules = true;
92
93 private volatile boolean started;
94
95 public CompositeJsonFormatter(ContextAware declaredOrigin) {
96 super(declaredOrigin);
97 }
98
99 @Override
100 public void start() {
101 if (jsonProviders.getProviders().isEmpty()) {
102 addError("No providers configured");
103 }
104 jsonFactory = createJsonFactory();
105 jsonProviders.setJsonFactory(jsonFactory);
106 jsonProviders.setContext(context);
107 jsonProviders.start();
108 started = true;
109 }
110
111 @Override
112 public void stop() {
113 jsonProviders.stop();
114 started = false;
115 }
116
117 @Override
118 public boolean isStarted() {
119 return started;
120 }
121
122 private JsonFactory createJsonFactory() {
123 ObjectMapper objectMapper = new ObjectMapper()
124
127 .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
128
129 if (findAndRegisterJacksonModules) {
130 try {
131 objectMapper.findAndRegisterModules();
132 } catch (ServiceConfigurationError serviceConfigurationError) {
133 addError("Error occurred while dynamically loading jackson modules", serviceConfigurationError);
134 }
135 }
136
137 JsonFactory jsonFactory = objectMapper
138 .getFactory()
139
151 .disable(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM);
152
153 return this.jsonFactoryDecorator.decorate(jsonFactory);
154 }
155
156 public byte[] writeEventAsBytes(Event event) throws IOException {
157 ByteArrayBuilder outputStream = new ByteArrayBuilder(getBufferRecycler());
158
159 try {
160 writeEventToOutputStream(event, outputStream);
161 outputStream.flush();
162 return outputStream.toByteArray();
163 } finally {
164 outputStream.release();
165 }
166 }
167
168 public void writeEventToOutputStream(Event event, OutputStream outputStream) throws IOException {
169 try (JsonGenerator generator = createGenerator(outputStream)) {
170 writeEventToGenerator(generator, event);
171 }
172
178 }
179
180 public String writeEventAsString(Event event) throws IOException {
181 SegmentedStringWriter writer = new SegmentedStringWriter(getBufferRecycler());
182
183 try (JsonGenerator generator = createGenerator(writer)) {
184 writeEventToGenerator(generator, event);
185 writer.flush();
186 return writer.getAndClear();
187 }
188 }
189
190 protected void writeEventToGenerator(JsonGenerator generator, Event event) throws IOException {
191 if (!isStarted()) {
192 throw new IllegalStateException("Encoding attempted before starting.");
193 }
194 generator.writeStartObject();
195 jsonProviders.writeTo(generator, event);
196 generator.writeEndObject();
197 generator.flush();
198 }
199
200 protected void prepareForDeferredProcessing(Event event) {
201 event.prepareForDeferredProcessing();
202 jsonProviders.prepareForDeferredProcessing(event);
203 }
204
205 private JsonGenerator createGenerator(OutputStream outputStream) throws IOException {
206 return this.jsonGeneratorDecorator.decorate(jsonFactory.createGenerator(outputStream, encoding));
207 }
208
209 private JsonGenerator createGenerator(Writer writer) throws IOException {
210 return this.jsonGeneratorDecorator.decorate(jsonFactory.createGenerator(writer));
211 }
212
213 private BufferRecycler getBufferRecycler() {
214 SoftReference<BufferRecycler> bufferRecyclerReference = recycler.get();
215 BufferRecycler bufferRecycler = bufferRecyclerReference.get();
216 if (bufferRecycler == null) {
217 recycler.remove();
218 return getBufferRecycler();
219 }
220 return bufferRecycler;
221 }
222
223 public JsonFactory getJsonFactory() {
224 return jsonFactory;
225 }
226
227 public JsonFactoryDecorator getJsonFactoryDecorator() {
228 return jsonFactoryDecorator;
229 }
230
231 public void setJsonFactoryDecorator(JsonFactoryDecorator jsonFactoryDecorator) {
232 this.jsonFactoryDecorator = jsonFactoryDecorator;
233 }
234
235 public JsonGeneratorDecorator getJsonGeneratorDecorator() {
236 return jsonGeneratorDecorator;
237 }
238
239 public void setJsonGeneratorDecorator(JsonGeneratorDecorator jsonGeneratorDecorator) {
240 this.jsonGeneratorDecorator = jsonGeneratorDecorator;
241 }
242
243 public JsonProviders<Event> getProviders() {
244 return jsonProviders;
245 }
246
247 public String getEncoding() {
248 return encoding.getJavaName();
249 }
250
251 public void setEncoding(String encodingName) {
252 for (JsonEncoding encoding: JsonEncoding.values()) {
253 if (encoding.getJavaName().equals(encodingName) || encoding.name().equals(encodingName)) {
254 this.encoding = encoding;
255 return;
256 }
257 }
258 throw new IllegalArgumentException("Unknown encoding " + encodingName);
259 }
260
261 public void setProviders(JsonProviders<Event> jsonProviders) {
262 this.jsonProviders = jsonProviders;
263 }
264
265 public boolean isFindAndRegisterJacksonModules() {
266 return findAndRegisterJacksonModules;
267 }
268
269 public void setFindAndRegisterJacksonModules(boolean findAndRegisterJacksonModules) {
270 this.findAndRegisterJacksonModules = findAndRegisterJacksonModules;
271 }
272 }
273