Skip to content

Create Custom Fields from MDC (Log4j) #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ public void setFieldName(String fieldName) {
}
}

public void convert(Object[] arguments, StringBuilder appendTo) {
convert(appendTo, Collections.emptyMap(), arguments);
}

public void convert(StringBuilder appendTo, Map<String, String> mdcCustomFields, Object... arguments) {
List<CustomField> customFields = getCustomFields(arguments);
if (!customFields.isEmpty() || !mdcCustomFields.isEmpty()) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.sap.hcp.cf.log4j2.converter;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.core.pattern.PatternConverter;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.util.BiConsumer;
import org.apache.logging.log4j.util.ReadOnlyStringMap;

import com.sap.hcp.cf.logging.common.converter.DefaultCustomFieldsConverter;
import com.sap.hcp.cf.logging.common.customfields.CustomField;

/**
* This is a simple {@link LogEventPatternConverter} implementation that
* converts key/value pairs stored in {@link CustomField} instances which have
* been passed as arguments.
* <p>
* We allow to types of addition to a log message, either <i>embedded</i>, i.e.
* the key/value pairs appear as a list of JSON fields in the message, or as a
* nested object where the field name has been specified as an option to this
* converter.
*/
@Plugin(name = "ArgsConverter", category = PatternConverter.CATEGORY)
@ConverterKeys({ "cf" })
public class CustomFieldsConverter extends LogEventPatternConverter {

public static final String WORD = "cf";

private final List<String> customFieldMdcKeyNames;
private DefaultCustomFieldsConverter converter = new DefaultCustomFieldsConverter();

public CustomFieldsConverter(String[] options) {
super(WORD, WORD);

customFieldMdcKeyNames = options == null ? emptyList() : unmodifiableList(asList(options));
}

public static CustomFieldsConverter newInstance(final String[] options) {
return new CustomFieldsConverter(options);
}

void setConverter(DefaultCustomFieldsConverter converter) {
this.converter = converter;
}

@Override
public void format(LogEvent event, StringBuilder appendTo) {
converter.convert(appendTo, getCustomFieldsFromMdc(event), getMessageParameters(event));
}

private Object[] getMessageParameters(LogEvent event) {
Message message = event.getMessage();
return message == null ? null : message.getParameters();
}

private Map<String, String> getCustomFieldsFromMdc(LogEvent event) {
ReadOnlyStringMap contextData = event.getContextData();
if (contextData == null || contextData.isEmpty()) {
return Collections.emptyMap();
}
CustomFieldsMdcCollector mdcCollector = new CustomFieldsMdcCollector();
contextData.forEach(mdcCollector);
return mdcCollector.getCustomFields();
}

private class CustomFieldsMdcCollector implements BiConsumer<String, String> {
private Map<String, String> customFields = new HashMap<>(customFieldMdcKeyNames.size());

@Override
public void accept(String key, String value) {
if (customFieldMdcKeyNames.contains(key)) {
customFields.put(key, value);
}
}

public Map<String, String> getCustomFields() {
return customFields;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.sap.hcp.cf.log4j2.layout;

import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;

@Plugin(name = "customField", category = Node.CATEGORY, printObject = true)
public class CustomField {

private final String key;
private final boolean retainOriginal;

private CustomField(Builder builder) {
this.key = builder.key;
this.retainOriginal = builder.retainOriginal;
}

public String getKey() {
return key;
}

public boolean isRetainOriginal() {
return retainOriginal;
}

@Override
public String toString() {
return key + (retainOriginal ? " (retained)" : "");
}

@PluginBuilderFactory
public static Builder newBuilder() {
return new Builder();
}

public static class Builder implements org.apache.logging.log4j.core.util.Builder<CustomField> {

@PluginBuilderAttribute("mdcKeyName")
private String key;

@PluginBuilderAttribute("retainOriginal")
private boolean retainOriginal;

public Builder setKey(String key) {
this.key = key;
return this;
}

public Builder setRetainOriginal(boolean retainOriginal) {
this.retainOriginal = retainOriginal;
return this;
}

@Override
public CustomField build() {
return new CustomField(this);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.sap.hcp.cf.log4j2.layout;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.sap.hcp.cf.logging.common.LogContext;

public class CustomFieldsAdapter {

private List<CustomField> customFields;

public CustomFieldsAdapter(CustomField... customFields) {
this.customFields = customFields == null ? emptyList() : asList(customFields);
}

public List<String> getCustomFieldKeyNames() {
List<String> result = new ArrayList<>(customFields.size());
for (CustomField customField : customFields) {
result.add(customField.getKey());
}
return result;
}

public List<String> getExcludedFieldKeyNames() {
Collection<String> contextFieldsKeys = LogContext.getContextFieldsKeys();
List<String> result = new ArrayList<>(customFields.size());
for (CustomField customField : customFields) {
if (!customField.isRetainOriginal() && !contextFieldsKeys.contains(customField.getKey())) {
result.add(customField.getKey());
}
}
return result;
}

}
Loading