Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.vlkan.log4j2.logstash.layout.resolver.TemplateResolvers Maven / Gradle / Ivy
/*
* Copyright 2017-2020 Volkan Yazıcı
*
* 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 permits and
* limitations under the License.
*/
package com.vlkan.log4j2.logstash.layout.resolver;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.util.KeyValuePair;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public enum TemplateResolvers {;
private static final TemplateResolver> EMPTY_ARRAY_RESOLVER = (TemplateResolver) (ignored, jsonGenerator) -> {
jsonGenerator.writeStartArray();
jsonGenerator.writeEndArray();
};
private static final TemplateResolver> EMPTY_OBJECT_RESOLVER = (TemplateResolver) (ignored, jsonGenerator) -> {
jsonGenerator.writeStartObject();
jsonGenerator.writeEndObject();
};
private static final TemplateResolver> NULL_NODE_RESOLVER = (TemplateResolver) (ignored, jsonGenerator) -> jsonGenerator.writeNull();
public static > TemplateResolver ofTemplate(C context, String template) {
// Read the template.
ObjectNode node;
try {
node = context.getObjectMapper().readValue(template, ObjectNode.class);
} catch (IOException error) {
String message = String.format("failed parsing template (template=%s)", template);
throw new RuntimeException(message, error);
}
// Append the additional fields.
if (context instanceof EventResolverContext) {
EventResolverContext eventResolverContext = (EventResolverContext) context;
KeyValuePair[] additionalFields = eventResolverContext.getAdditionalFields();
if (additionalFields != null) {
for (KeyValuePair additionalField : additionalFields) {
node.put(additionalField.getKey(), additionalField.getValue());
}
}
}
// Resolve the template.
return ofNode(context, node);
}
private static > TemplateResolver ofNode(C context, JsonNode node) {
// Check for known types.
JsonNodeType nodeType = node.getNodeType();
switch (nodeType) {
case ARRAY: return ofArrayNode(context, node);
case OBJECT: return ofObjectNode(context, node);
case STRING: return ofStringNode(context, node);
}
// Create constant resolver for the JSON.
return (ignored, jsonGenerator) -> jsonGenerator.writeTree(node);
}
private static > TemplateResolver ofArrayNode(C context, JsonNode arrayNode) {
// Create resolver for each children.
List> itemResolvers = new ArrayList<>();
for (int itemIndex = 0; itemIndex < arrayNode.size(); itemIndex++) {
JsonNode itemNode = arrayNode.get(itemIndex);
TemplateResolver itemResolver = ofNode(context, itemNode);
itemResolvers.add(itemResolver);
}
// Short-circuit if the array is empty.
if (itemResolvers.isEmpty()) {
@SuppressWarnings("unchecked") TemplateResolver emptyArrayResolver = (TemplateResolver) EMPTY_ARRAY_RESOLVER;
return emptyArrayResolver;
}
// Create a parent resolver collecting each child resolver execution.
return (value, jsonGenerator) -> {
jsonGenerator.writeStartArray();
// noinspection ForLoopReplaceableByForEach (avoid iterator instantiation)
for (int itemResolverIndex = 0; itemResolverIndex < itemResolvers.size(); itemResolverIndex++) {
TemplateResolver itemResolver = itemResolvers.get(itemResolverIndex);
itemResolver.resolve(value, jsonGenerator);
}
jsonGenerator.writeEndArray();
};
}
private static > TemplateResolver ofObjectNode(C context, JsonNode srcNode) {
// Create resolver for each object field.
List fieldNames = new ArrayList<>();
List> fieldResolvers = new ArrayList<>();
Iterator> srcNodeFieldIterator = srcNode.fields();
while (srcNodeFieldIterator.hasNext()) {
Map.Entry srcNodeField = srcNodeFieldIterator.next();
String fieldName = srcNodeField.getKey();
JsonNode fieldValue = srcNodeField.getValue();
TemplateResolver fieldResolver = ofNode(context, fieldValue);
fieldNames.add(fieldName);
fieldResolvers.add(fieldResolver);
}
// Short-circuit if the object is empty.
int fieldCount = fieldNames.size();
if (fieldCount == 0) {
@SuppressWarnings("unchecked") TemplateResolver emptyObjectResolver = (TemplateResolver) EMPTY_OBJECT_RESOLVER;
return emptyObjectResolver;
}
// Create a parent resolver collecting each object field resolver execution.
return (value, jsonGenerator) -> {
jsonGenerator.writeStartObject();
for (int fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) {
String fieldName = fieldNames.get(fieldIndex);
TemplateResolver fieldResolver = fieldResolvers.get(fieldIndex);
jsonGenerator.writeFieldName(fieldName);
fieldResolver.resolve(value, jsonGenerator);
}
jsonGenerator.writeEndObject();
};
}
private static > TemplateResolver ofStringNode(C context, JsonNode textNode) {
// Short-circuit if content is blank and not allowed.
String fieldValue = textNode.asText();
if (context.isEmptyPropertyExclusionEnabled() && StringUtils.isEmpty(fieldValue)) {
@SuppressWarnings("unchecked") TemplateResolver nullNodeResolver = (TemplateResolver) NULL_NODE_RESOLVER;
return nullNodeResolver;
}
// Try to resolve the directive as a ${json:xxx} parameter.
TemplateResolverRequest resolverRequest = readResolverRequest(fieldValue);
if (resolverRequest != null) {
TemplateResolverFactory> resolverFactory =
context.getResolverFactoryByName().get(resolverRequest.resolverName);
if (resolverFactory != null) {
return resolverFactory.create(context, resolverRequest.resolverKey);
}
}
// The rest is the fallback template resolver that delegates every other substitution to Log4j. This will be the
// case for every template value that does not use directives of pattern ${json:xxx}. This additionally serves
// as a mechanism to resolve values at runtime when this library misses certain resolvers.
// Check if substitution needed at all. (Copied logic from AbstractJacksonLayout.valueNeedsLookup() method.)
boolean substitutionNeeded = fieldValue.contains("${");
if (substitutionNeeded) {
if (EventResolverContext.class.isAssignableFrom(context.getContextClass())) {
// Use Log4j substitutor with LogEvent.
return (value, jsonGenerator) -> {
LogEvent logEvent = (LogEvent) value;
String replacedText = context.getSubstitutor().replace(logEvent, fieldValue);
boolean replacedTextExcluded = context.isEmptyPropertyExclusionEnabled() && StringUtils.isEmpty(replacedText);
if (replacedTextExcluded) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeString(replacedText);
}
};
} else {
// Use standalone Log4j substitutor.
return (value, jsonGenerator) -> {
String replacedText = context.getSubstitutor().replace(null, fieldValue);
boolean replacedTextExcluded = context.isEmptyPropertyExclusionEnabled() && StringUtils.isEmpty(replacedText);
if (replacedTextExcluded) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeString(replacedText);
}
};
}
} else {
// Write the field value as is. (Blank value check has already been done at the top.)
return (value, jsonGenerator) -> jsonGenerator.writeString(fieldValue);
}
}
private static TemplateResolverRequest readResolverRequest(String fieldValue) {
// Bail-out if cannot spot the template signature.
if (!fieldValue.startsWith("${json:") || !fieldValue.endsWith("}")) {
return null;
}
// Try to read both resolver name and key.
int resolverNameStartIndex = 7;
int fieldNameSeparatorIndex = fieldValue.indexOf(':', resolverNameStartIndex);
if (fieldNameSeparatorIndex < 0) {
int resolverNameEndIndex = fieldValue.length() - 1;
String resolverName = fieldValue.substring(resolverNameStartIndex, resolverNameEndIndex);
return new TemplateResolverRequest(resolverName, null);
} else {
@SuppressWarnings("UnnecessaryLocalVariable")
int resolverNameEndIndex = fieldNameSeparatorIndex;
int resolverKeyStartIndex = fieldNameSeparatorIndex + 1;
int resolverKeyEndIndex = fieldValue.length() - 1;
String resolverName = fieldValue.substring(resolverNameStartIndex, resolverNameEndIndex);
String resolverKey = fieldValue.substring(resolverKeyStartIndex, resolverKeyEndIndex);
return new TemplateResolverRequest(resolverName, resolverKey);
}
}
private static class TemplateResolverRequest {
private final String resolverName;
private final String resolverKey;
private TemplateResolverRequest(String resolverName, String resolverKey) {
this.resolverName = resolverName;
this.resolverKey = resolverKey;
}
}
}