Skip to content

Commit

Permalink
Making MetadataKey's emit() method properly typed for its value.
Browse files Browse the repository at this point in the history
RELNOTES=Making MetadataKey's emit() method properly typed for its value.
PiperOrigin-RevId: 336382116
  • Loading branch information
hagbard authored and Flogger Team committed Oct 9, 2020
1 parent 262618b commit 95ded18
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 6 deletions.
27 changes: 26 additions & 1 deletion api/src/main/java/com/google/common/flogger/MetadataKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

import static com.google.common.flogger.util.Checks.checkMetadataIdentifier;
import static com.google.common.flogger.util.Checks.checkNotNull;
import static com.google.common.flogger.util.Checks.checkState;

import java.util.Iterator;

/**
* Key for logging semi-structured metadata values.
Expand Down Expand Up @@ -113,15 +116,37 @@ public final boolean canRepeat() {
* emits the given value with this key's label, but it can be overridden to emit multiple
* key/value pairs if necessary. Note that if multiple key/value pairs are emitted, the following
* best-practice should be followed:
*
* <ul>
* <li>Key names should be of the form {@code "<label>.<suffix>"}.
* <li>Suffixes may only contain lower case ASCII letters and underscore (i.e. [a-z_]).
* </ul>
*/
public void emit(Object value, KeyValueHandler out) {
public void emit(T value, KeyValueHandler out) {
out.handle(getLabel(), value);
}

/**
* Emits one or more key/value pairs for a sequence of repeated metadata values. By default this
* method simply calls {@link #emit} once for each value, in order. However it could be overridden
* to treat the sequence of values for a repeated key as a single entity (e.g. by joining elements
* with a separator such as {@code '/'}). Note that if multiple key/value pairs are emitted, the
* following best-practice should be followed:
*
* <ul>
* <li>Key names should be of the form {@code "<label>.<suffix>"}.
* <li>Suffixes may only contain lower case ASCII letters and underscore (i.e. [a-z_]).
* </ul>
*
* <p>This method should only be called for repeated keys.
*/
public void emitRepeated(Iterator<T> values, KeyValueHandler out) {
checkState(canRepeat, "non repeating key");
while (values.hasNext()) {
emit(values.next(), out);
}
}

/**
* Returns a 64-bit bloom filter mask for this metadata key, usable by backend implementations to
* efficiently determine uniqueness of keys (e.g. for deduplication and grouping). This value is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,14 +252,20 @@ private static void appendContext(
tags = LogContext.Key.TAGS.cast(metadata.getValue(n));
continue;
}
key.emit(metadata.getValue(n), kvf);
castAndEmit(key, metadata.getValue(n), kvf);
}
if (tags != null) {
emitAllTags(tags, kvf);
}
kvf.done();
}

// Needed to re-capture the key type locally so the cast value is known to have the same type
// when passed to the emit method (in the loop it's just a MetadataKey<?>).
private static <T> void castAndEmit(MetadataKey<T> key, Object value, KeyValueFormatter kvf) {
key.emit(key.cast(value), kvf);
}

/** Emits all the key/value pairs of this Tags instance to the given consumer. */
private static void emitAllTags(Tags tags, KeyValueFormatter out) {
for (Map.Entry<String, SortedSet<Object>> e : tags.asMap().entrySet()) {
Expand Down
26 changes: 24 additions & 2 deletions api/src/test/java/com/google/common/flogger/MetadataKeyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.junit.Assert.fail;

import com.google.common.flogger.MetadataKey.KeyValueHandler;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
Expand Down Expand Up @@ -58,8 +59,29 @@ public void testCasting() {
public void testDefaultEmit() {
MetadataKey<String> k = MetadataKey.single("foo", String.class);
KeyValueHandler handler = Mockito.mock(KeyValueHandler.class);
k.emit(123, handler);
Mockito.verify(handler).handle("foo", 123);
k.emit("123", handler);
Mockito.verify(handler).handle("foo", "123");
}

@Test
public void testDefaultEmitRepeated() {
MetadataKey<String> k = MetadataKey.repeated("foo", String.class);
KeyValueHandler handler = Mockito.mock(KeyValueHandler.class);
List<String> values = asList("123", "abc");
k.emitRepeated(values.iterator(), handler);
Mockito.verify(handler).handle("foo", "123");
Mockito.verify(handler).handle("foo", "abc");
}

@Test
public void testDefaultEmitRepeated_singleKeyFails() {
MetadataKey<String> k = MetadataKey.single("foo", String.class);
KeyValueHandler handler = Mockito.mock(KeyValueHandler.class);
List<String> values = asList("123", "abc");
try {
k.emitRepeated(values.iterator(), handler);
fail("expected IllegalStateException");
} catch (IllegalStateException expected) { }
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ private static final class Point {
private static final MetadataKey<Point> POINT =
new MetadataKey<Point>("point", Point.class, false) {
@Override
public void emit(Object value, KeyValueHandler out) {
Point p = cast(value);
public void emit(Point p, KeyValueHandler out) {
out.handle("point.x", p.x);
out.handle("point.y", p.y);
}
Expand Down

0 comments on commit 95ded18

Please sign in to comment.