Skip to content

Commit

Permalink
Add ability to output all keys and values in thread context (#718)
Browse files Browse the repository at this point in the history
  • Loading branch information
rhanneken authored Apr 7, 2024
1 parent 22549cf commit 46a73dc
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.tinylog.impl.format.pattern.placeholders;

import java.util.EnumSet;
import java.util.Set;

import org.tinylog.impl.LogEntryValue;
import org.tinylog.impl.format.pattern.ValueType;

/**
* Placeholder implementation for resolving thread context values for a log entry.
*/
public abstract class AbstractContextPlaceholder implements Placeholder {

@Override
public Set<LogEntryValue> getRequiredLogEntryValues() {
return EnumSet.of(LogEntryValue.CONTEXT);
}

@Override
public ValueType getType() {
return ValueType.STRING;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.tinylog.core.internal.LoggingContext;

/**
* Builder for creating an instance of {@link ContextPlaceholder}.
* Builder for creating an instance of {@link AbstractContextPlaceholder}.
*/
public class ContextPlaceholderBuilder implements PlaceholderBuilder {

Expand All @@ -18,11 +18,6 @@ public String getName() {

@Override
public Placeholder create(LoggingContext context, String value) {
if (value == null) {
throw new IllegalArgumentException("Thread context key is not defined for context placeholder");
} else {
return new ContextPlaceholder(value);
}
return value == null ? new MultiValueContextPlaceholder() : new SingleValueContextPlaceholder(value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.tinylog.impl.format.pattern.placeholders;

import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.tinylog.impl.LogEntry;

/**
* Placeholder implementation for resolving all thread context keys and values for a log entry.
*/
public class MultiValueContextPlaceholder extends AbstractContextPlaceholder {

/**
* Constructs a MultiValueContextPlaceholder.
*/
public MultiValueContextPlaceholder() {
}

@Override
public String getValue(LogEntry entry) {
return contextKeysAndValues(entry);
}

@Override
public void render(StringBuilder builder, LogEntry entry) {
builder.append(contextKeysAndValues(entry));
}

/**
* Creates a string representation of all keys and values in thread context.
*
* @param logEntry The log entry whose thread context is to be represented as a string
* @return A string representation of all keys and values in entry's thread context
*/
private static String contextKeysAndValues(LogEntry logEntry) {
Set<Map.Entry<String, String>> contextEntries = logEntry.getContext().entrySet();
if (contextEntries.isEmpty()) {
return "";
}

return contextEntries.stream()
.sorted(Map.Entry.comparingByKey())
.map(mapEntry -> new StringBuilder(mapEntry.getKey()).append("=").append(mapEntry.getValue()))
.collect(Collectors.joining(", "));
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.tinylog.impl.format.pattern.placeholders;

import org.tinylog.impl.LogEntry;

/**
* Placeholder implementation for resolving one thread context value for a log entry.
*/
public class SingleValueContextPlaceholder extends AbstractContextPlaceholder {

private final String key;

/**
* @param key The key of the thread context value to output
*/
public SingleValueContextPlaceholder(String key) {
this.key = key;
}

@Override
public String getValue(LogEntry entry) {
return entry.getContext().get(key);
}

@Override
public void render(StringBuilder builder, LogEntry entry) {
String value = entry.getContext().getOrDefault(key, "");
builder.append(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.tinylog.impl.test.LogEntryBuilder;

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

@CaptureLogEntries
class ContextPlaceholderBuilderTest {
Expand All @@ -20,14 +19,15 @@ class ContextPlaceholderBuilderTest {
private LoggingContext context;

/**
* Verifies that an {@link IllegalArgumentException} with a meaningful message description will be thrown, if the
* thread context key is missing.
* Verifies that a context placeholder can be created without a thread context key.
*/
@Test
void creationWithoutConfigurationValue() {
assertThatThrownBy(() -> new ContextPlaceholderBuilder().create(context, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("key");
Placeholder placeholder = new ContextPlaceholderBuilder().create(context, null);
assertThat(placeholder).isInstanceOf(MultiValueContextPlaceholder.class);

LogEntry logEntry = new LogEntryBuilder().context("foo", "bar").context("baz", "quk").create();
assertThat(placeholder.getValue(logEntry)).isEqualTo("baz=quk, foo=bar");
}

/**
Expand All @@ -36,7 +36,7 @@ void creationWithoutConfigurationValue() {
@Test
void creationWithConfigurationValue() {
Placeholder placeholder = new ContextPlaceholderBuilder().create(context, "foo");
assertThat(placeholder).isInstanceOf(ContextPlaceholder.class);
assertThat(placeholder).isInstanceOf(SingleValueContextPlaceholder.class);

LogEntry logEntry = new LogEntryBuilder().context("foo", "bar").create();
assertThat(placeholder.getValue(logEntry)).isEqualTo("bar");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.tinylog.impl.format.pattern.placeholders;

import org.junit.jupiter.api.Test;
import org.tinylog.impl.LogEntry;
import org.tinylog.impl.LogEntryValue;
import org.tinylog.impl.format.pattern.ValueType;
import org.tinylog.impl.test.FormatOutputRenderer;
import org.tinylog.impl.test.LogEntryBuilder;

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

/**
* Tests for MultiValueContextPlaceholder.
*/
public class MultiValueContextPlaceholderTest {

/**
* Verifies that the log entry value {@link LogEntryValue#CONTEXT} is defined as required by the context
* placeholder.
*/
@Test
void requiredLogEntryValues() {
MultiValueContextPlaceholder placeholder = new MultiValueContextPlaceholder();
assertThat(placeholder.getRequiredLogEntryValues()).containsExactly(LogEntryValue.CONTEXT);
}

/**
* Verifies that all thread context keys and values of a log entry will be resolved.
*/
@Test
void resolveWithContextValues() {
MultiValueContextPlaceholder placeholder = new MultiValueContextPlaceholder();
LogEntry logEntry = new LogEntryBuilder().context("foo", "bar").context("baz", "quk").create();
assertThat(placeholder.getType()).isEqualTo(ValueType.STRING);
assertThat(placeholder.getValue(logEntry)).isEqualTo("baz=quk, foo=bar");
}

/**
* Verifies that empty string will be resolved, if a thread context value is not present.
*/
@Test
void resolveWithoutContextValues() {
MultiValueContextPlaceholder placeholder = new MultiValueContextPlaceholder();
LogEntry logEntry = new LogEntryBuilder().create();
assertThat(placeholder.getType()).isEqualTo(ValueType.STRING);
assertThat(placeholder.getValue(logEntry)).isEqualTo("");
}

/**
* Verifies that all thread context keys and values of a log entry will be output.
*/
@Test
void renderWithContextValues() {
MultiValueContextPlaceholder placeholder = new MultiValueContextPlaceholder();
FormatOutputRenderer renderer = new FormatOutputRenderer(placeholder);
LogEntry logEntry = new LogEntryBuilder().context("foo", "bar").context("baz", "quk").create();
assertThat(renderer.render(logEntry)).isEqualTo("baz=quk, foo=bar");
}

/**
* Verifies that an empty string will be output, if no thread context values are
* present.
*/
@Test
void renderWithoutContextValues() {
MultiValueContextPlaceholder placeholder = new MultiValueContextPlaceholder();
FormatOutputRenderer renderer = new FormatOutputRenderer(placeholder);
LogEntry logEntry = new LogEntryBuilder().create();
assertThat(renderer.render(logEntry)).isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@

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

class ContextPlaceholderTest {
class SingleValueContextPlaceholderTest {

/**
* Verifies that the log entry value {@link LogEntryValue#CONTEXT} is defined as required by the context
* placeholder.
*/
@Test
void requiredLogEntryValues() {
ContextPlaceholder placeholder = new ContextPlaceholder("foo");
SingleValueContextPlaceholder placeholder = new SingleValueContextPlaceholder("foo");
assertThat(placeholder.getRequiredLogEntryValues()).containsExactly(LogEntryValue.CONTEXT);
}

Expand All @@ -26,7 +26,7 @@ void requiredLogEntryValues() {
*/
@Test
void resolveWithContextValue() {
ContextPlaceholder placeholder = new ContextPlaceholder("foo");
SingleValueContextPlaceholder placeholder = new SingleValueContextPlaceholder("foo");
LogEntry logEntry = new LogEntryBuilder().context("foo", "bar").create();
assertThat(placeholder.getType()).isEqualTo(ValueType.STRING);
assertThat(placeholder.getValue(logEntry)).isEqualTo("bar");
Expand All @@ -37,7 +37,7 @@ void resolveWithContextValue() {
*/
@Test
void resolveWithoutContextValue() {
ContextPlaceholder placeholder = new ContextPlaceholder("foo");
SingleValueContextPlaceholder placeholder = new SingleValueContextPlaceholder("foo");
LogEntry logEntry = new LogEntryBuilder().create();
assertThat(placeholder.getType()).isEqualTo(ValueType.STRING);
assertThat(placeholder.getValue(logEntry)).isNull();
Expand All @@ -48,7 +48,7 @@ void resolveWithoutContextValue() {
*/
@Test
void renderWithContextValue() {
ContextPlaceholder placeholder = new ContextPlaceholder("foo");
SingleValueContextPlaceholder placeholder = new SingleValueContextPlaceholder("foo");
FormatOutputRenderer renderer = new FormatOutputRenderer(placeholder);
LogEntry logEntry = new LogEntryBuilder().context("foo", "bar").create();
assertThat(renderer.render(logEntry)).isEqualTo("bar");
Expand All @@ -59,10 +59,9 @@ void renderWithContextValue() {
*/
@Test
void renderWithoutContextValue() {
ContextPlaceholder placeholder = new ContextPlaceholder("foo");
SingleValueContextPlaceholder placeholder = new SingleValueContextPlaceholder("foo");
FormatOutputRenderer renderer = new FormatOutputRenderer(placeholder);
LogEntry logEntry = new LogEntryBuilder().create();
assertThat(renderer.render(logEntry)).isEmpty();
}

}

0 comments on commit 46a73dc

Please sign in to comment.