Skip to content

Commit

Permalink
Unify field collection providers
Browse files Browse the repository at this point in the history
  • Loading branch information
nehaev committed Dec 15, 2024
1 parent 955733b commit e58f119
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 139 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.github.loki4j.logback.json;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import ch.qos.logback.classic.spi.ILoggingEvent;

/**
* An abstract provider that writes a collection of JSON fields
*/
public abstract class AbstractFieldCollectionJsonProvider<E, T extends Collection<E>> extends AbstractJsonProvider {

/**
* A prefix added to all the keys (field names) in this collection.
*/
private String prefix;

/**
* Whether to omit the prefix and use field names from this collection as they are.
*/
private boolean noPrefix;

/**
* A set of keys to exclude from JSON payload.
* Exclude list has a precedence over include list.
* If not specified, all keys are included.
*/
private Set<String> excludeKeys = new HashSet<>();

/**
* A set of keys to include into JSON payload.
* If not specified, all keys are included.
*/
private Set<String> includeKeys = new HashSet<>();

@Override
public boolean canWrite(ILoggingEvent event) {
var entries = extractEntries(event);
return entries != null && !entries.isEmpty();
}

@Override
public boolean writeTo(JsonEventWriter writer, ILoggingEvent event, boolean startWithSeparator) {
var entries = extractEntries(event);
var firstFieldWritten = false;
for (E entry : entries) {
var key = extractKey(entry);
var value = extractValue(entry);
// skip empty records
if (key == null || value == null)
continue;

// check exclude list, if defined
if (!excludeKeys.isEmpty() && excludeKeys.contains(key))
continue;

// check include list, if defined
if (!includeKeys.isEmpty() && !includeKeys.contains(key))
continue;

if (startWithSeparator || firstFieldWritten)
writer.writeFieldSeparator();
var name = noPrefix ? key : prefix + key;
writer.writeObjectField(name, value);
firstFieldWritten = true;
}
return firstFieldWritten;
}

/**
* Extract the collection of entries (i.e. fields) from the logging event.
* @param event Current logback event.
*/
protected abstract T extractEntries(ILoggingEvent event);

/**
* Extract the key (i.e. field name) from the collection entry.
*/
protected abstract String extractKey(E entry);

/**
* Extract the value (i.e. field value) from the collection entry.
*/
protected abstract Object extractValue(E entry);

public void addExclude(String key) {
excludeKeys.add(key);
}

public void addInclude(String key) {
includeKeys.add(key);
}

public String getPrefix() {
return prefix;
}

public void setPrefix(String prefix) {
this.prefix = prefix;
}

public boolean isNoPrefix() {
return noPrefix;
}

public void setNoPrefix(boolean noPrefix) {
this.noPrefix = noPrefix;
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
package com.github.loki4j.logback.json;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.spi.ContextAwareBase;

/**
* An abstract provider that writes a certain aspect of a logging event as a JSON field
* An abstract provider that writes a single JSON field
*/
public abstract class AbstractFieldJsonProvider extends ContextAwareBase implements JsonProvider<ILoggingEvent> {

private boolean enabled = true;
public abstract class AbstractFieldJsonProvider extends AbstractJsonProvider {

/**
* A JSON field name to use for this provider.
*/
private String fieldName;

private volatile boolean started;

@Override
public boolean canWrite(ILoggingEvent event) {
return true;
Expand All @@ -37,30 +32,6 @@ public boolean writeTo(JsonEventWriter writer, ILoggingEvent event, boolean star
*/
protected abstract void writeExactlyOneField(JsonEventWriter writer, ILoggingEvent event);

@Override
public void start() {
started = true;
}

@Override
public void stop() {
started = false;
}

@Override
public boolean isStarted() {
return started;
}

@Override
public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public String getFieldName() {
return fieldName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.github.loki4j.logback.json;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.spi.ContextAwareBase;

/**
* An abstract provider that writes a certain aspect of a logging event as a JSON fragment
*/
public abstract class AbstractJsonProvider extends ContextAwareBase implements JsonProvider<ILoggingEvent> {

private boolean enabled = true;

private volatile boolean started;

@Override
public void start() {
started = true;
}

@Override
public void stop() {
started = false;
}

@Override
public boolean isStarted() {
return started;
}

@Override
public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
Original file line number Diff line number Diff line change
@@ -1,77 +1,32 @@
package com.github.loki4j.logback.json;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.event.KeyValuePair;

import ch.qos.logback.classic.spi.ILoggingEvent;

public class KeyValuePairsJsonProvider extends AbstractFieldJsonProvider {
public class KeyValuePairsJsonProvider extends AbstractFieldCollectionJsonProvider<KeyValuePair, List<KeyValuePair>> {

public static final String FIELD_KVP_PREFIX = "kvp_";

/**
* A set of keys to exclude from JSON payload.
* Exclude list has a precedence over include list.
* If not specified, all keys are included.
*/
private Set<String> excludeKeys = new HashSet<>();

/**
* A set of keys to include into JSON payload.
* If not specified, all keys are included.
*/
private Set<String> includeKeys = new HashSet<>();

public KeyValuePairsJsonProvider() {
setFieldName(FIELD_KVP_PREFIX);
setPrefix(FIELD_KVP_PREFIX);
}

@Override
public boolean canWrite(ILoggingEvent event) {
List<KeyValuePair> kvPairs = event.getKeyValuePairs();
return kvPairs != null && !kvPairs.isEmpty();
protected List<KeyValuePair> extractEntries(ILoggingEvent event) {
return event.getKeyValuePairs();
}

@Override
public boolean writeTo(JsonEventWriter writer, ILoggingEvent event, boolean startWithSeparator) {
List<KeyValuePair> kvPairs = event.getKeyValuePairs();
var firstFieldWritten = false;
for (KeyValuePair entry : kvPairs) {
// skip empty records
if (entry.key == null || entry.value == null)
continue;

// check exclude list, if defined
if (!excludeKeys.isEmpty() && excludeKeys.contains(entry.key))
continue;

// check include list, if defined
if (!includeKeys.isEmpty() && !includeKeys.contains(entry.key))
continue;

if (startWithSeparator || firstFieldWritten)
writer.writeFieldSeparator();
writer.writeObjectField(getFieldName() + entry.key, entry.value);
firstFieldWritten = true;
}
return firstFieldWritten;
protected String extractKey(KeyValuePair entry) {
return entry.key;
}

@Override
protected void writeExactlyOneField(JsonEventWriter writer, ILoggingEvent event) {
throw new UnsupportedOperationException(
"KeyValuePairsJsonProvider can write an arbitrary number of fields. `writeExactlyOneField` should never be called for KeyValuePairsJsonProvider.");
}

public void addExclude(String key) {
excludeKeys.add(key);
}

public void addInclude(String key) {
includeKeys.add(key);
protected Object extractValue(KeyValuePair entry) {
return entry.value;
}

}
Original file line number Diff line number Diff line change
@@ -1,75 +1,33 @@
package com.github.loki4j.logback.json;

import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import ch.qos.logback.classic.spi.ILoggingEvent;

public class MdcJsonProvider extends AbstractFieldJsonProvider {
public class MdcJsonProvider extends AbstractFieldCollectionJsonProvider<Map.Entry<String, String>, Set<Map.Entry<String, String>>> {

public static final String FIELD_MDC_PREFIX = "mdc_";

/**
* A set of MDC keys to exclude from JSON payload.
* Exclude list has a precedence over include list.
* If not specified, all keys are included.
*/
private Set<String> excludeKeys = new HashSet<>();

/**
* A set of MDC keys to include into JSON payload.
* If not specified, all keys are included.
*/
private Set<String> includeKeys = new HashSet<>();

public MdcJsonProvider() {
setFieldName(FIELD_MDC_PREFIX);
setPrefix(FIELD_MDC_PREFIX);
}

@Override
public boolean canWrite(ILoggingEvent event) {
Map<String, String> mdcProperties = event.getMDCPropertyMap();
return mdcProperties != null && !mdcProperties.isEmpty();
protected Set<Entry<String, String>> extractEntries(ILoggingEvent event) {
var mdcProperties = event.getMDCPropertyMap();
return mdcProperties == null ? null : mdcProperties.entrySet();
}

@Override
public boolean writeTo(JsonEventWriter writer, ILoggingEvent event, boolean startWithSeparator) {
Map<String, String> mdcProperties = event.getMDCPropertyMap();
var firstFieldWritten = false;
for (Map.Entry<String, String> entry : mdcProperties.entrySet()) {
// skip empty records
if (entry.getKey() == null || entry.getValue() == null)
continue;

// check exclude list, if defined
if (!excludeKeys.isEmpty() && excludeKeys.contains(entry.getKey()))
continue;

// check include list, if defined
if (!includeKeys.isEmpty() && !includeKeys.contains(entry.getKey()))
continue;

if (startWithSeparator || firstFieldWritten)
writer.writeFieldSeparator();
writer.writeStringField(getFieldName() + entry.getKey(), entry.getValue());
firstFieldWritten = true;
}
return firstFieldWritten;
protected String extractKey(Entry<String, String> entry) {
return entry.getKey();
}

@Override
protected void writeExactlyOneField(JsonEventWriter writer, ILoggingEvent event) {
throw new UnsupportedOperationException(
"MdcJsonProvider can write an arbitrary number of fields. `writeExactlyOneField` should never be called for MdcJsonProvider.");
}

public void addExclude(String key) {
excludeKeys.add(key);
}

public void addInclude(String key) {
includeKeys.add(key);
protected String extractValue(Entry<String, String> entry) {
return entry.getValue();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static org.junit.Assert.assertEquals;

import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.junit.Test;

Expand Down Expand Up @@ -89,7 +89,7 @@ public void testWriteListOfStrings() {
public void testWriteSetOfInts() {
var writer = new JsonEventWriter(10);
writer.writeBeginObject();
writer.writeObjectField("it", Set.of(1, 2, 3));
writer.writeObjectField("it", new TreeSet<Integer>(List.of(1, 2, 3)));
writer.writeEndObject();

assertEquals("{\"it\":[1,2,3]}", writer.toString());
Expand Down
Loading

0 comments on commit e58f119

Please sign in to comment.