1
14 package net.logstash.logback.pattern;
15
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25
26 import ch.qos.logback.core.pattern.PatternLayoutBase;
27 import ch.qos.logback.core.spi.ContextAware;
28
29 import com.fasterxml.jackson.core.JsonFactory;
30 import com.fasterxml.jackson.core.JsonGenerator;
31 import com.fasterxml.jackson.core.JsonParseException;
32 import com.fasterxml.jackson.core.JsonParser;
33 import com.fasterxml.jackson.core.TreeNode;
34 import com.fasterxml.jackson.databind.JsonNode;
35 import com.fasterxml.jackson.databind.node.JsonNodeType;
36
37
45 public abstract class AbstractJsonPatternParser<Event> {
46
47 public static final Pattern OPERATION_PATTERN = Pattern.compile("\\# (\\w+) (?: \\{ (.*) \\} )?", Pattern.COMMENTS);
48
49 private final ContextAware contextAware;
50 private final JsonFactory jsonFactory;
51
52 private final Map<String, Operation> operations = new HashMap<>();
53
54
58 private boolean omitEmptyFields;
59
60 public AbstractJsonPatternParser(final ContextAware contextAware, final JsonFactory jsonFactory) {
61 this.contextAware = contextAware;
62 this.jsonFactory = jsonFactory;
63 addOperation(new AsLongOperation());
64 addOperation(new AsDoubleOperation());
65 addOperation(new AsJsonOperation());
66 addOperation(new TryJsonOperation());
67 }
68
69 protected void addOperation(Operation operation) {
70 this.operations.put(operation.getName(), operation);
71 }
72
73 protected abstract class Operation {
74 private final String name;
75 private final boolean requiresData;
76
77 public Operation(String name, boolean requiresData) {
78 this.name = name;
79 this.requiresData = requiresData;
80 }
81
82 public String getName() {
83 return name;
84 }
85
86 public boolean requiresData() {
87 return requiresData;
88 }
89
90 public abstract ValueGetter<?, Event> createValueGetter(String data);
91
92 }
93
94 protected class AsLongOperation extends Operation {
95
96 public AsLongOperation() {
97 super("asLong", true);
98 }
99
100 @Override
101 public ValueGetter<?, Event> createValueGetter(String data) {
102 return new AsLongValueTransformer<>(makeLayoutValueGetter(data));
103 }
104 }
105
106 protected class AsDoubleOperation extends Operation {
107
108 public AsDoubleOperation() {
109 super("asDouble", true);
110 }
111
112 @Override
113 public ValueGetter<?, Event> createValueGetter(String data) {
114 return new AsDoubleValueTransformer<>(makeLayoutValueGetter(data));
115 }
116 }
117
118 protected class AsJsonOperation extends Operation {
119
120 public AsJsonOperation() {
121 super("asJson", true);
122 }
123
124 @Override
125 public ValueGetter<?, Event> createValueGetter(String data) {
126 return new AsJsonValueTransformer(makeLayoutValueGetter(data));
127 }
128 }
129
130 protected class TryJsonOperation extends Operation {
131
132 public TryJsonOperation() {
133 super("tryJson", true);
134 }
135
136 @Override
137 public ValueGetter<?, Event> createValueGetter(String data) {
138 return new TryJsonValueTransformer(makeLayoutValueGetter(data));
139 }
140 }
141
142 protected static class LayoutValueGetter<Event> implements ValueGetter<String, Event> {
143
144 private final PatternLayoutBase<Event> layout;
145
146 LayoutValueGetter(final PatternLayoutBase<Event> layout) {
147 this.layout = layout;
148 }
149
150 @Override
151 public String getValue(final Event event) {
152 return layout.doLayout(event);
153 }
154 }
155
156 protected static abstract class AbstractAsObjectTransformer<T, Event> implements ValueGetter<T, Event> {
157
158 private final ValueGetter<String, Event> generator;
159
160 AbstractAsObjectTransformer(final ValueGetter<String, Event> generator) {
161 this.generator = generator;
162 }
163
164 @Override
165 public T getValue(final Event event) {
166 final String value = generator.getValue(event);
167 if (value == null || value.isEmpty()) {
168 return null;
169 }
170 try {
171 return transform(value);
172 } catch (Exception e) {
173 return null;
174 }
175 }
176
177 abstract protected T transform(final String value) throws NumberFormatException, IOException;
178 }
179
180 protected static abstract class AbstractAsNumberTransformer<T extends Number, Event> implements ValueGetter<T, Event> {
181
182 private final ValueGetter<String, Event> generator;
183
184 AbstractAsNumberTransformer(final ValueGetter<String, Event> generator) {
185 this.generator = generator;
186 }
187
188 @Override
189 public T getValue(final Event event) {
190 final String value = generator.getValue(event);
191 if (value == null || value.isEmpty()) {
192 return null;
193 }
194 try {
195 return transform(value);
196 } catch (NumberFormatException e) {
197 return null;
198 }
199 }
200
201 abstract protected T transform(final String value) throws NumberFormatException;
202 }
203
204 protected static class AsLongValueTransformer<Event> extends AbstractAsNumberTransformer<Long, Event> {
205 public AsLongValueTransformer(final ValueGetter<String, Event> generator) {
206 super(generator);
207 }
208
209 protected Long transform(final String value) throws NumberFormatException {
210 return Long.parseLong(value);
211 }
212 }
213
214 protected static class AsDoubleValueTransformer<Event> extends AbstractAsNumberTransformer<Double, Event> {
215 public AsDoubleValueTransformer(final ValueGetter<String, Event> generator) {
216 super(generator);
217 }
218
219 protected Double transform(final String value) throws NumberFormatException {
220 return Double.parseDouble(value);
221 }
222 }
223
224 protected class AsJsonValueTransformer extends AbstractAsObjectTransformer<JsonNode, Event> {
225
226 public AsJsonValueTransformer(final ValueGetter<String, Event> generator) {
227 super(generator);
228 }
229
230 protected JsonNode transform(final String value) throws IOException {
231 return jsonFactory.getCodec().readTree(jsonFactory.createParser(value));
232 }
233 }
234
235 protected class TryJsonValueTransformer extends AbstractAsObjectTransformer<Object, Event> {
236
237 public TryJsonValueTransformer(final ValueGetter<String, Event> generator) {
238 super(generator);
239 }
240
241 protected Object transform(final String value) throws IOException {
242 try {
243 final String trimmedValue = value.trim();
244 final JsonParser parser = jsonFactory.createParser(trimmedValue);
245 final TreeNode tree = jsonFactory.getCodec().readTree(parser);
246 if (parser.getCurrentLocation().getCharOffset() < trimmedValue.length()) {
247
252 return value;
253 }
254 return tree;
255 } catch (JsonParseException e) {
256 return value;
257 }
258 }
259 }
260
261 protected interface FieldWriter<Event> extends NodeWriter<Event> {
262 }
263
264 protected class ConstantValueWriter implements NodeWriter<Event> {
265 private final Object value;
266
267 public ConstantValueWriter(final Object value) {
268 this.value = value;
269 }
270
271 public void write(JsonGenerator generator, Event event) throws IOException {
272 generator.writeObject(value);
273 }
274
275 @Override
276 public boolean shouldWrite(JsonGenerator generator, Event event) {
277 return !omitEmptyFields
278 || (value != null
279 && !(value instanceof JsonNode && ((JsonNode) value).getNodeType() == JsonNodeType.NULL));
280 }
281 }
282
283 protected class ListWriter<Event> implements NodeWriter<Event> {
284 private final List<NodeWriter<Event>> items;
285
286 public ListWriter(final List<NodeWriter<Event>> items) {
287 this.items = items;
288 }
289
290 public void write(JsonGenerator generator, Event event) throws IOException {
291 generator.writeStartArray();
292 for (NodeWriter<Event> item : items) {
293 if (item.shouldWrite(generator, event)) {
294 item.write(generator, event);
295 }
296 }
297 generator.writeEndArray();
298 }
299
300 @Override
301 public boolean shouldWrite(JsonGenerator generator, Event event) {
302 if (!omitEmptyFields) {
303 return true;
304 }
305
306 for (NodeWriter<Event> item : items) {
307 if (item.shouldWrite(generator, event)) {
308 return true;
309 }
310 }
311
312 return false;
313 }
314 }
315
316 protected class ComputableValueWriter<Event> implements NodeWriter<Event> {
317
318 private final ValueGetter<?, Event> getter;
319
320 public ComputableValueWriter(final ValueGetter<?, Event> getter) {
321 this.getter = getter;
322 }
323
324 public void write(JsonGenerator generator, Event event) throws IOException {
325 Object value = getter.getValue(event);
326 generator.writeObject(value);
327 }
328
329 @Override
330 public boolean shouldWrite(JsonGenerator generator, Event event) {
331 return !omitEmptyFields || getter.getValue(event) != null;
332 }
333 }
334
335 protected class DelegatingObjectFieldWriter<Event> implements FieldWriter<Event> {
336
337 private final String name;
338 private final NodeWriter<Event> delegate;
339
340 public DelegatingObjectFieldWriter(final String name, final NodeWriter<Event> delegate) {
341 this.name = name;
342 this.delegate = delegate;
343 }
344
345 public void write(JsonGenerator generator, Event event) throws IOException {
346 if (delegate.shouldWrite(generator, event)) {
347 generator.writeFieldName(name);
348 delegate.write(generator, event);
349 }
350 }
351
352 @Override
353 public boolean shouldWrite(JsonGenerator generator, Event event) {
354 return delegate.shouldWrite(generator, event);
355 }
356 }
357
358 protected class ComputableObjectFieldWriter<Event> implements FieldWriter<Event> {
359
360 private final String name;
361 private final ValueGetter<?, Event> getter;
362
363 public ComputableObjectFieldWriter(final String name, final ValueGetter<?, Event> getter) {
364 this.name = name;
365 this.getter = getter;
366 }
367
368 public void write(JsonGenerator generator, Event event) throws IOException {
369 Object value = getter.getValue(event);
370 if (!omitEmptyFields || value != null) {
371 generator.writeFieldName(name);
372 generator.writeObject(value);
373 }
374 }
375
376 @Override
377 public boolean shouldWrite(JsonGenerator generator, Event event) {
378 return !omitEmptyFields || !isEmptyValue(getter.getValue(event));
379 }
380 }
381
382 protected class ObjectWriter<Event> implements NodeWriter<Event> {
383
384 private final ChildrenWriter<Event> childrenWriter;
385
386 public ObjectWriter(ChildrenWriter<Event> childrenWriter) {
387 this.childrenWriter = childrenWriter;
388 }
389
390 public void write(JsonGenerator generator, Event event) throws IOException {
391 if (childrenWriter.shouldWrite(generator, event)) {
392 generator.writeStartObject();
393 this.childrenWriter.write(generator, event);
394 generator.writeEndObject();
395 }
396 }
397
398 @Override
399 public boolean shouldWrite(JsonGenerator generator, Event event) {
400 return childrenWriter.shouldWrite(generator, event);
401 }
402 }
403
404 protected class ChildrenWriter<Event> implements NodeWriter<Event> {
405
406 private final List<FieldWriter<Event>> items;
407
408 public ChildrenWriter(final List<FieldWriter<Event>> items) {
409 this.items = items;
410 }
411
412 public void write(JsonGenerator generator, Event event) throws IOException {
413 for (FieldWriter<Event> item : items) {
414 if (item.shouldWrite(generator, event)) {
415 item.write(generator, event);
416 }
417 }
418 }
419
420 @Override
421 public boolean shouldWrite(JsonGenerator generator, Event event) {
422 if (!omitEmptyFields) {
423 return true;
424 }
425
426 for (FieldWriter<Event> item : items) {
427 if (item.shouldWrite(generator, event)) {
428 return true;
429 }
430 }
431 return false;
432 }
433 }
434
435 protected PatternLayoutBase<Event> buildLayout(String format) {
436 PatternLayoutBase<Event> layout = createLayout();
437 layout.setContext(contextAware.getContext());
438 layout.setPattern(format);
439 layout.setPostCompileProcessor(null);
440 layout.start();
441
442 return layout;
443 }
444
445 protected abstract PatternLayoutBase<Event> createLayout();
446
447 private ValueGetter<?, Event> makeComputableValueGetter(String pattern) {
448
449 Matcher matcher = OPERATION_PATTERN.matcher(pattern);
450
451 if (matcher.matches()) {
452 String operationName = matcher.group(1);
453 String operationData = matcher.groupCount() > 1
454 ? matcher.group(2)
455 : null;
456
457 Operation operation = this.operations.get(operationName);
458 if (operation != null) {
459 if (operation.requiresData() && operationData == null) {
460 contextAware.addError("No parameter provided to operation: " + operation.getName());
461 } else {
462 return operation.createValueGetter(operationData);
463 }
464 }
465 }
466 return makeLayoutValueGetter(pattern);
467 }
468
469 protected LayoutValueGetter<Event> makeLayoutValueGetter(final String data) {
470 return new LayoutValueGetter<Event>(buildLayout(data));
471 }
472
473 private NodeWriter<Event> parseValue(JsonNode node) {
474 if (node.isTextual()) {
475 ValueGetter<?, Event> getter = makeComputableValueGetter(node.asText());
476 return new ComputableValueWriter<Event>(getter);
477 } else if (node.isArray()) {
478 return parseArray(node);
479 } else if (node.isObject()) {
480 return parseObject(node);
481 } else {
482
483 return new ConstantValueWriter(node);
484 }
485 }
486
487 private ListWriter<Event> parseArray(JsonNode node) {
488
489 List<NodeWriter<Event>> children = new ArrayList<>();
490 for (JsonNode item : node) {
491 children.add(parseValue(item));
492 }
493
494 return new ListWriter<>(children);
495 }
496
497 private ObjectWriter<Event> parseObject(JsonNode node) {
498
499 return new ObjectWriter<>(parseChildren(node));
500 }
501
502 private ChildrenWriter<Event> parseChildren(JsonNode node) {
503 List<FieldWriter<Event>> children = new ArrayList<>();
504 for (Iterator<Map.Entry<String, JsonNode>> nodeFields = node.fields(); nodeFields.hasNext(); ) {
505 Map.Entry<String, JsonNode> field = nodeFields.next();
506
507 String key = field.getKey();
508 JsonNode value = field.getValue();
509
510 if (value.isTextual()) {
511 ValueGetter<?, Event> getter = makeComputableValueGetter(value.asText());
512 children.add(new ComputableObjectFieldWriter<>(key, getter));
513 } else {
514 children.add(new DelegatingObjectFieldWriter<>(key, parseValue(value)));
515 }
516 }
517 return new ChildrenWriter<>(children);
518 }
519
520 public NodeWriter<Event> parse(String pattern) {
521
522 if (pattern == null) {
523 contextAware.addError("No pattern specified");
524 return null;
525 }
526
527 JsonNode node;
528 try {
529 node = jsonFactory.createParser(pattern).readValueAsTree();
530 } catch (IOException e) {
531 contextAware.addError("Failed to parse pattern [" + pattern + "]", e);
532 return null;
533 }
534
535 if (node == null) {
536 contextAware.addError("Empty JSON pattern");
537 return null;
538 }
539
540 if (!node.isObject()) {
541 contextAware.addError("Invalid pattern JSON - must be an object");
542 return null;
543 }
544
545 return parseChildren(node);
546 }
547
548
553 private boolean isEmptyValue(Object value) {
554 if (value == null) {
555 return true;
556 }
557 if (value instanceof String && ((String) value).isEmpty()) {
558 return true;
559 }
560 if (value instanceof Collection && ((Collection) value).isEmpty()) {
561 return true;
562 }
563 if (value instanceof Map && ((Map) value).isEmpty()) {
564 return true;
565 }
566
567 if (value instanceof JsonNode) {
568 JsonNode node = (JsonNode) value;
569 if (node.getNodeType() == JsonNodeType.NULL) {
570 return true;
571 }
572 if (node.isTextual() && node.textValue().isEmpty()) {
573 return true;
574 }
575 if (node.isContainerNode() && node.size() == 0) {
576 return true;
577 }
578 }
579 return false;
580
581 }
582
583
587 public boolean isOmitEmptyFields() {
588 return omitEmptyFields;
589 }
590
591
595 public void setOmitEmptyFields(boolean omitEmptyFields) {
596 this.omitEmptyFields = omitEmptyFields;
597 }
598 }
599