net.logstash.logback.pattern.AbstractJsonPatternParser Maven / Gradle / Ivy
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.logstash.logback.pattern;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.juli.logging.ch.qos.logback.core.pattern.PatternLayoutBase;
import org.apache.juli.logging.ch.qos.logback.core.spi.ContextAware;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
/**
* Parser that takes a JSON pattern, resolves all the conversion specifiers and returns an instance
* of NodeWriter that, when its write() method is invoked, produces JSON defined by the parsed pattern.
*
* @param - type of the event (ILoggingEvent, IAccessEvent)
*
* @author Dmitry Andrianov
*/
public abstract class AbstractJsonPatternParser {
public static final Pattern OPERATION_PATTERN = Pattern.compile("\\# (\\w+) (?: \\{ (.*) \\} )?", Pattern.COMMENTS);
private final ContextAware contextAware;
private final JsonFactory jsonFactory;
private final Map operations = new HashMap();
public AbstractJsonPatternParser(final ContextAware contextAware, final JsonFactory jsonFactory) {
this.contextAware = contextAware;
this.jsonFactory = jsonFactory;
addOperation(new AsLongOperation());
addOperation(new AsDoubleOperation());
addOperation(new AsJsonOperation());
}
protected void addOperation(Operation operation) {
this.operations.put(operation.getName(), operation);
}
protected abstract class Operation {
private final String name;
private final boolean requiresData;
public Operation(String name, boolean requiresData) {
this.name = name;
this.requiresData = requiresData;
}
public String getName() {
return name;
}
public boolean requiresData() {
return requiresData;
}
public abstract ValueGetter, Event> createValueGetter(String data);
}
protected class AsLongOperation extends Operation {
public AsLongOperation() {
super("asLong", true);
}
@Override
public ValueGetter, Event> createValueGetter(String data) {
return new AsLongValueTransformer(makeLayoutValueGetter(data));
}
}
protected class AsDoubleOperation extends Operation {
public AsDoubleOperation() {
super("asDouble", true);
}
@Override
public ValueGetter, Event> createValueGetter(String data) {
return new AsDoubleValueTransformer(makeLayoutValueGetter(data));
}
}
protected class AsJsonOperation extends Operation {
public AsJsonOperation() {
super("asJson", true);
}
@Override
public ValueGetter, Event> createValueGetter(String data) {
return new AsJsonValueTransformer(makeLayoutValueGetter(data));
}
}
protected static class LayoutValueGetter implements ValueGetter {
private final PatternLayoutBase layout;
LayoutValueGetter(final PatternLayoutBase layout) {
this.layout = layout;
}
@Override
public String getValue(final Event event) {
return layout.doLayout(event);
}
}
protected static abstract class AbstractAsObjectTransformer implements ValueGetter {
private final ValueGetter generator;
AbstractAsObjectTransformer(final ValueGetter generator) {
this.generator = generator;
}
@Override
public T getValue(final Event event) {
final String value = generator.getValue(event);
if (value == null || value.isEmpty()) {
return null;
}
try {
return transform(value);
} catch (Exception e) {
return null;
}
}
abstract protected T transform(final String value) throws NumberFormatException, IOException;
}
protected static abstract class AbstractAsNumberTransformer implements ValueGetter {
private final ValueGetter generator;
AbstractAsNumberTransformer(final ValueGetter generator) {
this.generator = generator;
}
@Override
public T getValue(final Event event) {
final String value = generator.getValue(event);
if (value == null || value.isEmpty()) {
return null;
}
try {
return transform(value);
} catch (NumberFormatException e) {
return null;
}
}
abstract protected T transform(final String value) throws NumberFormatException;
}
protected static class AsLongValueTransformer extends AbstractAsNumberTransformer {
public AsLongValueTransformer(final ValueGetter generator) {
super(generator);
}
protected Long transform(final String value) throws NumberFormatException {
return Long.parseLong(value);
}
}
protected static class AsDoubleValueTransformer extends AbstractAsNumberTransformer {
public AsDoubleValueTransformer(final ValueGetter generator) {
super(generator);
}
protected Double transform(final String value) throws NumberFormatException {
return Double.parseDouble(value);
}
}
protected class AsJsonValueTransformer extends AbstractAsObjectTransformer {
public AsJsonValueTransformer(final ValueGetter generator) {
super(generator);
}
protected JsonNode transform(final String value) throws IOException {
return jsonFactory.getCodec().readTree(jsonFactory.createParser(value));
}
}
protected static interface FieldWriter extends NodeWriter {
}
protected static class ConstantValueWriter implements NodeWriter {
private final Object value;
public ConstantValueWriter(final Object value) {
this.value = value;
}
public void write(JsonGenerator generator, Event event) throws IOException {
generator.writeObject(value);
}
}
protected static class ListWriter implements NodeWriter {
private final List> items;
public ListWriter(final List> items) {
this.items = items;
}
public void write(JsonGenerator generator, Event event) throws IOException {
generator.writeStartArray();
for (NodeWriter item : items) {
item.write(generator, event);
}
generator.writeEndArray();
}
}
protected static class ComputableValueWriter implements NodeWriter {
private final ValueGetter, Event> getter;
public ComputableValueWriter(final ValueGetter, Event> getter) {
this.getter = getter;
}
public void write(JsonGenerator generator, Event event) throws IOException {
Object value = getter.getValue(event);
generator.writeObject(value);
}
}
protected static class DelegatingObjectFieldWriter implements FieldWriter {
private final String name;
private final NodeWriter delegate;
public DelegatingObjectFieldWriter(final String name, final NodeWriter delegate) {
this.name = name;
this.delegate = delegate;
}
public void write(JsonGenerator generator, Event event) throws IOException {
generator.writeFieldName(name);
delegate.write(generator, event);
}
}
protected static class ComputableObjectFieldWriter implements FieldWriter {
private final String name;
private final ValueGetter, Event> getter;
public ComputableObjectFieldWriter(final String name, final ValueGetter, Event> getter) {
this.name = name;
this.getter = getter;
}
public void write(JsonGenerator generator, Event event) throws IOException {
Object value = getter.getValue(event);
generator.writeFieldName(name);
generator.writeObject(value);
}
}
protected static class ObjectWriter implements NodeWriter {
private final ChildrenWriter childrenWriter;
public ObjectWriter(ChildrenWriter childrenWriter) {
this.childrenWriter = childrenWriter;
}
public void write(JsonGenerator generator, Event event) throws IOException {
generator.writeStartObject();
this.childrenWriter.write(generator, event);
generator.writeEndObject();
}
}
protected static class ChildrenWriter implements NodeWriter {
private final List> items;
public ChildrenWriter(final List> items) {
this.items = items;
}
public void write(JsonGenerator generator, Event event) throws IOException {
for (FieldWriter item : items) {
item.write(generator, event);
}
}
}
protected PatternLayoutBase buildLayout(String format) {
PatternLayoutBase layout = createLayout();
layout.setContext(contextAware.getContext());
layout.setPattern(format);
layout.setPostCompileProcessor(null); // Remove EnsureLineSeparation which is there by default
layout.start();
return layout;
}
protected abstract PatternLayoutBase createLayout();
private ValueGetter, Event> makeComputableValueGetter(String pattern) {
Matcher matcher = OPERATION_PATTERN.matcher(pattern);
if (matcher.matches()) {
String operationName = matcher.group(1);
String operationData = matcher.groupCount() > 1
? matcher.group(2)
: null;
Operation operation = this.operations.get(operationName);
if (operation != null) {
if (operation.requiresData() && operationData == null) {
contextAware.addError("No parameter provided to operation: " + operation.getName());
} else {
return operation.createValueGetter(operationData);
}
}
}
return makeLayoutValueGetter(pattern);
}
protected LayoutValueGetter makeLayoutValueGetter(final String data) {
return new LayoutValueGetter(buildLayout(data));
}
private NodeWriter parseValue(JsonNode node) {
if (node.isTextual()) {
ValueGetter, Event> getter = makeComputableValueGetter(node.asText());
return new ComputableValueWriter(getter);
} else if (node.isArray()) {
return parseArray(node);
} else if (node.isObject()) {
return parseObject(node);
} else {
// Anything else, we will be just writing as is (nulls, numbers, booleans and whatnot)
return new ConstantValueWriter(node);
}
}
private ListWriter parseArray(JsonNode node) {
List> children = new ArrayList>();
for (JsonNode item : node) {
children.add(parseValue(item));
}
return new ListWriter(children);
}
private ObjectWriter parseObject(JsonNode node) {
return new ObjectWriter(parseChildren(node));
}
private ChildrenWriter parseChildren(JsonNode node) {
List> children = new ArrayList>();
for (Iterator> nodeFields = node.fields(); nodeFields.hasNext(); ) {
Map.Entry field = nodeFields.next();
String key = field.getKey();
JsonNode value = field.getValue();
if (value.isTextual()) {
ValueGetter, Event> getter = makeComputableValueGetter(value.asText());
children.add(new ComputableObjectFieldWriter(key, getter));
} else {
children.add(new DelegatingObjectFieldWriter(key, parseValue(value)));
}
}
return new ChildrenWriter(children);
}
public NodeWriter parse(String pattern) {
if (pattern == null) {
contextAware.addError("No pattern specified");
return null;
}
JsonNode node;
try {
node = jsonFactory.createParser(pattern).readValueAsTree();
} catch (IOException e) {
contextAware.addError("Failed to parse pattern [" + pattern + "]", e);
return null;
}
if (node == null) {
contextAware.addError("Empty JSON pattern");
return null;
}
if (!node.isObject()) {
contextAware.addError("Invalid pattern JSON - must be an object");
return null;
}
return parseChildren(node);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy