8000 Support change the context prefix and suffix format of KeyValueFormatter by kuraun · Pull Request #276 · google/flogger · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Support change the context prefix and suffix format of KeyValueFormatter #276

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
14 changes: 14 additions & 0 deletions api/src/main/java/com/google/common/flogger/LogContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,20 @@ public void emit(Tags tags, KeyValueHandler out) {
*/
public static final MetadataKey<StackSize> CONTEXT_STACK_SIZE =
MetadataKey.single("stack_size", StackSize.class);

/**
* Key associated with the metadata for specifying {@code KeyValueFormatter.prefix} with a log
* statement.
*/
public static final MetadataKey<String> CONTEXT_PREFIX =
MetadataKey.single("prefix", String.class);

/**
* Key associated with the metadata for specifying {@code KeyValueFormatter.suffix} with a log
* statement.
*/
public static final MetadataKey<String> CONTEXT_SUFFIX =
MetadataKey.single("suffix", String.class);
}

static final class MutableMetadata extends Metadata {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.google.common.flogger.LogContext;
import com.google.common.flogger.MetadataKey;
import com.google.common.flogger.MetadataKey.KeyValueHandler;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -53,12 +54,15 @@
public final class SimpleMessageFormatter {
@SuppressWarnings("ConstantCaseForConstants")
private static final Set<MetadataKey<?>> DEFAULT_KEYS_TO_IGNORE =
Collections.<MetadataKey<?>>singleton(LogContext.Key.LOG_CAUSE);
Collections.unmodifiableSet(new HashSet<MetadataKey<?>>(Arrays.asList(
LogContext.Key.LOG_CAUSE,
LogContext.Key.CONTEXT_PREFIX,
LogContext.Key.CONTEXT_SUFFIX)));

private static final LogMessageFormatter DEFAULT_FORMATTER = newFormatter(DEFAULT_KEYS_TO_IGNORE);

/**
* Returns the singleton default log message formatter. This formats log messages in the form:
* Returns the default log message formatter. This formats log messages in the form:
*
* <pre>{@code
* Log message [CONTEXT key="value" id=42 ]
Expand All @@ -71,9 +75,20 @@ public final class SimpleMessageFormatter {
* <p>The {@code cause} is omitted from the context section, since it's handled separately by most
* logger backends and not considered part of the formatted message. Other internal metadata keys
* may also be suppressed.
*
* <p>{@code
* LogContext.addMetadata(MetadataKey, Object)} can be used to change prefix and suffix, instead
* of using the default.
*/
public static LogMessageFormatter getDefaultFormatter() {
return DEFAULT_FORMATTER;
public static LogMessageFormatter getDefaultFormatter(Metadata metadata) {
if (metadata == null
|| metadata.equals(Metadata.empty())
|| metadata.findValue(LogContext.Key.CONTEXT_PREFIX) == null) {
return DEFAULT_FORMATTER;
}
return newFormatter(DEFAULT_KEYS_TO_IGNORE,
metadata.findValue(LogContext.Key.CONTEXT_PREFIX),
metadata.findValue(LogContext.Key.CONTEXT_SUFFIX));
}

/**
Expand All @@ -95,7 +110,7 @@ public static LogMessageFormatter getDefaultFormatter() {
*/
public static LogMessageFormatter getSimpleFormatterIgnoring(MetadataKey<?>... extraIgnoredKeys) {
if (extraIgnoredKeys.length == 0) {
return getDefaultFormatter();
return DEFAULT_FORMATTER;
}
Set<MetadataKey<?>> ignored = new HashSet<MetadataKey<?>>(DEFAULT_KEYS_TO_IGNORE);
Collections.addAll(ignored, extraIgnoredKeys);
Expand All @@ -121,8 +136,13 @@ public static LogMessageFormatter getSimpleFormatterIgnoring(MetadataKey<?>... e
public static StringBuilder appendContext(
MetadataProcessor metadataProcessor,
MetadataHandler<KeyValueHandler> metadataHandler,
String prefix,
String suffix,
StringBuilder buffer) {
KeyValueFormatter kvf = new KeyValueFormatter("[CONTEXT ", " ]", buffer);
KeyValueFormatter kvf = new KeyValueFormatter(
prefix == null ? "" : prefix,
suffix == null ? "" : suffix,
buffer);
metadataProcessor.process(metadataHandler, kvf);
kvf.done();
return buffer;
Expand Down Expand Up @@ -179,7 +199,9 @@ public static boolean mustBeFormatted(
* Returns a new "simple" formatter which ignores the given set of metadata keys. The caller must
* ensure that the given set is effectively immutable.
*/
private static LogMessageFormatter newFormatter(final Set<MetadataKey<?>> keysToIgnore) {
private static LogMessageFormatter newFormatter(final Set<MetadataKey<?>> keysToIgnore,
final String prefix,
final String suffix) {
return new LogMessageFormatter() {
private final MetadataHandler<KeyValueHandler> handler =
MetadataKeyValueHandlers.getDefaultHandler(keysToIgnore);
Expand All @@ -188,7 +210,7 @@ private static LogMessageFormatter newFormatter(final Set<MetadataKey<?>> keysTo
public StringBuilder append(
LogData logData, MetadataProcessor metadata, StringBuilder buffer) {
BaseMessageFormatter.appendFormattedMessage(logData, buffer);
return appendContext(metadata, handler, buffer);
return appendContext(metadata, handler, prefix, suffix, buffer);
}

@Override
Expand All @@ -202,6 +224,14 @@ public String format(LogData logData, MetadataProcessor metadata) {
};
}

/**
* Returns a new "simple" default formatter which ignores the given set of metadata keys. The caller must
* ensure that the given set is effectively immutable.
*/
private static LogMessageFormatter newFormatter(final Set<MetadataKey<?>> keysToIgnore) {
return newFormatter(keysToIgnore, "[CONTEXT ", " ]");
}

// ---- Everything below this point is deprecated and will be removed. ----

/** @deprecated Use a {@link LogMessageFormatter} and obtain the level and cause separately. */
Expand All @@ -213,7 +243,7 @@ public static void format(LogData logData, SimpleLogHandler receiver) {
MetadataProcessor.forScopeAndLogSite(Metadata.empty(), logData.getMetadata());
receiver.handleFormattedLogMessage(
logData.getLevel(),
getDefaultFormatter().format(logData, metadata),
getDefaultFormatter(logData.getMetadata()).format(logData, metadata),
metadata.getSingleValue(LogContext.Key.LOG_CAUSE));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ protected AbstractLogRecord(RuntimeException error, LogData data, Metadata scope
* requiring a new field in this class (to cut down on instance size).
*/
protected LogMessageFormatter getLogMessageFormatter() {
return SimpleMessageFormatter.getDefaultFormatter();
return SimpleMessageFormatter.getDefaultFormatter(data.getMetadata());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ public void testFormatFastPath() {
assertThat(format(logData, Metadata.empty())).isEqualTo("Hello World [CONTEXT bool=true ]");
}

@Test
public void testCustomContextFormatFastPath() {
// Just formatting a literal argument with no metadata avoids copying the message in a buffer.
String literal = "Hello World";
FakeLogData logData = FakeLogData.of(literal);
assertThat(format(logData, Metadata.empty())).isSameInstanceAs(literal);

// It also works if the log data has a "cause" (which is a special case and never formatted).
Throwable cause = new IllegalArgumentException("Badness");
logData.addMetadata(LogContext.Key.LOG_CAUSE, cause);
assertThat(format(logData, Metadata.empty())).isSameInstanceAs(literal);

// Add metadata of custom format.
logData.addMetadata(LogContext.Key.CONTEXT_PREFIX, "{ ");
logData.addMetadata(LogContext.Key.CONTEXT_SUFFIX, " }");

// However it does not work as soon as there's any scope metadata.
assertThat(format(logData, new FakeMetadata().add(INT_KEY, 42)))
.isEqualTo("Hello World { int=42 }");

// Or if there's more metadata added to the log site.
logData.addMetadata(BOOL_KEY, true);
assertThat(format(logData, Metadata.empty())).isEqualTo("Hello World { bool=true }");
}

// Parsing and basic formatting is well tested in BaseMessageFormatterTest.
@Test
public void testAppendFormatted() {
Expand Down Expand Up @@ -84,21 +109,75 @@ public void testAppendFormatted() {
.isEqualTo("answer=42 [CONTEXT int=1 int=2 string=\"Hello\" first=\"foo\" last=\"bar\" ]");
}

// Parsing and basic formatting is well tested in BaseMessageFormatterTest.
@Test
public void testCustomContextAppendFormatted() {
FakeLogData logData = FakeLogData.withPrintfStyle("answer=%d", 42);
assertThat(appendFormatted(logData, Metadata.empty())).isEqualTo("answer=42");

// Add metadata of custom format.
logData.addMetadata(LogContext.Key.CONTEXT_PREFIX, "{ ");
logData.addMetadata(LogContext.Key.CONTEXT_SUFFIX, " }");

FakeMetadata scope = new FakeMetadata().add(INT_KEY, 1);
assertThat(appendFormatted(logData, scope)).isEqualTo("answer=42 { int=1 }");

Throwable cause = new IllegalArgumentException("Badness");
logData.addMetadata(LogContext.Key.LOG_CAUSE, cause);
assertThat(appendFormatted(logData, scope)).isEqualTo("answer=42 { int=1 }");

logData.addMetadata(INT_KEY, 2);
assertThat(appendFormatted(logData, scope)).isEqualTo("answer=42 { int=1 int=2 }");

// Note that values are grouped by key, and keys are emitted in "encounter order" (scope first).
scope.add(STRING_KEY, "Hello");
assertThat(appendFormatted(logData, scope))
.isEqualTo("answer=42 { int=1 int=2 string=\"Hello\" }");

// Tags get embedded as metadata, and format in metadata order. So while tag keys are ordered
// locally, mixing tags and metadata does not result in a global ordering of context keys.
Tags tags = Tags.builder().addTag("last", "bar").addTag("first", "foo").build();
logData.addMetadata(LogContext.Key.TAGS, tags);
assertThat(appendFormatted(logData, scope))
.isEqualTo("answer=42 { int=1 int=2 string=\"Hello\" first=\"foo\" last=\"bar\" }");
}

@Test
public void testLogMessageFormatter() {
LogMessageFormatter formatter = SimpleMessageFormatter.getDefaultFormatter();
FakeLogData logData = FakeLogData.of("message");

FakeMetadata scope = new FakeMetadata();
scope.add(STRING_KEY, "Hello");
Tags tags = Tags.builder().addTag("last", "bar").addTag("first", "foo").build();
logData.addMetadata(LogContext.Key.TAGS, tags);

LogMessageFormatter formatter = SimpleMessageFormatter.getDefaultFormatter(logData.getMetadata());
MetadataProcessor metadata = MetadataProcessor.forScopeAndLogSite(scope, logData.getMetadata());
assertThat(formatter.format(logData, metadata))
.isEqualTo("message [CONTEXT string=\"Hello\" first=\"foo\" last=\"bar\" ]");
assertThat(formatter.append(logData, metadata, new StringBuilder("PREFIX: ")).toString())
.isEqualTo("PREFIX: message [CONTEXT string=\"Hello\" first=\"foo\" last=\"bar\" ]");
}

@Test
public void testCustomContextLogMessageFormatter() {
FakeLogData logData = FakeLogData.of("message");

FakeMetadata scope = new FakeMetadata();
scope.add(STRING_KEY, "Hello");
Tags tags = Tags.builder().addTag("last", "bar").addTag("first", "foo").build();
logData.addMetadata(LogContext.Key.TAGS, tags);

// Add metadata of custom format.
logData.addMetadata(LogContext.Key.CONTEXT_PREFIX, "{ ");
logData.addMetadata(LogContext.Key.CONTEXT_SUFFIX, " }");

LogMessageFormatter formatter = SimpleMessageFormatter.getDefaultFormatter(logData.getMetadata());
MetadataProcessor metadata = MetadataProcessor.forScopeAndLogSite(scope, logData.getMetadata());
assertThat(formatter.format(logData, metadata))
.isEqualTo("message [CONTEXT string=\"Hello\" first=\"foo\" last=\"bar\" ]");
.isEqualTo("message { string=\"Hello\" first=\"foo\" last=\"bar\" }");
assertThat(formatter.append(logData, metadata, new StringBuilder("PREFIX: ")).toString())
.isEqualTo("PREFIX: message [CONTEXT string=\"Hello\" first=\"foo\" last=\"bar\" ]");
.isEqualTo("PREFIX: message { string=\"Hello\" first=\"foo\" last=\"bar\" }");
}

// As above but also ignore the STRING_KEY metadata.
Expand Down Expand Up @@ -126,12 +205,12 @@ public void testLogMessageFormatter_ignoreExtra() {

private static String format(LogData logData, Metadata scope) {
MetadataProcessor metadata = MetadataProcessor.forScopeAndLogSite(scope, logData.getMetadata());
return SimpleMessageFormatter.getDefaultFormatter().format(logData, metadata);
return SimpleMessageFormatter.getDefaultFormatter(logData.getMetadata()).format(logData, metadata);
}

private static String appendFormatted(LogData logData, Metadata scope) {
MetadataProcessor metadata = MetadataProcessor.forScopeAndLogSite(scope, logData.getMetadata());
return SimpleMessageFormatter.getDefaultFormatter()
return SimpleMessageFormatter.getDefaultFormatter(logData.getMetadata())
.append(logData, metadata, new StringBuilder())
.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
@RunWith(JUnit4.class)
public final class AbstractLogRecordTest {
private static final LogMessageFormatter DEFAULT_FORMATTER =
SimpleM 4AE4 essageFormatter.getDefaultFormatter();
SimpleMessageFormatter.getDefaultFormatter(Metadata.empty());

private static final LogMessageFormatter TEST_MESSAGE_FORMATTER =
new LogMessageFormatter() {
Expand Down
0