Skip to content
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
@@ -0,0 +1,18 @@
package com.sap.hcp.cf.logging.common.serialization;

import java.util.ArrayList;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ContextFieldSupplierServiceLoader<T> {

private ContextFieldSupplierServiceLoader() {
}

public static <T extends ContextFieldSupplier> ArrayList<T> addFieldSuppliers(Stream<T> original, Class<T> clazz) {
Stream<T> spiSuppliers = ServiceLoader.load(clazz).stream().map(ServiceLoader.Provider::get).sorted();
return Stream.concat(original, spiSuppliers).sorted().collect(Collectors.toCollection(ArrayList::new));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@
@FunctionalInterface
public interface Log4jContextFieldSupplier extends EventContextFieldSupplier<LogEvent> {

// These are constants for the order. The default is 0. To retain the original order, negative values were chosen.
int BASE_FIELDS = -100;
int CONTEXT_FIELDS = -90;
int REQUEST_FIELDS = -80;

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;

@Plugin(name = "contextFieldSupplier", category = Node.CATEGORY, printObject = true)
public class ContextFieldSupplierElement {
public class ContextFieldSupplierElement implements ElementSupplier {

private String supplierClass;
private final String supplierClass;

public ContextFieldSupplierElement(Builder builder) {
this.supplierClass = builder.clazz;
Expand All @@ -19,6 +19,7 @@ public static Builder newBuilder() {
return new Builder();
}

@Override
public String getSupplierClass() {
return supplierClass;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.sap.hcp.cf.log4j2.layout;

public interface ElementSupplier {
String getSupplierClass();
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package com.sap.hcp.cf.log4j2.layout;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.jr.ob.JSON;
import com.fasterxml.jackson.jr.ob.JSONComposer;
import com.fasterxml.jackson.jr.ob.comp.ArrayComposer;
import com.fasterxml.jackson.jr.ob.comp.ComposerBase;
import com.fasterxml.jackson.jr.ob.comp.ObjectComposer;
import com.sap.hcp.cf.log4j2.converter.api.Log4jContextFieldSupplier;
import com.sap.hcp.cf.log4j2.layout.supppliers.BaseFieldSupplier;
import com.sap.hcp.cf.log4j2.layout.supppliers.EventContextFieldSupplier;
import com.sap.hcp.cf.log4j2.layout.supppliers.LogEventUtilities;
import com.sap.hcp.cf.log4j2.layout.supppliers.RequestRecordFieldSupplier;
import com.sap.hcp.cf.logging.common.Fields;
import com.sap.hcp.cf.logging.common.converter.LineWriter;
import com.sap.hcp.cf.logging.common.converter.StacktraceLines;
import com.sap.hcp.cf.logging.common.serialization.ContextFieldConverter;
import com.sap.hcp.cf.logging.common.serialization.ContextFieldSupplier;
import com.sap.hcp.cf.logging.common.serialization.ContextFieldSupplierServiceLoader;
import com.sap.hcp.cf.logging.common.serialization.JsonSerializationException;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.StringLayout;
Expand All @@ -28,10 +25,9 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;

/**
* A {@link StringLayout} implementation that encodes an {@link LogEvent} as a JSON object.
Expand All @@ -56,48 +52,62 @@ public final class JsonPatternLayout extends AbstractStringLayout {

private static final String NEWLINE = "\n";
private final ContextFieldConverter contextFieldConverter;
private List<Log4jContextFieldSupplier> log4jContextFieldSuppliers = new ArrayList<>();
private List<ContextFieldSupplier> contextFieldSuppliers = new ArrayList<>();
private int maxStacktraceSize;
private final List<Log4jContextFieldSupplier> log4jContextFieldSuppliers;
private final List<ContextFieldSupplier> contextFieldSuppliers;
private final int maxStacktraceSize;

private boolean sendDefaultValues;
private final boolean sendDefaultValues;

private JSON json;
private final JSON json;

protected JsonPatternLayout(Charset charset, boolean sendDefaultValues, int maxStacktraceSize,
String jsonBuilderClass, CustomFieldElement[] customFieldMdcKeys,
Log4jContextFieldSupplierElement[] log4jContextFieldSupplierElements,
ContextFieldSupplierElement[] contextFieldSupplierElements) {
private JsonPatternLayout(Charset charset, boolean sendDefaultValues, int maxStacktraceSize,
String jsonBuilderClass, CustomFieldElement[] customFieldMdcKeys,
Log4jContextFieldSupplierElement[] log4jContextFieldSupplierElements,
ContextFieldSupplierElement[] contextFieldSupplierElements) {
super(charset);
this.sendDefaultValues = sendDefaultValues;
this.maxStacktraceSize = maxStacktraceSize > 0 ? maxStacktraceSize : 55 * 1024;
this.contextFieldConverter = contextFieldConverter(sendDefaultValues, customFieldMdcKeys);
this.json = createJson(jsonBuilderClass);
this.log4jContextFieldSuppliers.add(new BaseFieldSupplier());
this.log4jContextFieldSuppliers.add(new EventContextFieldSupplier());
this.log4jContextFieldSuppliers.add(new RequestRecordFieldSupplier());
if (log4jContextFieldSupplierElements != null) {
for (Log4jContextFieldSupplierElement current: log4jContextFieldSupplierElements) {
try {
Log4jContextFieldSupplier instance =
createInstance(current.getSupplierClass(), Log4jContextFieldSupplier.class);
log4jContextFieldSuppliers.add(instance);
} catch (ReflectiveOperationException cause) {
LOGGER.warn("Cannot register Log4jContextFieldSupplier.", cause);
}
}
this.log4jContextFieldSuppliers =
getContextFieldSuppliers(log4jContextFieldSupplierElements, Log4jContextFieldSupplier.class);
this.contextFieldSuppliers = getContextFieldSuppliers(contextFieldSupplierElements, ContextFieldSupplier.class);
}

private static <E extends ElementSupplier, S extends ContextFieldSupplier> List<S> getContextFieldSuppliers(
E[] elements, Class<S> supplierClass) {
Stream<S> configSuppliers = loadContextFieldSuppliersFromConfig(elements, createInstance(supplierClass));
return ContextFieldSupplierServiceLoader.addFieldSuppliers(configSuppliers, supplierClass);
}

private static <E extends ElementSupplier, S extends ContextFieldSupplier> Stream<S> loadContextFieldSuppliersFromConfig(
E[] elements, Function<E, S> mapper) {
if (elements == null || elements.length == 0) {
return Stream.empty();
}
if (contextFieldSupplierElements != null) {
for (ContextFieldSupplierElement current: contextFieldSupplierElements) {
try {
ContextFieldSupplier instance =
createInstance(current.getSupplierClass(), ContextFieldSupplier.class);
contextFieldSuppliers.add(instance);
} catch (ReflectiveOperationException cause) {
LOGGER.warn("Cannot register ContextFieldSupplier.", cause);
}
return Stream.of(elements).map(mapper).filter(Objects::nonNull);
}

private static <T extends ElementSupplier, R extends ContextFieldSupplier> Function<T, R> createInstance(
Class<R> clazz) {
return e -> {
try {
return createInstance(e.getSupplierClass(), clazz);
} catch (ReflectiveOperationException cause) {
LOGGER.warn("Cannot register ContextFieldSupplier <" + clazz.getSimpleName() + ">.", cause);
return null;
}
}
};
}

// for testing
List<ContextFieldSupplier> getContextFieldSuppliers() {
return contextFieldSuppliers;
}

// for testing
List<Log4jContextFieldSupplier> getLog4jContextFieldSuppliers() {
return log4jContextFieldSuppliers;
}

@PluginFactory
Expand Down Expand Up @@ -169,8 +179,7 @@ public String toSerializable(LogEvent event) {
}
}

private <P extends ComposerBase> void addMarkers(ObjectComposer<P> oc, LogEvent event)
throws IOException, JsonProcessingException {
private <P extends ComposerBase> void addMarkers(ObjectComposer<P> oc, LogEvent event) throws IOException {
if (sendDefaultValues || event.getMarker() != null) {
ArrayComposer<ObjectComposer<P>> ac = oc.startArrayField(Fields.CATEGORIES);
addMarker(ac, event.getMarker());
Expand Down Expand Up @@ -198,8 +207,7 @@ private Map<String, Object> collectContextFields(LogEvent event) {
return contextFields;
}

private <P extends ComposerBase> void addStacktrace(ObjectComposer<P> oc, LogEvent event)
throws IOException, JsonProcessingException {
private <P extends ComposerBase> void addStacktrace(ObjectComposer<P> oc, LogEvent event) throws IOException {
if (event.getThrown() != null) {
LineWriter lw = new LineWriter();
event.getThrown().printStackTrace(new PrintWriter(lw));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;

@Plugin(name = "log4jContextFieldSupplier", category = Node.CATEGORY, printObject = true)
public class Log4jContextFieldSupplierElement {
public class Log4jContextFieldSupplierElement implements ElementSupplier {

private String supplierClass;
private final String supplierClass;

public Log4jContextFieldSupplierElement(Builder builder) {
this.supplierClass = builder.clazz;
Expand All @@ -19,6 +19,7 @@ public static Builder newBuilder() {
return new Builder();
}

@Override
public String getSupplierClass() {
return supplierClass;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

public class BaseFieldSupplier implements Log4jContextFieldSupplier {

@Override
public int order() {
return Log4jContextFieldSupplier.BASE_FIELDS;
}

@Override
public Map<String, Object> map(LogEvent event) {
Map<String, Object> fields = new HashMap<>(6);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
public class EventContextFieldSupplier extends AbstractContextFieldSupplier<LogEvent>
implements Log4jContextFieldSupplier {

@Override
public int order() {
return Log4jContextFieldSupplier.CONTEXT_FIELDS;
}

@Override
protected Map<String, String> getContextMap(LogEvent event) {
return event.getContextData().toMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
public class RequestRecordFieldSupplier extends AbstractRequestRecordFieldSupplier<LogEvent>
implements Log4jContextFieldSupplier {

@Override
public int order() {
return Log4jContextFieldSupplier.REQUEST_FIELDS;
}

@Override
protected boolean isRequestLog(LogEvent event) {
return LogEventUtilities.isRequestLog(event);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
com.sap.hcp.cf.log4j2.layout.supppliers.BaseFieldSupplier
com.sap.hcp.cf.log4j2.layout.supppliers.EventContextFieldSupplier
com.sap.hcp.cf.log4j2.layout.supppliers.RequestRecordFieldSupplier
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.sap.hcp.cf.log4j2.layout;

import com.sap.hcp.cf.log4j2.converter.api.Log4jContextFieldSupplier;
import com.sap.hcp.cf.log4j2.layout.supppliers.BaseFieldSupplier;
import com.sap.hcp.cf.log4j2.layout.supppliers.EventContextFieldSupplier;
import com.sap.hcp.cf.log4j2.layout.supppliers.RequestRecordFieldSupplier;
import com.sap.hcp.cf.logging.common.serialization.ContextFieldSupplier;
import org.apache.logging.log4j.core.LogEvent;
import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

class JsonPatternLayoutTest {

private final JsonPatternLayout LAYOUT =
JsonPatternLayout.createLayout(StandardCharsets.UTF_8, false, 10, null, new CustomFieldElement[0],
new Log4jContextFieldSupplierElement[] {
Log4jContextFieldSupplierElement.newBuilder().setClazz(
SampleLog4jContextFieldSupplier.class.getName()).build() },
new ContextFieldSupplierElement[] { ContextFieldSupplierElement.newBuilder()
.setClazz(
SampleContextFieldSupplier.class.getName()).build() },
null);

@Test
void loadsContextFieldSuppliers() {
assertThat(LAYOUT.getContextFieldSuppliers()).map(Object::getClass).map(Class::getName)
.containsExactly(SampleContextFieldSupplier.class.getName(),
SpiContextFieldSupplier.class.getName());
}

@Test
void getLog4jContextFieldSuppliers() {
assertThat(LAYOUT.getLog4jContextFieldSuppliers()).map(Object::getClass).map(Class::getName)
.containsExactly(BaseFieldSupplier.class.getName(),
EventContextFieldSupplier.class.getName(),
RequestRecordFieldSupplier.class.getName(),
SampleLog4jContextFieldSupplier.class.getName(),
SpiLog4jContextFieldSupplier.class.getName());
}

static class SampleContextFieldSupplier implements ContextFieldSupplier {
@Override
public Map<String, Object> get() {
return Collections.emptyMap();
}
}

public static class SpiContextFieldSupplier implements ContextFieldSupplier {

public SpiContextFieldSupplier() {
}

@Override
public Map<String, Object> get() {
return Collections.emptyMap();
}

}

static class SampleLog4jContextFieldSupplier implements Log4jContextFieldSupplier {

@Override
public Map<String, Object> map(LogEvent event) {
return Collections.emptyMap();
}
}

public static class SpiLog4jContextFieldSupplier implements Log4jContextFieldSupplier {

@Override
public Map<String, Object> map(LogEvent event) {
return Collections.emptyMap();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.sap.hcp.cf.log4j2.layout.JsonPatternLayoutTest$SpiLog4jContextFieldSupplier
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.sap.hcp.cf.log4j2.layout.JsonPatternLayoutTest$SpiContextFieldSupplier
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@FunctionalInterface
public interface LogbackContextFieldSupplier extends EventContextFieldSupplier<ILoggingEvent> {

// These are constants for the order. The default is 0. To retain the original order, negative values were chosen.
int BASE_FIELDS = -100;
int CONTEXT_FIELDS = -90;
int REQUEST_FIELDS = -80;
Expand Down

This file was deleted.

Loading